xineliboutput-2.0.0/0000755000175000017500000000000013061253423012223 5ustar phphxineliboutput-2.0.0/xineliboutput.c0000644000175000017500000003252613061253352015313 0ustar phph/* * vdr-xineliboutput: xine-lib based output device plugin for VDR * * Copyright (C) 2003-2017 Petri Hintukainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Or, point your browser to http://www.gnu.org/copyleft/gpl.html * * * xineliboutput.c: VDR Plugin interface * * $Id$ * */ #include "features.h" #include #include #include "logdefs.h" #include "config.h" #include "device.h" #include "setup_menu.h" #include "menu.h" #include "media_player.h" #if defined(APIVERSNUM) && (APIVERSNUM < 10733) # error VDR API versions < 1.7.33 are not supported ! #endif //---------------------------------plugin------------------------------------- static const char *VERSION = "2.0.0"; static const char *DESCRIPTION = trNOOP("X11/xine-lib output plugin"); static const char *MAINMENUENTRY = trNOOP("Media Player"); class cPluginXinelibOutput : public cPlugin { private: // Add any member variables or functions you may need here. cXinelibDevice *m_Dev; int m_MakePrimary; public: cPluginXinelibOutput(void); virtual ~cPluginXinelibOutput(); virtual const char *Version(void) { return VERSION; } virtual const char *Description(void) { return tr(DESCRIPTION); } virtual const char *CommandLineHelp(void); virtual bool ProcessArgs(int argc, char *argv[]); virtual bool Initialize(void); virtual bool Start(void); virtual void Stop(void); //virtual void Housekeeping(void); virtual void MainThreadHook(); //virtual cString Active(void); //virtual time_t WakeupTime(void); virtual const char *MainMenuEntry(void) { return xc.hide_main_menu ? NULL : tr(MAINMENUENTRY); } virtual cOsdObject *MainMenuAction(void); virtual cMenuSetupPage *SetupMenu(void); virtual bool SetupParse(const char *Name, const char *Value); virtual bool Service(const char *Id, void *Data = NULL); virtual const char **SVDRPHelpPages(void); virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode); }; cPluginXinelibOutput::cPluginXinelibOutput(void) { // Initialize any member variables here. // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! m_Dev = NULL; m_MakePrimary = 0; } cPluginXinelibOutput::~cPluginXinelibOutput() { // Clean up after yourself! if (m_Dev) { cXinelibDevice::Dispose(); } } const char cmdLineHelp[] = " -l NAME --local=NAME Use local frontend NAME\n" " Supported frontends:\n" " sxfe (X11)\n" " fbfe (framebuffer)\n" " none (only remote frontends)\n" " -r PORT --remote=PORT Listen PORT for remote clients\n" " (default " LISTEN_PORT_S ")\n" " none or 0 disables remote mode\n" " Also local interface address can be specified:\n" " --remote=: (default is all interfaces)\n" " -A NAME --audio=NAME Use audio driver NAME for local frontend\n" " Supported values:\n" " auto, alsa, oss, esound, none\n" " -V NAME --video=NAME Use video driver NAME for local frontend\n" " Supported values:\n" " for sxfe: auto, x11, xshm, xv, xvmc, xxmc,\n" #ifdef HAVE_VDPAU " vdpau, " #endif " vaapi, vidix, sdl, opengl, none\n" " for fbfe: auto, fb, DirectFB, vidixfb,\n" " sdl, dxr3, aadxr3, none\n" #if 0 " -m M --modeline=M Use modeline M for local frontend\n" " (example: )\n" #endif " -f --fullscreen Fullscreen mode (X11)\n" #if defined(HAVE_XRENDER) || defined(HAVE_OPENGL) " -D --hud[=flag[,flag]]\n" " Head Up Display OSD (X11)\n" " flags:\n" #endif #if defined(HAVE_XRENDER) && defined(HAVE_XSHAPE) " xshape Use XShape instead of compositing\n" #endif #ifdef HAVE_OPENGL " opengl Use OpenGL instead of compositing\n" " -O --opengl Use OpenGL for video and Head Up Display OSD\n" #endif " -w --width=x Window width\n" " -h --height=x Window width\n" " -g, --geometry=WxH[+X+Y] Set output window geometry (X style)\n" " -d DISP --display=DISP Use X11 display DISP\n" " (or framebuffer device name)\n" " -W ID --wid=ID Use existing X11 window\n" " Special ID for root window: --wid=root\n" #ifdef HAVE_XRANDR " -m --modeswitch Enable video mode switching\n" #endif " -P NAME --post=NAME Use xine post plugin NAME\n" " format: pluginname[:arg=val[,arg=val]][,...]\n" " example: \n" " --post=upmix;tvtime:enabled=1,cheap_mode=1\n" " -p --primary Force xineliboutput to be primary device when\n" " there are active frontend(s)\n" " -c --exit-on-close Exit vdr when local frontend window is closed\n" " -C --config=file Use xine-lib config file\n" " -t --truecolor Support True Color OSD if no client is connected\n" " -s --auto-suspend Trigger vdr-suspendoutput when there are no clients\n" ; const char *cPluginXinelibOutput::CommandLineHelp(void) { // Return a string that describes all known command line options. return cmdLineHelp; } bool cPluginXinelibOutput::ProcessArgs(int argc, char *argv[]) { // Implement command line argument processing here if applicable. return xc.ProcessArgs(argc, argv); } bool cPluginXinelibOutput::Initialize(void) { // Initialize any background activities the plugin shall perform. TRACEF("cPluginXinelibOutput::Initialize"); m_Dev = &(cXinelibDevice::Instance()); return m_Dev ? m_Dev->InitDevice() : false; } bool cPluginXinelibOutput::Start(void) { // Start any background activities the plugin shall perform. TRACEF("cPluginXinelibOutput::Start"); return m_Dev ? m_Dev->StartDevice() : false; } void cPluginXinelibOutput::MainThreadHook(void) { TRACEF("cPluginXinelibOutput::MainThreadHook"); if (m_MakePrimary) { LOGDBG("Switching primary device to %d", m_MakePrimary); cDevice::SetPrimaryDevice(m_MakePrimary); m_MakePrimary = 0; } if (m_Dev) { m_Dev->MainThreadHook(); } } void cPluginXinelibOutput::Stop(void) { // Start any background activities the plugin shall perform. TRACEF("cPluginXinelibOutput::Stop"); if (m_Dev) { m_Dev->StopDevice(); } } cOsdObject *cPluginXinelibOutput::MainMenuAction(void) { // Perform the action when selected from the main VDR menu. TRACEF("cPluginXinelibOutput::MainMenuAction"); if (!m_Dev) { return NULL; } if (xc.main_menu_mode == CloseOsd) { xc.main_menu_mode = ShowMenu; return NULL; } if (xc.pending_menu_action) { cOsdObject *tmp = xc.pending_menu_action; xc.pending_menu_action = NULL; return tmp; } if (xc.hide_main_menu) return NULL; return m_Dev ? new cMenuXinelib(m_Dev) : NULL; } cMenuSetupPage *cPluginXinelibOutput::SetupMenu(void) { // Return a setup menu in case the plugin supports one. TRACEF("cPluginXinelibOutput::SetupMenu"); return m_Dev ? new cMenuSetupXinelib(m_Dev) : NULL; } bool cPluginXinelibOutput::SetupParse(const char *Name, const char *Value) { // Parse your own setup parameters and store their values. return xc.SetupParse(Name, Value); } bool cPluginXinelibOutput::Service(const char *Id, void *Data) { if(Id) { char *CData = (char*)Data; if(!strcmp(Id, "MediaPlayer-1.0")) { if(CData && *CData) { LOGMSG("Service(%s, %s)", Id, CData); cPlayerFactory::Launch(m_Dev, pmAudioVideo, CData); return true; } LOGMSG("Service(%s) -> true", Id); return true; } else if(!strcmp(Id, "MusicPlayer-1.0")) { if(CData && *CData) { LOGMSG("Service(%s, %s)", Id, CData); cPlayerFactory::Launch(m_Dev, pmAudioOnly, CData); return true; } LOGMSG("Service(%s) -> true", Id); return true; } else if(!strcmp(Id, "DvdPlayer-1.0")) { if(Data && *CData) { LOGMSG("Service(%s, %s)", Id, CData); cPlayerFactory::Launch(m_Dev, pmNone, CData); return true; } LOGMSG("Service(%s) -> true", Id); return true; } else if(!strcmp(Id, "ImagePlayer-1.0")) { if(CData && *CData) { LOGMSG("Service(%s, %s)", Id, CData); cPlayerFactory::Launch(m_Dev, pmVideoOnly, CData); return true; } LOGMSG("Service(%s) -> true", Id); return true; } else if(!strcmp(Id, "StartFrontend-1.0")) { if(CData && *CData) { LOGMSG("Service(%s, %s)", Id, CData); int local_frontend = strstra(CData, xc.s_frontends, -1); if (local_frontend >= 0 && local_frontend < FRONTEND_count && strcmp(CData, xc.local_frontend)) { strn0cpy(xc.local_frontend, xc.s_frontends[local_frontend], sizeof(xc.local_frontend)); m_Dev->ConfigureWindow( xc.fullscreen, xc.width, xc.height, xc.modeswitch, xc.modeline, xc.display_aspect, xc.scale_video); } return true; } LOGMSG("Service(%s) -> true", Id); return true; } } return false; } const char **cPluginXinelibOutput::SVDRPHelpPages(void) { static const char *HelpPages[] = { "PMDA \n" " Play media file.", "PDVD \n" " Play DVD disc.", "PMSC \n" " Play music file.", "PIMG \n" " Play/show image file.", "QMSC \n" " Queue music file to playlist.", "LFRO \n" " Start/stop local frontend. can be none, sxfe or fbfe.", "PRIM \n" " Make the primary device. If is missing,\n" " xineliboutput will become the primary device.", NULL }; return HelpPages; } cString cPluginXinelibOutput::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) { if(strcasecmp(Command, "PMDA") == 0) { if(*Option) { LOGMSG("SVDRP(%s, %s)", Command, Option); cPlayerFactory::Launch(m_Dev, pmAudioVideo, Option); return cString("Playing video file"); } else { ReplyCode = 550; // Requested action not taken return cString("File name missing"); } } else if(strcasecmp(Command, "PDVD") == 0) { if(*Option) { LOGMSG("SVDRP(%s, %s)", Command, Option); cPlayerFactory::Launch(m_Dev, pmNone, Option); } else { cPlayerFactory::Launch(m_Dev, pmNone, "dvd:/"); } return cString("Playing DVD disc"); } else if(strcasecmp(Command, "PMSC") == 0) { if(*Option) { LOGMSG("SVDRP(%s, %s)", Command, Option); cPlayerFactory::Launch(m_Dev, pmAudioOnly, Option); return cString("Playing music file"); } else { ReplyCode = 550; // Requested action not taken return cString("Music file name missing"); } } else if(strcasecmp(Command, "PIMG") == 0) { if(*Option) { LOGMSG("SVDRP(%s, %s)", Command, Option); cPlayerFactory::Launch(m_Dev, pmVideoOnly, Option); return cString("Showing image file"); } else { ReplyCode = 550; // Requested action not taken return cString("Image file name missing"); } } else if(strcasecmp(Command, "QMSC") == 0) { if(*Option) { LOGMSG("SVDRP(%s, %s)", Command, Option); cPlayerFactory::Queue(m_Dev, Option); return cString("Queueing music file"); } else { ReplyCode = 550; // Requested action not taken return cString("Music file name missing"); } } else if(strcasecmp(Command, "LFRO") == 0) { if(*Option) { LOGMSG("SVDRP(%s, %s)", Command, Option); Service("StartFrontend-1.0", (void*)Option); return cString::sprintf("Local frontend: %s", xc.local_frontend); } else { ReplyCode = 550; // Requested action not taken return cString("Local frontend name missing"); } } else if(strcasecmp(Command, "PRIM") == 0) { int primary = 0; if(*Option) { LOGMSG("SVDRP(%s, %s)", Command, Option); primary = strtol(Option, NULL, 0); } else { LOGMSG("SVDRP(%s)", Command); } if(!primary && m_Dev) primary = m_Dev->DeviceNumber() + 1; m_MakePrimary = primary; return cString::sprintf("Switching primary device to %d", primary); } return NULL; } extern "C" void *VDRPluginCreator(void) __attribute__((visibility("default"))); VDRPLUGINCREATOR(cPluginXinelibOutput); // Don't touch this! xineliboutput-2.0.0/xine_sxfe_frontend.c0000644000175000017500000030110213061253352016254 0ustar phph/* * xine_sxfe_frontend.c: Simple front-end, X11 functions * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ /*#define HAVE_XF86VIDMODE*/ #include "features.h" #include #include #include #include #include /* DBL_MIN */ /* X11 */ #include #include #include #include #ifdef HAVE_XSHM # include #endif #ifdef HAVE_XRENDER # include #endif #ifdef HAVE_XSHAPE # include #endif #ifdef HAVE_XDPMS # include #endif #ifdef HAVE_XINERAMA # include #endif #ifdef HAVE_XRANDR # include #endif #ifdef HAVE_OPENGL # include # ifdef HAVE_DLFCN # include # endif # ifdef HAVE_PTHREAD # include # endif #endif #include #define LOG_MODULENAME "[vdr-sxfe] " #include "logdefs.h" #include "xine_frontend_internal.h" #ifdef HAVE_DBUS_GLIB_1 # include "tools/gnome_screensaver.h" #endif #include "tools/osd_command.h" #include "tools/rle.h" #include "tools/time_ms.h" #ifndef WIN_LAYER_NORMAL #define WIN_LAYER_NORMAL 4 #endif #ifndef WIN_LAYER_ONTOP #define WIN_LAYER_ONTOP 6 #endif #define MWM_HINTS_DECORATIONS (1L << 1) #define PROP_MWM_HINTS_ELEMENTS 5 typedef struct _mwmhints { unsigned long flags; unsigned long functions; unsigned long decorations; long input_mode; unsigned long status; } MWMHints; #ifdef HAVE_XRENDER /* HUD Scaling */ typedef struct _xrender_surf { Visual *vis; Drawable draw; Picture pic; uint16_t w, h; uint8_t depth; uint8_t allocated : 1; } Xrender_Surf; #endif /* HAVE_XRENDER */ /* * data */ typedef struct sxfe_s { /* function pointers / base class */ union { frontend_t fe; /* generic frontend */ fe_t x; /* xine frontend */ }; /* stored original handlers */ int (*fe_xine_open)(frontend_t *this_gen, const char *mrl); int (*fe_xine_play)(frontend_t *this_gen); /* X11 */ Display *display; Window root_window; Window window[2]; int screen; int window_id; /* output to another window */ #ifdef HAVE_XSHM int xshm_completion_event; #endif Time prev_click_time; /* time of previous mouse button click (grab double clicks) */ int mousecursor_timeout; #ifdef HAVE_MCE_DBUS_NAMES int mce_blank_prevent_timer; #endif #ifdef HAVE_XDPMS BOOL dpms_state; #endif /* Atoms */ Atom xa_SXFE_INTERRUPT; Atom xa_WM_DELETE_WINDOW; Atom xa_MOTIF_WM_HINTS; Atom xa_WIN_LAYER; Atom xa_WIN_STATE; Atom xa_NET_ACTIVE_WINDOW; Atom xa_NET_WM_STATE; Atom xa_NET_WM_STATE_ADD; Atom xa_NET_WM_STATE_DEL; Atom xa_NET_WM_STATE_ABOVE; Atom xa_NET_WM_STATE_STICKY; Atom xa_NET_WM_STATE_FULLSCREEN; Atom xa_NET_WM_STATE_STAYS_ON_TOP; int xinerama_screen; /* current xinerama screen, -1 = auto */ uint16_t xinerama_x; /* left-top position of current xinerama screen */ uint16_t xinerama_y; uint16_t origwidth; /* saved window-mode window size */ uint16_t origheight; uint16_t origxpos; /* saved window-mode window position */ uint16_t origypos; uint16_t dragging_x; uint16_t dragging_y; uint8_t fullscreen : 1; /*uint8_t vmode_switch : 1;*/ uint8_t fullscreen_state_forced : 1; uint8_t stay_above : 1; uint8_t no_border : 1; uint8_t check_move : 1; uint8_t dragging : 1; uint8_t gui_hotkeys : 1; uint8_t no_x_kbd : 1; uint8_t touchscreen : 1; /* OSD Video Window */ pthread_mutex_t video_win_mutex; uint8_t video_win_active; /* activate video window? */ uint8_t video_win_changed; uint16_t video_win_x; /* video window position, x */ uint16_t video_win_y; /* video window position, y */ uint16_t video_win_w; /* video window width */ uint16_t video_win_h; /* video window height */ /* OSD */ #if defined(HAVE_XRENDER) || defined(HAVE_OPENGL) uint8_t osd_visible; uint16_t osd_width; uint16_t osd_height; #endif /* OpenGL */ #ifdef HAVE_OPENGL GLXDrawable opengl_window; GLXContext opengl_context; int screen_width, screen_height; pthread_t opengl_drawing_thread; pthread_mutex_t opengl_redraw_mutex; pthread_cond_t opengl_redraw_cv; uint32_t opengl_redraw_request_nr; uint32_t opengl_redraw_served_nr; pthread_mutex_t opengl_redraw_finished_mutex; pthread_cond_t opengl_redraw_finished_cv; pthread_mutex_t opengl_osd_texture_img_mutex; Pixmap video_frame_pixmap; GC video_frame_gc; GLuint video_frame_texture; GLuint osd_texture; uint32_t *opengl_osd_texture_img; uint8_t opengl_hud : 1; uint8_t opengl_always : 1; uint8_t opengl_osd_texture_img_updated : 1; uint8_t opengl_deinit : 1; #ifdef HAVE_XSHAPE uint8_t opengl_xshape_available : 1; #endif #endif /* HAVE_OPENGL */ /* HUD */ #ifdef HAVE_XRENDER uint8_t hud; XImage *hud_img; Visual *hud_vis; Xrender_Surf *surf_win; Xrender_Surf *surf_img; uint32_t *hud_img_mem; GC gc; Window hud_window; # ifdef HAVE_XSHAPE uint8_t xshape_hud : 1; Xrender_Surf *surf_back_img; uint32_t *shape_mask_mem; Pixmap shape_mask_pixmap; GC shape_mask_gc; Picture shape_mask_picture; # endif # ifdef HAVE_XSHM XShmSegmentInfo hud_shminfo; # endif #endif /* HAVE_XRENDER */ } sxfe_t; #ifdef HAVE_OPENGL #if 0 typedef int (*GLXGetVideoSyncProc) (unsigned int *count); typedef int (*GLXWaitVideoSyncProc) (int divisor, int remainder, unsigned int *count); #endif typedef void (*GLXBindTexImageProc) (Display *display, GLXDrawable drawable, int buffer, int *attribList); #if 0 GLXGetVideoSyncProc getVideoSync; GLXWaitVideoSyncProc waitVideoSync; #endif GLXBindTexImageProc bindTexImage; #endif #define DOUBLECLICK_TIME 500 // ms #define OSD_DEF_WIDTH 720 #define OSD_DEF_HEIGHT 576 #define HUD_MAX_WIDTH 1920 #define HUD_MAX_HEIGHT 1200 #define MOUSECURSOR_TIMEOUT 2000 // 2 seconds static void sxfe_dest_size_cb (void *data, int video_width, int video_height, double video_pixel_aspect, int *dest_width, int *dest_height, double *dest_pixel_aspect) { fe_t *this = (fe_t *)data; if (!this) return; *dest_width = this->width; *dest_height = this->height; *dest_pixel_aspect = this->dest_pixel_aspect(this, video_pixel_aspect, video_width, video_height); } static void init_atoms(sxfe_t *this) { if (this->xa_SXFE_INTERRUPT == None) { this->xa_SXFE_INTERRUPT = XInternAtom(this->display, "SXFE_INTERRUPT", False); this->xa_WM_DELETE_WINDOW = XInternAtom(this->display, "WM_DELETE_WINDOW", False); this->xa_MOTIF_WM_HINTS = XInternAtom(this->display, "_MOTIF_WM_HINTS", False); this->xa_WIN_LAYER = XInternAtom(this->display, "_WIN_LAYER", False); this->xa_WIN_STATE = XInternAtom(this->display, "_WIN_STATE", False); this->xa_NET_ACTIVE_WINDOW = XInternAtom(this->display, "_NET_ACTIVE_WINDOW", False); this->xa_NET_WM_STATE = XInternAtom(this->display, "_NET_WM_STATE", False); this->xa_NET_WM_STATE_ADD = XInternAtom(this->display, "_NET_WM_STATE_ADD", False); this->xa_NET_WM_STATE_DEL = XInternAtom(this->display, "_NET_WM_STATE_DEL", False); this->xa_NET_WM_STATE_ABOVE = XInternAtom(this->display, "_NET_WM_STATE_ABOVE", False); this->xa_NET_WM_STATE_STICKY= XInternAtom(this->display, "_NET_WM_STATE_STICKY", False); this->xa_NET_WM_STATE_FULLSCREEN = XInternAtom(this->display, "_NET_WM_STATE_FULLSCREEN", False); this->xa_NET_WM_STATE_STAYS_ON_TOP = XInternAtom(this->display, "_NET_WM_STATE_STAYS_ON_TOP", False); } } static void set_fs_size_hint(sxfe_t *this) { XSizeHints hint; hint.flags = USSize | USPosition | PPosition | PSize; hint.x = this->xinerama_x; hint.y = this->xinerama_y; hint.width = this->x.width; hint.height = this->x.height; XLockDisplay(this->display); XSetNormalHints(this->display, this->window[1], &hint); XUnlockDisplay(this->display); } /* set_border * * Set/remove window border * */ static void set_border(sxfe_t *this, Window window, int border) { MWMHints mwmhints = { .flags = MWM_HINTS_DECORATIONS, .decorations = border ? 1 : 0, }; if(this->window_id > 0) return; XLockDisplay(this->display); XChangeProperty(this->display, window, this->xa_MOTIF_WM_HINTS, this->xa_MOTIF_WM_HINTS, 32, PropModeReplace, (unsigned char *) &mwmhints, PROP_MWM_HINTS_ELEMENTS); XUnlockDisplay(this->display); } static void set_fullscreen_props(sxfe_t *this) { XEvent ev; if(this->window_id > 0) return; set_fs_size_hint(this); /* no border in fullscreen window */ set_border(this, this->window[1], 0); memset(&ev, 0, sizeof(ev)); ev.type = ClientMessage; ev.xclient.type = ClientMessage; ev.xclient.message_type = this->xa_NET_WM_STATE; ev.xclient.display = this->display; ev.xclient.window = this->window[1]; ev.xclient.format = 32; ev.xclient.data.l[0] = 1; /*ev.xclient.data.l[0] = this->atom_state_add;*/ /* _NET_WM_STATE_FULLSCREEN */ XLockDisplay(this->display); ev.xclient.data.l[1] = this->xa_NET_WM_STATE_FULLSCREEN; XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); XUnlockDisplay(this->display); /* _NET_WM_STATE_ABOVE */ XLockDisplay(this->display); ev.xclient.data.l[1] = this->xa_NET_WM_STATE_ABOVE; XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); XUnlockDisplay(this->display); /* _NET_WM_STATE_ON_TOP */ XLockDisplay(this->display); ev.xclient.data.l[1] = this->xa_NET_WM_STATE_STAYS_ON_TOP; XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); XUnlockDisplay(this->display); /* _NET_ACTIVE_WINDOW */ XLockDisplay(this->display); ev.xclient.message_type = this->xa_NET_ACTIVE_WINDOW; ev.xclient.data.l[0] = 0; ev.xclient.data.l[1] = 0; XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); XUnlockDisplay(this->display); } static void update_window_title(sxfe_t *this) { XLockDisplay(this->display); if (!this->fe.fe_message_cb) { /* handler is set only in local mode */ char *name = NULL; if (XFetchName(this->display, this->window[0], &name) && name) { char *newname = NULL; if (strstr(name, " (top)")) *strstr(name, " (top)") = 0; if (this->stay_above) if (asprintf(&newname, "%s (top)", name) < 0) newname = NULL; XStoreName(this->display, this->window[0], newname ?: name); XStoreName(this->display, this->window[1], newname ?: name); XFree(name); free(newname); } else { XStoreName(this->display, this->window[0], this->stay_above ? "VDR - (top)" : "VDR"); XStoreName(this->display, this->window[1], this->stay_above ? "VDR - (top)" : "VDR"); } } else { XStoreName(this->display, this->window[0], this->stay_above ? "Local VDR (top)" : "Local VDR"); XStoreName(this->display, this->window[1], this->stay_above ? "Local VDR (top)" : "Local VDR"); } XUnlockDisplay(this->display); } static void set_above(sxfe_t *this, int stay_above) { XEvent ev; long propvalue[1]; if(this->window_id > 0) return; if(this->stay_above != stay_above) { this->stay_above = stay_above; /* Update window title */ update_window_title(this); } memset(&ev, 0, sizeof(ev)); ev.type = ClientMessage; ev.xclient.type = ClientMessage; ev.xclient.message_type = this->xa_NET_WM_STATE; ev.xclient.display = this->display; ev.xclient.window = this->window[0]; ev.xclient.format = 32; ev.xclient.data.l[0] = stay_above ? 1:0; /*this->atom_state_add : this->atom_state_del;*/ /* _NET_WM_STATE_ABOVE */ XLockDisplay(this->display); ev.xclient.data.l[1] = this->xa_NET_WM_STATE_ABOVE; XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); XUnlockDisplay(this->display); /* _NET_WM_STATE_STAYS_ON_TOP */ XLockDisplay(this->display); ev.xclient.data.l[1] = this->xa_NET_WM_STATE_STAYS_ON_TOP; XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); XUnlockDisplay(this->display); /* _NET_WM_STATE_STICKY */ XLockDisplay(this->display); ev.xclient.data.l[1] = this->xa_NET_WM_STATE_STICKY; XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); XUnlockDisplay(this->display); /* _WIN_LAYER */ propvalue[0] = stay_above ? WIN_LAYER_ONTOP : WIN_LAYER_NORMAL; XLockDisplay(this->display); XChangeProperty(this->display, this->window[0], this->xa_WIN_LAYER, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)propvalue, 1); XUnlockDisplay(this->display); #if 0 /* sticky */ memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.message_type = this->xa_WIN_STATE; ev.xclient.display = this->display; ev.xclient.window = this->window[0]; ev.xclient.format = 32; ev.xclient.data.l[0] = (1<<0); ev.xclient.data.l[1] = (stay_above?(1<<0):0); XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); #endif #if 0 /* on top */ memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.message_type = this->xa_WIN_LAYER; ev.xclient.display = this->display; ev.xclient.window = this->window[0]; ev.xclient.format = 32; ev.xclient.data.l[0] = (stay_above?10:6); XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask|SubstructureRedirectMask, &ev); #endif #if 0 /* layer */ XClientMessageEvent xev; memset(&xev, 0, sizeof(xev)); xev.type = ClientMessage; xev.display = this->display; xev.window = this->window[0]; xev.message_type = this->xa_WIN_LAYER; xev.format = 32; xev.data.l[0] = 10; xev.data.l[1] = CurrentTime; XSendEvent(this->display, DefaultRootWindow(this->display), False, SubstructureNotifyMask, (XEvent *) & xev); XMapRaised(this->display, this->window[0]); #endif #if 0 xine_port_send_gui_data(this->video_port, XINE_GUI_SEND_DRAWABLE_CHANGED, (void*) this->window[this->fullscreen ? 1 : 0]); #endif } /* * set_cursor * * Disable mouse cursor in video window (set transparent cursor) */ static void set_cursor(Display *dpy, Window win, const int enable) { XLockDisplay(dpy); if(enable) XDefineCursor(dpy, win, None); else { /* no cursor */ const char bm_no_data[] = { 0,0,0,0, 0,0,0,0 }; Cursor no_ptr; XColor black, dummy; Pixmap bm_no = XCreateBitmapFromData(dpy, win, bm_no_data, 8, 8); XAllocNamedColor(dpy, DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)), "black", &black, &dummy); no_ptr = XCreatePixmapCursor(dpy, bm_no, bm_no, &black, &black, 0, 0); XDefineCursor(dpy, win, None); XDefineCursor(dpy, win, no_ptr); } XUnlockDisplay(dpy); } static void update_xinerama_info(sxfe_t *this) { #ifdef HAVE_XINERAMA int screen = this->xinerama_screen; this->xinerama_x = this->xinerama_y = 0; XLockDisplay(this->display); if(screen >= -1 && XineramaIsActive(this->display)) { XineramaScreenInfo *screens; int num_screens; screens = XineramaQueryScreens(this->display, &num_screens); if(screen >= num_screens) screen = num_screens - 1; if(screen == -1) { const int x = this->origxpos + this->origwidth / 2; const int y = this->origypos + this->origheight / 2; for(screen = num_screens - 1; screen > 0; screen--) { const int x0 = screens[screen].x_org; const int y0 = screens[screen].y_org; const int x1 = x0 + screens[screen].width; const int y1 = y0 + screens[screen].height; if(x0 <= x && x <= x1 && y0 <= y && y <= y1) break; } } if (screen < 0) screen = 0; this->xinerama_x = screens[screen].x_org; this->xinerama_y = screens[screen].y_org; this->x.width = screens[screen].width; this->x.height = screens[screen].height; XFree(screens); } XUnlockDisplay(this->display); #endif } /* * update_screen_size * * Update screen size (= size of fullscreen window) */ static void update_screen_size(sxfe_t *this) { XLockDisplay (this->display); this->x.width = DisplayWidth(this->display, this->screen); this->x.height = DisplayHeight(this->display, this->screen); XUnlockDisplay(this->display); update_xinerama_info(this); } /* * Generic OSD */ #if defined(HAVE_XRENDER) || defined(HAVE_OPENGL) static void osd_fill_lut8(uint32_t* dst, int dst_pitch, int argb, const struct osd_command_s *cmd) { uint8_t *data = cmd->raw_data; unsigned x, y; uint32_t lut[256]; if (argb) { rle_palette_to_argb(lut, cmd->palette, cmd->colors); } else { rle_palette_to_rgba(lut, cmd->palette, cmd->colors); } dst += cmd->y * dst_pitch + cmd->x; for (y = cmd->h; y; y--) { for (x = 0; x < cmd->w; x++) { dst[x] = lut[*data]; ++data; } dst += dst_pitch; } } static void osd_set_video_window(sxfe_t *this, const struct osd_command_s *cmd) { LOGDBG("unscaled video window: %d,%d %dx%d", cmd->x, cmd->y, cmd->w, cmd->h); // Compute the coordinates of the video window double scale_x = (double)this->x.width / (double)this->osd_width; double scale_y = (double)this->x.height / (double)this->osd_height; int x = cmd->x; int y = cmd->y; int w = cmd->w; int h = cmd->h; x = (int)ceil((double)(x>0 ? x-1 : 0) * scale_x); y = (int)ceil((double)(y>0 ? y-1 : 0) * scale_y); w = (int)floor((double)(w+2) * scale_x); h = (int)floor((double)(h+2) * scale_y); pthread_mutex_lock(&this->video_win_mutex); if (x != this->video_win_x || y != this->video_win_y || w != this->video_win_w || h != this->video_win_h) this->video_win_changed = 1; this->video_win_x = x; this->video_win_y = y; this->video_win_w = w; this->video_win_h = h; this->video_win_active = 1; pthread_mutex_unlock(&this->video_win_mutex); LOGDBG("scaled video window: %d,%d %dx%d", this->video_win_x, this->video_win_y, this->video_win_w, this->video_win_h); } static int osd_command(sxfe_t *this, struct osd_command_s *cmd) { switch(cmd->cmd) { case OSD_Size: /* Set size of VDR OSD area */ if (!(cmd->flags & OSDFLAG_TOP_LAYER)) break; LOGVERBOSE("OSD Size %dx%d", cmd->w, cmd->h); this->osd_width = (cmd->w > 0) ? cmd->w : OSD_DEF_WIDTH; this->osd_height = (cmd->h > 0) ? cmd->h : OSD_DEF_HEIGHT; break; case OSD_VideoWindow: LOGVERBOSE("OSD VideoWindow %dx%d@%d,%d", cmd->w, cmd->h, cmd->x, cmd->y); osd_set_video_window(this, cmd); break; default: break; } return 1; } #endif /* HAVE_XRENDER || HAVE_OPENGL */ /* * HUD OSD stuff */ #ifdef HAVE_XRENDER static Xrender_Surf * xrender_surf_new(Display *dpy, Drawable draw, Visual *vis, int w, int h, int alpha) { Xrender_Surf *rs; XRenderPictFormat *fmt; XRenderPictureAttributes att; rs = calloc(1, sizeof (Xrender_Surf)); fmt = XRenderFindStandardFormat(dpy, alpha ? PictStandardARGB32 : PictStandardRGB24); rs->w = w; rs->h = h; rs->depth = fmt->depth; rs->vis = vis; rs->draw = XCreatePixmap(dpy, draw, w, h, fmt->depth); att.dither = 1; att.component_alpha = 1; att.repeat = 0; rs->pic = XRenderCreatePicture(dpy, rs->draw, fmt, CPRepeat | CPDither | CPComponentAlpha, &att); rs->allocated = 1; return rs; } static void xrender_surf_blend(Display *dpy, Xrender_Surf *src, Xrender_Surf *dst, int x, int y, int w, int h, XDouble scale_x, XDouble scale_y, int smooth, int *new_x, int *new_y, int *new_w, int *new_h) { XTransform xf; if (scale_x <= 2.0 * DBL_MIN) scale_x = 1; if (scale_y <= 2.0 * DBL_MIN) scale_y = 1; xf.matrix[0][0] = XDoubleToFixed(1.0 / scale_x); xf.matrix[0][1] = 0; xf.matrix[0][2] = 0; xf.matrix[1][0] = 0; xf.matrix[1][1] = XDoubleToFixed(1.0 / scale_y); xf.matrix[1][2] = 0; xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = XDoubleToFixed(1.0); XRenderSetPictureFilter(dpy, src->pic, smooth ? "bilinear" : "nearest", NULL, 0); XRenderSetPictureTransform(dpy, src->pic, &xf); x = (int)ceil((double)(x>0?x-1:0) * scale_x); y = (int)ceil((double)(y>0?y-1:0) * scale_y); w = (int)floor((double)(w+2) * scale_x); h = (int)floor((double)(h+2) * scale_y); XRenderComposite(dpy, PictOpSrc, src->pic, None, dst->pic, x, y, 0, 0, x, y, w, h); *new_x = x; *new_y = y; *new_w = w; *new_h = h; } static Xrender_Surf * xrender_surf_adopt(Display *dpy, Drawable draw, Visual *vis, int w, int h) { Xrender_Surf *rs; XRenderPictFormat *fmt; XRenderPictureAttributes att; rs = calloc(1, sizeof(Xrender_Surf)); fmt = XRenderFindVisualFormat(dpy, vis); rs->w = w; rs->h = h; rs->depth = fmt->depth; rs->vis = vis; rs->draw = draw; att.dither = 1; att.component_alpha = 1; att.repeat = 0; rs->pic = XRenderCreatePicture(dpy, rs->draw, fmt, CPRepeat | CPDither | CPComponentAlpha, &att); rs->allocated = 0; return rs; } static void xrender_surf_free(Display *dpy, Xrender_Surf *rs) { if(rs->allocated) XFreePixmap(dpy, rs->draw); XRenderFreePicture(dpy, rs->pic); free(rs); } static Visual *find_argb_visual(Display *dpy, int scr) { XVisualInfo *xvi, template; int nvi, i; XRenderPictFormat *format; Visual *visual; template.screen = scr; template.depth = 32; template.class = TrueColor; xvi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &template, &nvi); if(!xvi) { LOGERR("find_argb_visual: XGetVisualInfo failed (no xvi)"); return 0; } visual = 0; for(i = 0; i < nvi; i++) { LOGDBG("find_argb_visual: iteration %d of %d", i, nvi); format = XRenderFindVisualFormat(dpy, xvi[i].visual); if((format->type == PictTypeDirect) && format->direct.alphaMask) { visual = xvi[i].visual; break; } } XFree(xvi); if(!visual) LOGERR("find_argb_visual: No visual found"); return visual; } static void hud_fill_argb(uint32_t* dst, int dst_pitch, const struct osd_command_s *cmd) { uint32_t *data = (uint32_t*)cmd->raw_data; unsigned y; dst += cmd->y * dst_pitch + cmd->x; for (y = cmd->h; y; y--) { memcpy(dst, data, cmd->w * sizeof(uint32_t)); data += cmd->w; dst += dst_pitch; } } static void update_mask(uint32_t* dst, int dst_pitch, uint32_t* mask, int mask_pitch, int *mask_changed, const struct osd_command_s *cmd) { unsigned x, y; *mask_changed = 0; dst += dst_pitch * cmd->y + cmd->x; mask += mask_pitch * cmd->y + cmd->x; for (y = 0; y < cmd->h; y++) { for (x = 0; x < cmd->w; x++) { if (mask[x] != dst[x]) { *mask_changed = 1; mask[x] = dst[x]; } } dst += dst_pitch; mask += mask_pitch; } } static void hud_fill_img_memory(uint32_t* dst, int dst_pitch, uint32_t* mask, int mask_pitch, int *mask_changed, const struct osd_command_s *cmd) { if (cmd->cmd == OSD_Set_LUT8) { osd_fill_lut8(dst, dst_pitch, 1, cmd); } else if (cmd->cmd == OSD_Set_ARGB) { hud_fill_argb(dst, dst_pitch, cmd); } else if (cmd->cmd == OSD_Set_RLE) { rle_uncompress_argb(dst + cmd->y * dst_pitch + cmd->x, cmd->w, cmd->h, dst_pitch, cmd->data, cmd->num_rle, cmd->palette, cmd->colors); } else { LOGMSG("hud_fill_img_memory(): unsupported format"); return; } if (mask && mask_changed) { update_mask(dst, dst_pitch, mask, mask_pitch, mask_changed, cmd); } } static void hud_osd_draw(sxfe_t *this, const struct osd_command_s *cmd) { int x = cmd->x + cmd->dirty_area.x1; int y = cmd->y + cmd->dirty_area.y1; int w = cmd->dirty_area.x2 - cmd->dirty_area.x1 + 1; int h = cmd->dirty_area.y2 - cmd->dirty_area.y1 + 1; XDouble scale_x = (XDouble)this->x.width / (XDouble)this->osd_width; XDouble scale_y = (XDouble)this->x.height / (XDouble)this->osd_height; int mask_changed; #ifdef HAVE_XSHAPE Xrender_Surf *dst_surf = this->surf_back_img ? this->surf_back_img : this->surf_win; Window dst_win = this->surf_back_img ? this->surf_back_img->draw : this->hud_window; uint32_t *shape_mask_mem = this->shape_mask_mem; #else Xrender_Surf *dst_surf = this->surf_win; Window dst_win = this->hud_window; uint32_t *shape_mask_mem = NULL; #endif #ifdef HAVE_XSHM if (this->xshm_completion_event != -1) { hud_fill_img_memory((uint32_t*)(this->hud_img->data), HUD_MAX_WIDTH, shape_mask_mem, HUD_MAX_WIDTH, &mask_changed, cmd); if (!cmd->scaling) { /* Place image directly onto hud window */ XShmPutImage(this->display, dst_win, this->gc, this->hud_img, x, y, x, y, w, h, False); } else { /* Place image onto Xrender surface which will be blended onto hud window */ XShmPutImage(this->display, this->surf_img->draw, this->gc, this->hud_img, x, y, x, y, w, h, False); xrender_surf_blend(this->display, this->surf_img, dst_surf, x, y, w, h, scale_x, scale_y, (cmd->scaling & 2), // Note: HUD_SCALING_BILINEAR=2 &x, &y, &w, &h); } } else #endif { hud_fill_img_memory(this->hud_img_mem, HUD_MAX_WIDTH, shape_mask_mem, HUD_MAX_WIDTH, &mask_changed, cmd); if (!cmd->scaling) { /* Place image directly onto hud window (always unscaled) */ XPutImage(this->display, dst_win, this->gc, this->hud_img, x, y, x, y, w, h); } else { /* Place image onto Xrender surface which will be blended onto hud window */ XPutImage(this->display, this->surf_img->draw, this->gc, this->hud_img, x, y, x, y, w, h); xrender_surf_blend(this->display, this->surf_img, dst_surf, x, y, w, h, scale_x, scale_y, (cmd->scaling & 2), // Note: HUD_SCALING_BILINEAR=2 &x, &y, &w, &h); } } #ifdef HAVE_XSHAPE if (this->xshape_hud) { if (mask_changed) { LOGDBG("hud_osd_draw: Updating shape of window"); XRenderComposite(this->display, PictOpSrc, this->surf_back_img->pic, None, this->shape_mask_picture, x, y, 0, 0, x, y, w, h); XShapeCombineMask(this->display, this->hud_window, ShapeBounding, 0, 0, this->shape_mask_pixmap, ShapeSet); } } /* Put the image onto the hud window */ if (this->surf_back_img) XRenderComposite(this->display, PictOpSrc, this->surf_back_img->pic, None, this->surf_win->pic, x, y, 0, 0, x, y, w, h); #endif XFlush(this->display); } static void hud_osd_hide(sxfe_t *this) { if (!this->osd_visible) return; this->osd_visible = 0; this->video_win_active = 0; #ifdef HAVE_XSHAPE if (this->xshape_hud) { XUnmapWindow(this->display, this->hud_window); XSetForeground(this->display, this->shape_mask_gc, 0); XFillRectangle(this->display, this->shape_mask_pixmap, this->shape_mask_gc, 0, 0, this->x.width, this->x.height); memset(this->shape_mask_mem, 0, sizeof(uint32_t) * HUD_MAX_WIDTH * HUD_MAX_HEIGHT); XShapeCombineMask(this->display, this->hud_window, ShapeBounding, 0, 0, this->shape_mask_pixmap, ShapeSet); } #endif XSetForeground(this->display, this->gc, 0x00000000); XFillRectangle(this->display, this->hud_window, this->gc, 0, 0, this->x.width, this->x.height); XFillRectangle(this->display, this->surf_img->draw, this->gc, 0, 0, this->osd_width+2, this->osd_height+2); XFlush(this->display); } static void hud_osd_show(sxfe_t *this) { if (this->osd_visible) return; this->osd_visible = 1; this->video_win_active = 0; XSetForeground(this->display, this->gc, 0x00000000); XFillRectangle(this->display, this->surf_img->draw, this->gc, 0, 0, this->osd_width+2, this->osd_height+2); #ifdef HAVE_XSHAPE if (this->surf_back_img) XFillRectangle(this->display, this->surf_back_img->draw, this->gc, 0, 0, this->x.width, this->x.height); if (this->xshape_hud) { XSetForeground(this->display, this->shape_mask_gc, 0); XFillRectangle(this->display, this->shape_mask_pixmap, this->shape_mask_gc, 0, 0, this->x.width, this->x.height); memset(this->shape_mask_mem, 0, sizeof(uint32_t) * HUD_MAX_WIDTH * HUD_MAX_HEIGHT); XShapeCombineMask(this->display, this->hud_window, ShapeBounding, 0, 0, this->shape_mask_pixmap, ShapeSet); XRaiseWindow(this->display, this->hud_window); XMapWindow(this->display, this->hud_window); } #endif XFlush(this->display); } static int hud_osd_command(frontend_t *this_gen, struct osd_command_s *cmd) { sxfe_t *this = (sxfe_t*)this_gen; if (this && cmd) if (this->hud) { osd_command(this, cmd); if (!(cmd->flags & OSDFLAG_TOP_LAYER)) return 1; if (this->osd_height > HUD_MAX_HEIGHT || this->osd_width > HUD_MAX_WIDTH) { // XXX TODO crop or scale ? // really dimensions should be runtime param, not constant... LOGMSG("HUD ERROR: Unsupported OSD size: %dx%d is larger than %dx%d", this->osd_width, this->osd_height, HUD_MAX_WIDTH, HUD_MAX_HEIGHT); return 1; } XLockDisplay(this->display); switch (cmd->cmd) { case OSD_Nop: /* Do nothing ; used to initialize delay_ms counter */ LOGVERBOSE("HUD OSD NOP"); break; case OSD_Size: /* Set size of VDR OSD area */ LOGVERBOSE("HUD OSD Size"); hud_osd_show(this); break; case OSD_Set_LUT8: case OSD_Set_ARGB: case OSD_Set_RLE: /* Create/update OSD window. Data is rle-compressed. */ LOGVERBOSE("HUD OSD Set"); hud_osd_draw(this, cmd); break; case OSD_SetPalette: /* Modify palette of already created OSD window */ LOGDBG("HUD OSD SetPalette"); break; case OSD_Move: /* Change x/y position of already created OSD window */ LOGDBG("HUD OSD Move"); break; case OSD_Set_YUV: /* Create/update OSD window. Data is in YUV420 format. */ LOGDBG("HUD OSD Set YUV"); break; case OSD_VideoWindow: break; case OSD_Close: /* Close OSD window */ LOGVERBOSE("HUD OSD Close"); hud_osd_hide(this); break; default: break; } XUnlockDisplay(this->display); } return 1; } static void hud_frame_output_cb (void *data, int video_width, int video_height, double video_pixel_aspect, int *dest_x, int *dest_y, int *dest_width, int *dest_height, double *dest_pixel_aspect, int *win_x, int *win_y) { sxfe_t *this = (sxfe_t*)data; /* Call the original handler */ this->x.frame_output_handler(data, video_width, video_height, video_pixel_aspect, dest_x, dest_y, dest_width, dest_height, dest_pixel_aspect, win_x, win_y); /* Set the desitination position if the video window is active */ if (this->video_win_active) { pthread_mutex_lock(&this->video_win_mutex); /* Clear the window if the size has changed */ if (this->video_win_changed) { Window win = this->window[!!this->fullscreen]; GC gc = XCreateGC(this->display, win, 0, NULL); XSetForeground(this->display, gc, 0x00000000); XFillRectangle(this->display, win, gc, 0, 0, this->x.width-1, this->x.height-1); XFreeGC(this->display, gc); } *dest_x = this->video_win_x; *dest_y = this->video_win_y; *dest_width = this->video_win_w; *dest_height = this->video_win_h; this->video_win_changed = 0; pthread_mutex_unlock(&this->video_win_mutex); } } static int hud_osd_open(sxfe_t *this) { if(this && this->hud) { int dummy; XLockDisplay(this->display); LOGDBG("opening HUD OSD window..."); if(!XRenderQueryExtension(this->display, &dummy, &dummy)) { LOGMSG("hud_osd_open: ERROR: XRender extension not available."); LOGMSG("XRender extension must be enabled in X configuration (xorg.conf etc.)"); this->hud = 0; XUnlockDisplay(this->display); return 1; } this->hud_vis = find_argb_visual(this->display, this->screen); if(!this->hud_vis) { LOGMSG("find_argb_visual() failed. HUD OSD disabled."); this->hud = 0; XUnlockDisplay(this->display); return 1; } Colormap hud_colormap = XCreateColormap(this->display, this->root_window, this->hud_vis, AllocNone); XSetWindowAttributes attributes; attributes.override_redirect = True; attributes.background_pixel = 0x00000000; attributes.border_pixel = 0; attributes.colormap = hud_colormap; attributes.backing_store = Always; this->hud_window = XCreateWindow(this->display, this->root_window, this->x.xpos, this->x.ypos, this->x.width, this->x.height, 0, 32, InputOutput, this->hud_vis, CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWColormap, &attributes); XSelectInput(this->display, this->hud_window, StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | FocusChangeMask); XStoreName(this->display, this->hud_window, "HUD"); this->gc = XCreateGC(this->display, this->hud_window, 0, NULL); #ifdef HAVE_XSHM if(this->xshm_completion_event != -1) { this->hud_img = XShmCreateImage(this->display, this->hud_vis, 32, ZPixmap, NULL, &(this->hud_shminfo), HUD_MAX_WIDTH, HUD_MAX_HEIGHT); this->hud_shminfo.shmid = shmget(IPC_PRIVATE, this->hud_img->bytes_per_line * this->hud_img->height, IPC_CREAT | 0777); this->hud_shminfo.shmaddr = this->hud_img->data = shmat(this->hud_shminfo.shmid, 0, 0); this->hud_shminfo.readOnly = True; XShmAttach(this->display, &(this->hud_shminfo)); } else #endif { /* Fall-back to traditional memory */ LOGMSG("hud_osd_open: XShm not available, falling back to normal (slow) memory"); this->hud_img_mem = malloc(4 * HUD_MAX_WIDTH * HUD_MAX_HEIGHT); this->hud_img = XCreateImage(this->display, this->hud_vis, 32, ZPixmap, 0, (char*)this->hud_img_mem, HUD_MAX_WIDTH, HUD_MAX_HEIGHT, 32, 0); } #ifdef HAVE_XSHAPE if (this->xshape_hud) { // Check if extension is available if (XShapeQueryExtension(this->display, &dummy, &dummy)) { // Create the pixmap this->shape_mask_pixmap = XCreatePixmap(this->display, this->hud_window, DisplayWidth(this->display, this->screen), DisplayHeight(this->display, this->screen), 1); this->shape_mask_mem = calloc(sizeof(uint32_t), HUD_MAX_WIDTH * HUD_MAX_HEIGHT); this->shape_mask_gc = XCreateGC(this->display, this->shape_mask_pixmap, 0, NULL); XSetForeground(this->display, this->shape_mask_gc, 0); XFillRectangle(this->display, this->shape_mask_pixmap, this->shape_mask_gc, 0, 0, DisplayWidth(this->display, this->screen), DisplayHeight(this->display, this->screen)); this->shape_mask_picture = XRenderCreatePicture(this->display, this->shape_mask_pixmap, XRenderFindStandardFormat(this->display, PictStandardA1), 0, NULL); XShapeCombineMask(this->display, this->hud_window, ShapeBounding, 0, 0, this->shape_mask_pixmap, ShapeSet); } else { LOGMSG("hud_osd_open(): XShape not available, composite window manager must be running to see OSD."); this->xshape_hud = 0; } } #endif this->osd_visible = 0; this->surf_win = xrender_surf_adopt(this->display, this->hud_window, this->hud_vis, HUD_MAX_WIDTH, HUD_MAX_HEIGHT); this->surf_img = xrender_surf_new(this->display, this->hud_window, this->hud_vis, HUD_MAX_WIDTH, HUD_MAX_HEIGHT, 1); #ifdef HAVE_XSHAPE if (this->xshape_hud) this->surf_back_img = xrender_surf_new(this->display, this->hud_window, this->hud_vis, DisplayWidth(this->display, this->screen), DisplayHeight(this->display, this->screen), 1); #endif XUnlockDisplay(this->display); this->fe.xine_osd_command = hud_osd_command; if (this->x.scale_video) { // Enable this if you want to have video window support (e.g., for yaepghd) // However, vdpau does not support this. Works ok for xv this->x.vis_x11.frame_output_cb = hud_frame_output_cb; } return 1; } return 1; } /* * hud_osd_resize * * - Move and resize HUD along with main or fullscreen window */ static void hud_osd_resize(sxfe_t *this, Window video_window, int width, int height) { if(this->hud) { if(video_window == this->window[0]) { int hud_x, hud_y; Window tmp_win; XLockDisplay(this->display); XTranslateCoordinates(this->display, this->window[0], this->root_window, 0, 0, &hud_x, &hud_y, &tmp_win); XResizeWindow(this->display, this->hud_window, width, height); XMoveWindow(this->display, this->hud_window, hud_x, hud_y); set_cursor(this->display, this->hud_window, 1); XUnlockDisplay(this->display); } else if(video_window == this->window[1]) { XLockDisplay(this->display); XResizeWindow(this->display, this->hud_window, width, height); XMoveWindow(this->display, this->hud_window, 0, 0); set_cursor(this->display, this->hud_window, 0); XUnlockDisplay(this->display); } } } /* * hud_osd_focus * * - show / hide HUD OSD window */ static void hud_osd_focus(sxfe_t *this, XFocusChangeEvent *fev) { if(this && this->hud) if(fev->window == this->window[0] || fev->window == this->window[1]) { XLockDisplay(this->display); if (fev->type == FocusIn) { /* Show HUD again if sxfe window receives focus */ XMapWindow(this->display, this->hud_window); } else if (fev->type == FocusOut) { /* Dismiss HUD window if focusing away from frontend window */ XUnmapWindow(this->display, this->hud_window); } XUnlockDisplay(this->display); } } static void hud_osd_close(sxfe_t *this) { if(this && this->hud) { XLockDisplay(this->display); LOGDBG("closing hud window..."); #ifdef HAVE_XSHM if(this->xshm_completion_event != -1) { XShmDetach(this->display, &(this->hud_shminfo)); XDestroyImage(this->hud_img); shmdt(this->hud_shminfo.shmaddr); shmctl(this->hud_shminfo.shmid, IPC_RMID, 0); } else #endif XDestroyImage(this->hud_img); #ifdef HAVE_XSHAPE if (this->shape_mask_picture) XRenderFreePicture(this->display, this->shape_mask_picture); if (this->shape_mask_pixmap) XFreePixmap(this->display, this->shape_mask_pixmap); free(this->shape_mask_mem); if (this->surf_back_img) xrender_surf_free(this->display, this->surf_back_img); #endif if(this->surf_img) xrender_surf_free(this->display, this->surf_img); if(this->surf_win) xrender_surf_free(this->display, this->surf_win); XDestroyWindow(this->display, this->hud_window); XUnlockDisplay(this->display); } } #endif /* HAVE_XRENDER */ /* * disable_DPMS */ static void disable_DPMS(sxfe_t *this) { #ifdef HAVE_XDPMS int dpms_dummy; XLockDisplay(this->display); if (DPMSQueryExtension(this->display, &dpms_dummy, &dpms_dummy) && DPMSCapable(this->display)) { CARD16 dpms_level; DPMSInfo(this->display, &dpms_level, &this->dpms_state); DPMSDisable(this->display); } else { LOGMSG("disable_DPMS: DPMS unavailable"); } XUnlockDisplay(this->display); #endif } /* * open_display * * Try to connect to X server, in order * 1) user-given display * 2) DISPLAY environment variable * 3) default display * 4) :0.0 * 5) 127.0.0.1:0.0 */ static int open_display(sxfe_t *this, const char *video_port) { if (video_port && *video_port) { if (!(this->display = XOpenDisplay(video_port))) LOGERR("sxfe_display_open: failed to connect to X server (%s)", video_port); } if (!this->display) { if (NULL!=(video_port=getenv("DISPLAY")) && !(this->display = XOpenDisplay(video_port))) LOGERR("sxfe_display_open: failed to connect to X server (%s)", video_port); } if (!this->display) { this->display = XOpenDisplay(NULL); } if (!this->display) { if (!(this->display = XOpenDisplay(":0.0"))) LOGERR("sxfe_display_open: failed to connect to X server (:0.0)"); } if (!this->display) { if (!(this->display = XOpenDisplay("127.0.0.1:0.0"))) LOGERR("sxfe_display_open: failed to connect to X server (127.0.0.1:0.0"); } if (!this->display) { LOGERR("sxfe_display_open: failed to connect to X server."); LOGMSG("If X server is running, try running \"xhost +\" in xterm window"); return 0; } return 1; } /* * set_icon */ static void set_icon(sxfe_t *this) { # include "vdrlogo_32x32.c" XLockDisplay(this->display); #if defined(__WORDSIZE) && (__WORDSIZE == 32) /* Icon */ XChangeProperty(this->display, this->window[0], XInternAtom(this->display, "_NET_WM_ICON", False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &vdrlogo_32x32, 2 + vdrlogo_32x32.width*vdrlogo_32x32.height); #else long q[2+32*32]; uint32_t *p = (uint32_t*)&vdrlogo_32x32; unsigned i; for (i = 0; i < 2 + vdrlogo_32x32.width*vdrlogo_32x32.height; i++) q[i] = p[i]; XChangeProperty(this->display, this->window[0], XInternAtom(this->display, "_NET_WM_ICON", False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *) q, 2 + vdrlogo_32x32.width*vdrlogo_32x32.height); #endif XUnlockDisplay(this->display); } /* * detect_display_ratio * * Calculate display aspect ratio */ static double detect_display_ratio(Display *dpy, int screen) { double res_h = DisplayWidth (dpy, screen) * 1000.0 / DisplayWidthMM (dpy, screen); double res_v = DisplayHeight (dpy, screen) * 1000.0 / DisplayHeightMM (dpy, screen); double display_ratio = res_v / res_h; double diff = display_ratio - 1.0; if ((diff < 0.01) && (diff > -0.01)) display_ratio = 1.0; LOGDBG("Display size : %d x %d mm", DisplayWidthMM (dpy, screen), DisplayHeightMM (dpy, screen)); LOGDBG(" %d x %d pixels", DisplayWidth (dpy, screen), DisplayHeight (dpy, screen)); LOGDBG(" %ddpi / %ddpi", (int)(res_v/1000*25.4), (int)(res_h/1000*25.4)); LOGDBG("Display ratio: %f/%f = %f", res_v, res_h, display_ratio); return display_ratio; } /* * create_windows * * Create and initialize fullscreen and windowed mode X11 windows * - Borderless fullscreen window * - Set window title and icon */ static void create_windows(sxfe_t *this) { XSetWindowAttributes xswa; unsigned long xswa_mask; xswa.background_pixel = 0x00000000; xswa.border_pixel = 0; xswa.backing_store = WhenMapped; //xswa_mask = CWBackPixel | CWBorderPixel | CWBackingStore; xswa_mask = CWBorderPixel | CWBackingStore; XLockDisplay(this->display); /* create and display our video window */ this->window[0] = XCreateWindow (this->display, this->root_window, this->x.xpos, this->x.ypos, this->x.width, this->x.height, 1, CopyFromParent, InputOutput, CopyFromParent, xswa_mask, &xswa); this->window[1] = XCreateWindow (this->display, this->root_window, this->xinerama_x, this->xinerama_y, this->x.width, this->x.height, 0, CopyFromParent, InputOutput, CopyFromParent, xswa_mask, &xswa); /* full-screen window */ set_fullscreen_props(this); /* Window hint */ XClassHint *classHint = XAllocClassHint(); if (classHint) { classHint->res_name = "VDR"; classHint->res_class = "VDR"; XSetClassHint(this->display, this->window[0], classHint); XSetClassHint(this->display, this->window[1], classHint); XFree(classHint); } /* Window name */ const char *initial_title = (!this->fe.fe_message_cb) ? "Connecting to VDR ..." : "Local VDR"; XStoreName(this->display, this->window[0], initial_title); XStoreName(this->display, this->window[1], initial_title); /* Icon */ set_icon(this); XUnlockDisplay(this->display); } #ifdef HAVE_OPENGL /* * OpenGL OSD */ static void opengl_fill_argb(uint32_t* dst, int dst_pitch, const struct osd_command_s *cmd) { int x0 = cmd->dirty_area.x1; int y0 = cmd->dirty_area.y1; int w = cmd->dirty_area.x2 - cmd->dirty_area.x1 + 1; int h = cmd->dirty_area.y2 - cmd->dirty_area.y1 + 1; int x, y; uint32_t *src = (uint32_t*)cmd->raw_data; dst += (cmd->y + y0) * dst_pitch + cmd->x + x0; src += y0 * cmd->w + x0; for (y = h; y; y--){ for (x = 0; x < w; x++) { uint32_t value = src[x]; dst[x] = (value<<8)|((value>>24)&0xFF); } src += cmd->w; dst += dst_pitch; } } static void opengl_osd_draw(sxfe_t *this, const struct osd_command_s *cmd) { // Copy the image to the texture and inform the opengl thread pthread_mutex_lock(&this->opengl_osd_texture_img_mutex); uint32_t *dst = this->opengl_osd_texture_img; switch (cmd->cmd) { case OSD_Set_LUT8: osd_fill_lut8(dst, this->osd_width, 0, cmd); break; case OSD_Set_ARGB: opengl_fill_argb(dst, this->osd_width, cmd); break; case OSD_Set_RLE: rle_uncompress_rgba(dst + cmd->y * this->osd_width + cmd->x, cmd->w, cmd->h, this->osd_width, cmd->data, cmd->num_rle, cmd->palette, cmd->colors); break; default: LOGMSG("opengl_fill_img_memory(): unsupported format"); break; } this->opengl_osd_texture_img_updated = 1; pthread_mutex_unlock(&this->opengl_osd_texture_img_mutex); } static void opengl_osd_hide(sxfe_t *this) { if (!this->osd_visible) return; this->osd_visible = 0; this->video_win_active = 0; } static void opengl_osd_show(sxfe_t *this) { if (this->osd_visible) return; pthread_mutex_lock(&this->opengl_osd_texture_img_mutex); this->osd_visible = 1; this->video_win_active = 0; free(this->opengl_osd_texture_img); size_t size = sizeof(uint32_t) * this->osd_width * this->osd_height; this->opengl_osd_texture_img = malloc(size); memset(this->opengl_osd_texture_img, 0, size); this->opengl_osd_texture_img_updated = 1; pthread_mutex_unlock(&this->opengl_osd_texture_img_mutex); XFlush(this->display); } static int opengl_osd_command(frontend_t *this_gen, struct osd_command_s *cmd) { sxfe_t *this = (sxfe_t*)this_gen; if (this && cmd) if (this->opengl_always || this->opengl_hud) { osd_command(this, cmd); if (!(cmd->flags & OSDFLAG_TOP_LAYER)) return 1; XLockDisplay(this->display); switch (cmd->cmd) { case OSD_Size: /* Set size of VDR OSD area */ LOGDBG("OpenGL OSD Size"); opengl_osd_show(this); break; case OSD_Set_LUT8: case OSD_Set_ARGB: case OSD_Set_RLE: /* Create/update OSD window. Data is rle-compressed. */ LOGDBG("OpenGL OSD Set"); opengl_osd_draw(this, cmd); break; case OSD_Close: /* Close OSD window */ LOGDBG("OpenGL OSD Close"); opengl_osd_hide(this); break; default: break; } XUnlockDisplay(this->display); } return 1; } /* * OpenGL */ /* * Signals a change to the opengl drawing thread */ void opengl_trigger_drawing_thread(sxfe_t *this) { pthread_mutex_lock(&this->opengl_redraw_mutex); this->opengl_redraw_request_nr++; pthread_cond_signal(&this->opengl_redraw_cv); pthread_mutex_unlock(&this->opengl_redraw_mutex); } /* * Wait until drawing is finished */ void opengl_wait_drawing_finished(sxfe_t *this) { pthread_mutex_lock(&this->opengl_redraw_finished_mutex); if (this->opengl_redraw_request_nr != this->opengl_redraw_served_nr) pthread_cond_wait(&this->opengl_redraw_finished_cv, &this->opengl_redraw_finished_mutex); pthread_mutex_unlock(&this->opengl_redraw_finished_mutex); } static void opengl_frame_output_cb (void *data, int video_width, int video_height, double video_pixel_aspect, int *dest_x, int *dest_y, int *dest_width, int *dest_height, double *dest_pixel_aspect, int *win_x, int *win_y) { sxfe_t *this = (sxfe_t*)data; /* Inform the opengl drawing thread */ opengl_trigger_drawing_thread(this); /* Wait until the thrad is finished */ opengl_wait_drawing_finished(this); /* Call the original handler */ this->x.frame_output_handler(data, video_width, video_height, video_pixel_aspect, dest_x, dest_y, dest_width, dest_height, dest_pixel_aspect, win_x, win_y); } #if 0 static void time_measure_start(struct timeval *time_start) { gettimeofday(time_start, NULL); } static int time_measure_end(struct timeval *time_start) { struct timeval time_end; int diff; gettimeofday(&time_end, NULL); diff = time_end.tv_usec - time_start->tv_usec; if (time_end.tv_usec < time_start->tv_usec) { diff += 1000000; } return diff; } #endif static int opengl_init_dl(sxfe_t *this) { void *dlhand; // Get handles to important functions dlhand = dlopen (NULL, RTLD_LAZY); if (!dlhand) { LOGERR("opengl_init(): dlopen failed (%s)", dlerror()); return -1; } void *(*glXGetProcAddressARB)(unsigned char *) = NULL; glXGetProcAddressARB = (void *(*)(unsigned char *))dlsym(dlhand, "glXGetProcAddressARB"); if (glXGetProcAddressARB) { #if 0 if (!(waitVideoSync = (void *)glXGetProcAddressARB((unsigned char *)"glXWaitVideoSyncSGI"))) { LOGMSG("glXGetProcAddressARB(glXWaitVideoSyncSGI) failed"); goto error; } if (!(getVideoSync = (void *)glXGetProcAddressARB((unsigned char *)"glXGetVideoSyncSGI"))) { LOGMSG("glXGetProcAddressARB(glXGetVideoSyncSGI) failed"); goto error; } #endif if (!(bindTexImage = (void *)glXGetProcAddressARB((unsigned char *)"glXBindTexImageEXT"))) { LOGMSG("glXGetProcAddressARB(glXBindTexImageEXT) failed"); goto error; } } else { LOGMSG("glXGetProcAddressARB not found"); dlerror(); bindTexImage = dlsym(dlhand,"glXBindTexImageEXT"); if (dlerror() != NULL) { LOGMSG("opengl_init(): can not get pointer to glXBindTexImageEXT"); goto error; } #if 0 getVideoSync = dlsym(dlhand,"glXGetVideoSyncSGI"); if (dlerror() != NULL) { LOGMSG("opengl_init(): can not get pointer to glXGetVideoSyncSGI"); goto error; } waitVideoSync = dlsym(dlhand,"glXWaitVideoSyncSGI"); if (dlerror() != NULL) { LOGMSG("opengl_init(): can not get pointer to glXWaitVideoSyncSGI"); goto error; } #endif } dlclose(dlhand); return 0; error: dlclose(dlhand); return -1; } static int opengl_init(sxfe_t *this) { int glx_major, glx_minor; int n; GLXFBConfig *fbconfigs; GLXFBConfig fbcroot; XSetWindowAttributes attr; XVisualInfo *visinfo; const char* gl_version; int gl_major, gl_minor; static const int fbc_attr[] = { GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT, GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, GLX_BIND_TO_TEXTURE_RGBA_EXT, True, GLX_DOUBLEBUFFER, True, GLX_DEPTH_SIZE, 0, GLX_ALPHA_SIZE, 0, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_RENDER_TYPE, GLX_RGBA_BIT, None }; int fbc_attr2[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER, 1, None }; static const int pixmapAttribs[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, None }; GLXPixmap glxpixmap; #if 0 int frame_period_average = 0; int frame_period; const int frame_period_required_samples = 100; struct timeval t; int i; int unsigned sync; #endif const char *glxExtensions; // Get handles to important functions if (opengl_init_dl(this) < 0) { return -1; } // Get the GLX version LOGVERBOSE("Get GLX version ..."); if (!glXQueryVersion (this->display, &glx_major, &glx_minor)) { LOGMSG("no GLX support"); return -1; } LOGVERBOSE("GLX %d.%d", glx_major, glx_minor); // Should have full GLX 1.3 for GL 1.2, if (!(glx_major > 1 || glx_minor > 2)) { const char* exts = glXQueryExtensionsString (this->display, this->screen); if (!exts || !strstr (exts, "GLX_SGIX_fbconfig")) { LOGMSG("no glx fbconfig support"); return -1; } } LOGVERBOSE("Found GLX fbconfig support"); if (!(glx_major > 1 || glx_minor > 2)) { LOGMSG("no glx 1.3 support"); return -1; } // Check for important extension glxExtensions = glXQueryExtensionsString (this->display, this->screen); if (!strstr (glxExtensions, "GLX_EXT_texture_from_pixmap")) { LOGMSG("No texture from pixmap extension"); return -1; } LOGVERBOSE("Found texture from pixmap extension"); #if 0 if (!strstr (glxExtensions, "GLX_SGI_video_sync")) { LOGMSG("No sgi video sync extension"); return -1; } LOGVERBOSE("Found sgi video sync extension"); #endif // Get handles to important functions if (opengl_init_dl(this) < 0) { return -1; } // Get properties of the root window this->screen_width = DisplayWidth (this->display, this->screen); this->screen_height = DisplayHeight (this->display, this->screen); // Create the opengl window if (!(fbconfigs = glXChooseFBConfig(this->display, this->screen, fbc_attr, &n))) { LOGMSG("No glx frame buffer"); return -1; } fbcroot = fbconfigs[0]; XFree (fbconfigs); visinfo = glXChooseVisual(this->display, this->screen, fbc_attr2); attr.colormap = XCreateColormap (this->display, this->root_window, visinfo->visual, AllocNone); attr.override_redirect = True; this->opengl_window = XCreateWindow (this->display, this->root_window, 0, 0, this->screen_width, this->screen_height, 0, visinfo->depth, InputOutput, visinfo->visual, CWColormap | CWOverrideRedirect, &attr); XSelectInput(this->display, this->opengl_window, StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | FocusChangeMask); #ifdef HAVE_XSHAPE if (this->xshape_hud) XShapeCombineRectangles(this->display, this->opengl_window, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted); #endif this->opengl_context = glXCreateContext(this->display, visinfo, None, GL_TRUE); XFree (visinfo); if (this->opengl_context == NULL) { LOGERR("Can't create glx context"); return -1; } glXMakeCurrent(this->display, this->opengl_window, this->opengl_context); if (!(gl_version = (const char*) glGetString (GL_VERSION))) { LOGERR("Failed to initialize GL"); return -1; } sscanf (gl_version, "%d.%d", &gl_major, &gl_minor); if (!(gl_major > 1 || gl_minor > 1)) { LOGMSG("No GL 1.2 support on your platform"); return -1; } if (!(gl_major > 1 || gl_minor > 2)) { LOGMSG("No GL 1.3 support on your platform"); return -1; } glMatrixMode (GL_PROJECTION); glLoadIdentity (); glOrtho (0, this->screen_width-1, 0, this->screen_height-1, 0, 65535); glEnable(GL_TEXTURE_2D); glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glClearColor(0.0, 0.0, 0.0, 1.0); // Hide cursor set_cursor(this->display, this->opengl_window, 0); // Associate video and hud with a texture this->video_frame_pixmap = XCreatePixmap(this->display, this->opengl_window, this->screen_width, this->screen_height, DefaultDepth(this->display, this->screen)); this->video_frame_gc = XCreateGC(this->display, this->video_frame_pixmap, 0, NULL); glxpixmap = glXCreatePixmap (this->display, fbcroot, this->video_frame_pixmap, pixmapAttribs); glGenTextures (1, &this->video_frame_texture); glBindTexture (GL_TEXTURE_2D, this->video_frame_texture); bindTexImage (this->display, glxpixmap, GLX_FRONT_LEFT_EXT, NULL); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenTextures (1, &this->osd_texture); glBindTexture (GL_TEXTURE_2D, this->osd_texture); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); #if 0 // Detect the frame rate for (i = 0; i < frame_period_required_samples + 2; i++) { if (i > 1) { frame_period = time_measure_end(&t); frame_period_average += frame_period; } time_measure_start(&t); (*getVideoSync) (&sync); (*waitVideoSync) (2, (sync + 1) % 2, &sync); } frame_period_average /= frame_period_required_samples; this->opengl_frame_period = frame_period_average; LOGMSG("opengl_init(): average frame period is %d us", this->opengl_frame_period); #endif return 0; } // Frees all opengl related resources static void opengl_deinit(sxfe_t *this) { XFreePixmap(this->display, this->video_frame_pixmap); glXDestroyContext(this->display, this->opengl_context); XDestroyWindow(this->display, this->opengl_window); } // Handles video output static void *opengl_draw_frame_thread(void *arg) { sxfe_t *this=(sxfe_t *) arg; //int unsigned sync; int draw_frame = 0, window_mapped = 0, keep_osd_open = 0; int prev_osd_visible = 0; int16_t video_x0, video_y0, video_x1, video_y1; int16_t osd_x0 ,osd_y0, osd_x1, osd_y1; GLfloat osd_alpha = 0; GLfloat osd_alpha_step = 0.2; static int unsigned count = 0; int16_t win_width = -1, win_height = -1; int16_t win_x = -1, win_y = -1; #ifdef HAVE_XSHAPE XRectangle xrect; #endif GLfloat video_tex_width, video_tex_height; int first_frame = 1; int force_redirect = 0; //struct timeval t; XLockDisplay (this->display); if (opengl_init(this) < 0) { LOGMSG("OpenGL initialization failed"); XUnlockDisplay (this->display); exit(1); } XUnlockDisplay (this->display); while (1) { // Wait for trigger pthread_mutex_lock(&this->opengl_redraw_mutex); if (this->opengl_redraw_served_nr == this->opengl_redraw_request_nr) { pthread_cond_wait(&this->opengl_redraw_cv, &this->opengl_redraw_mutex); } pthread_mutex_unlock(&this->opengl_redraw_mutex); //time_measure_start(&t); // Check if we should exit if (this->opengl_deinit) break; // Check if we need to change the shape of the window if (win_x != this->x.xpos || win_y != this->x.ypos || win_width != this->x.width || win_height != this->x.height) { // Update sizes win_x = this->x.xpos; win_y = this->x.ypos; win_width = this->x.width; win_height = this->x.height; force_redirect = 1; // Set the shape of the opengl window #ifdef HAVE_XSHAPE xrect.x = win_x; xrect.y = win_y; xrect.width = win_width; xrect.height = win_height; if (this->opengl_xshape_available) XShapeCombineRectangles(this->display, this->opengl_window, ShapeBounding, 0, 0, &xrect, 1, ShapeSet, 0); #endif } LOGVERBOSE("win_x=%d win_y=%d win_width=%d win_height=%d", win_x, win_y, win_width, win_height); // Update the global alpha value of the OSD keep_osd_open = 0; if (this->osd_visible) { if (osd_alpha < 1.0) { osd_alpha += osd_alpha_step; if (osd_alpha > 1.0) osd_alpha = 1.0; } } else { this->video_win_active = 0; if (osd_alpha > 0.0) { osd_alpha -= osd_alpha_step; if (osd_alpha < 0.0) osd_alpha = 0.0; keep_osd_open = 1; } } LOGVERBOSE("osd_alpha=%.2f keep_osd_open=%d", osd_alpha, keep_osd_open); // Decide if we need to do something draw_frame = (this->osd_visible || window_mapped || this->opengl_always); if ((this->opengl_hud && this->osd_visible && (!prev_osd_visible || force_redirect)) || (this->opengl_always && (first_frame || force_redirect))) { LOGDBG("redirecting video to opengl frame texture"); xine_port_send_gui_data(this->x.video_port, XINE_GUI_SEND_DRAWABLE_CHANGED, (void*) this->video_frame_pixmap); draw_frame = 0; // first frame not yet available in pixmap count = 0; osd_alpha -= 2*osd_alpha_step; // delay the osd } if (this->opengl_hud && !this->osd_visible && prev_osd_visible && !keep_osd_open && !this->opengl_always) { LOGDBG("redirecting video to window"); xine_port_send_gui_data(this->x.video_port, XINE_GUI_SEND_DRAWABLE_CHANGED, (void*) this->window[this->fullscreen ? 1 : 0]); count = 0; } if (draw_frame) { LOGVERBOSE("drawing frame nr %d", count); XLockDisplay(this->display); video_tex_width = 1.0; video_tex_height = 1.0; if (this->video_win_active) { pthread_mutex_lock(&this->video_win_mutex); video_x0 = win_x + this->video_win_x; video_y0 = win_y + this->video_win_y; video_tex_width = ((GLfloat)win_width) / (GLfloat)this->screen_width; video_tex_height = ((GLfloat)win_height) / (GLfloat)this->screen_height; video_x1 = video_x0 + (this->video_win_w - 1); video_y1 = video_y0 + (this->video_win_h - 1); pthread_mutex_unlock(&this->video_win_mutex); } else { video_x0 = win_x; video_y0 = win_y; video_x1 = video_x0 + this->screen_width - 1; video_y1 = video_y0 + this->screen_height - 1; } osd_x0 = win_x; osd_y0 = win_y; osd_x1 = osd_x0 + win_width - 1; osd_y1 = osd_y0 + win_height - 1; video_y0 = (this->screen_height - 1) - video_y0; video_y1 = (this->screen_height - 1) - video_y1; osd_y0 = (this->screen_height - 1) - osd_y0; osd_y1 = (this->screen_height - 1) - osd_y1; LOGVERBOSE("video_x0=%d video_y0=%d video_x1=%d video_y1=%d", video_x0, video_y0, video_x1, video_y1); LOGVERBOSE("osd_x0=%d osd_y0=%d osd_x1=%d osd_y1=%d", osd_x0, osd_y0, osd_x1, osd_y1); glClear(GL_COLOR_BUFFER_BIT); glBindTexture (GL_TEXTURE_2D, this->video_frame_texture); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glBegin(GL_QUADS); glTexCoord2f(0.0, video_tex_height); glVertex3f(video_x0, video_y1 , 0.0); glTexCoord2f(video_tex_width, video_tex_height); glVertex3f(video_x1, video_y1, 0.0); glTexCoord2f(video_tex_width, 0.0); glVertex3f(video_x1, video_y0, 0.0); glTexCoord2f(0.0, 0.0); glVertex3f(video_x0, video_y0, 0.0); glEnd(); if (this->osd_visible || keep_osd_open) { // Check if we need to update the image glBindTexture (GL_TEXTURE_2D, this->osd_texture); pthread_mutex_lock(&this->opengl_osd_texture_img_mutex); if (this->opengl_osd_texture_img_updated) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this->osd_width, this->osd_height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, this->opengl_osd_texture_img); GLenum error = glGetError(); if (error != GL_NO_ERROR) { LOGERR("Can't update hud image texture"); } this->opengl_osd_texture_img_updated = 0; } pthread_mutex_unlock(&this->opengl_osd_texture_img_mutex); // Draw the hud glColor4f(1.0f, 1.0f, 1.0f, osd_alpha); glBegin(GL_QUADS); glTexCoord2f(0.0, 1.0); glVertex3f(osd_x0, osd_y1 , 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(osd_x1, osd_y1, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(osd_x1, osd_y0, 0.0); glTexCoord2f(0.0, 0.0); glVertex3f(osd_x0, osd_y0, 0.0); glEnd(); } glXSwapBuffers(this->display, this->opengl_window); XUnlockDisplay(this->display); count++; } if ((this->osd_visible && count==2 && !window_mapped) || (first_frame && this->opengl_always) || force_redirect) { LOGDBG("mapping opengl window"); XLockDisplay(this->display); XRaiseWindow (this->display, this->opengl_window); XMapWindow(this->display, this->opengl_window); XUnlockDisplay(this->display); window_mapped = 1; } if (!this->osd_visible && count==3 && window_mapped && !keep_osd_open && !this->opengl_always) { LOGDBG("unmapping opengl window"); XLockDisplay(this->display); XLowerWindow (this->display, this->opengl_window); XUnmapWindow(this->display, this->opengl_window); XUnlockDisplay(this->display); window_mapped = 0; } if (!keep_osd_open) { prev_osd_visible = this->osd_visible; } first_frame = 0; force_redirect = 0; //LOGVERBOSE("drawing time = %d",time_measure_end(&t)); // Drawing is finished pthread_mutex_lock(&this->opengl_redraw_finished_mutex); this->opengl_redraw_served_nr++; pthread_cond_signal(&this->opengl_redraw_finished_cv); pthread_mutex_unlock(&this->opengl_redraw_finished_mutex); } // Free resources opengl_deinit(this); return NULL; } static int opengl_start(sxfe_t *this) { LOGDBG("sxfe_display_open: starting opengl drawing thread"); pthread_mutex_init(&this->opengl_redraw_mutex, NULL); pthread_cond_init(&this->opengl_redraw_cv, NULL); this->opengl_redraw_request_nr = 0; this->opengl_redraw_served_nr = 0; pthread_mutex_init(&this->opengl_redraw_finished_mutex, NULL); pthread_cond_init(&this->opengl_redraw_finished_cv, NULL); pthread_mutex_init(&this->opengl_osd_texture_img_mutex, NULL); pthread_attr_t attr; struct sched_param param; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_getschedparam(&attr, ¶m); param.sched_priority = sched_get_priority_min(SCHED_OTHER); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); this->x.vis_x11.frame_output_cb = opengl_frame_output_cb; // ensure that the opengl drawing thread gets triggered this->fe.xine_osd_command = opengl_osd_command; // opengl thread needs to get updated with the new hud image this->osd_width = OSD_DEF_WIDTH; this->osd_height = OSD_DEF_HEIGHT; this->opengl_osd_texture_img = malloc(sizeof(uint32_t) * this->osd_width * this->osd_height); memset((void*)this->opengl_osd_texture_img,0,sizeof(uint32_t)*this->osd_width*this->osd_height); this->opengl_osd_texture_img_updated = 0; #ifdef HAVE_XSHAPE int dummy; if (XShapeQueryExtension(this->display, &dummy, &dummy)) this->opengl_xshape_available = 1; else this->opengl_xshape_available = 0; #endif if (pthread_create(&this->opengl_drawing_thread, &attr, opengl_draw_frame_thread, (void *)this)) { pthread_attr_destroy(&attr); LOGERR("sxfe_display_open: can not start OpenGL drawing thread"); this->opengl_always = this->opengl_hud = 0; /* avoid pthread_join segfault */ return 0; } pthread_attr_destroy(&attr); return 1; } #endif /* * sxfe_display_open * * connect to X server, create windows */ static int sxfe_display_open(frontend_t *this_gen, int xpos, int ypos, int width, int height, int fullscreen, int hud, int opengl, int modeswitch, const char *modeline, int aspect, int no_x_kbd, int gui_hotkeys, int touchscreen, const char *video_port, int scale_video, const char *aspect_controller, int window_id) { sxfe_t *this = (sxfe_t*)this_gen; if(this->display) this->fe.fe_display_close(this_gen); LOGDBG("sxfe_display_open(width=%d, height=%d, fullscreen=%d, display=%s)", width, height, fullscreen, video_port); pthread_mutex_init(&this->video_win_mutex, NULL); if(this->fe.fe_message_cb) { /* trigger key learning mode */ this->fe.fe_message_cb(this->fe.fe_message_h, "XKeySym", ""); } #if defined(HAVE_XRENDER) || defined(HAVE_OPENGL) this->osd_width = OSD_DEF_WIDTH; this->osd_height = OSD_DEF_HEIGHT; #endif if (opengl) { #ifdef HAVE_OPENGL LOGDBG("sxfe_display_open: Using opengl to draw video and HUD OSD"); this->opengl_always = 1; #else LOGMSG("sxfe_display_open: Application was compiled without OpenGL support."); return 0; #endif } else if (hud & HUD_OPENGL) { #ifdef HAVE_OPENGL LOGDBG("sxfe_display_open: Using opengl to draw HUD OSD only"); this->opengl_hud = 1; #else LOGMSG("sxfe_display_open: Application was compiled without OpenGL support."); return 0; #endif } else if (hud) { #ifdef HAVE_XRENDER LOGDBG("sxfe_display_open: Enabling HUD OSD"); this->hud = hud; # ifdef HAVE_XSHAPE this->xshape_hud = !!(hud & HUD_XSHAPE); # endif #else LOGMSG("sxfe_display_open: Application was compiled without XRender support. HUD OSD disabled."); #endif } this->x.xpos = xpos; this->x.ypos = ypos; this->x.width = width; this->x.height = height; this->x.aspect = aspect; /*this->x.cropping = 0;*/ this->x.overscan = 0; this->x.scale_video = scale_video; this->x.aspect_controller = aspect_controller ? strdup(aspect_controller) : NULL; this->video_win_active = 0; this->origxpos = 0; this->origypos = 0; this->origwidth = width>0 ? width : OSD_DEF_WIDTH; this->origheight = height>0 ? height : OSD_DEF_HEIGHT; this->check_move = 0; this->dragging = 0; this->dragging_x = 0; this->dragging_y = 0; this->fullscreen = fullscreen; /*this->vmode_switch = modeswitch;*/ this->fullscreen_state_forced = 0; /*this->modeline = strdup(modeline ?: "");*/ this->window_id = window_id; this->xinerama_screen = -1; this->gui_hotkeys = !!gui_hotkeys; this->no_x_kbd = !!no_x_kbd; this->touchscreen = !!touchscreen; /* * init x11 stuff */ if (!XInitThreads ()) { LOGERR("sxfe_display_open: XInitThreads failed"); free(this); return 0; } if (!open_display(this, video_port)) return 0; XLockDisplay (this->display); this->screen = DefaultScreen(this->display); this->root_window = DefaultRootWindow(this->display); /* #warning sxfe_display_open: TODO: switch vmode */ /* completion event */ #ifdef HAVE_XSHM this->xshm_completion_event = -1; if (XShmQueryExtension (this->display) == True) { this->xshm_completion_event = XShmGetEventBase (this->display) + ShmCompletion; } #endif init_atoms(this); if(fullscreen) update_screen_size(this); /* Output to existing window ? (embedded to another app) */ if (this->window_id == WINDOW_ID_ROOT) { this->window_id = this->root_window; } if(this->window_id > 0) { LOGMSG("sxfe_display_open(): Using X11 window %d for output", this->window_id); this->window[0] = this->window[1] = (Window)this->window_id; XUnmapWindow(this->display, this->window[0]); } else { create_windows(this); } /* Select input */ XSelectInput (this->display, this->window[0], StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | FocusChangeMask | PointerMotionMask); XSelectInput (this->display, this->window[1], StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | FocusChangeMask | PointerMotionMask); /* Get notified when root window size changes */ XSelectInput (this->display, this->root_window, StructureNotifyMask); /* Map current window */ XMapRaised (this->display, this->window[!!this->fullscreen]); XMoveWindow(this->display, this->window[0], this->x.xpos, this->x.ypos); /* determine display aspect ratio */ this->x.display_ratio = detect_display_ratio(this->display, this->screen); /* we want to get notified if user closes the window */ XSetWMProtocols(this->display, this->window[this->fullscreen ? 1 : 0], &(this->xa_WM_DELETE_WINDOW), 1); /* Hide cursor */ if(this->window_id <= 0) { set_cursor(this->display, this->window[this->fullscreen ? 1 : 0], 0); this->mousecursor_timeout = 0; } /* No screen saver */ /* #warning TODO: suspend --> activate blank screen saver / DPMS display off ? */ XSetScreenSaver(this->display, 0, 0, DefaultBlanking, DefaultExposures); /* Disable DPMS */ disable_DPMS(this); #ifdef HAVE_DBUS_GLIB_1 /* Disable GNOME screensaver */ gnome_screensaver_control(0); #endif /* setup xine visual type */ this->x.xine_visual_type = XINE_VISUAL_TYPE_X11; this->x.vis_x11.display = this->display; this->x.vis_x11.screen = this->screen; this->x.vis_x11.d = this->window[this->fullscreen ? 1 : 0]; this->x.vis_x11.dest_size_cb = sxfe_dest_size_cb; this->x.vis_x11.frame_output_cb = this->x.frame_output_handler; this->x.vis_x11.user_data = this; set_fullscreen_props(this); XUnlockDisplay (this->display); #ifdef HAVE_OPENGL // Shall opengl or xrender be used? if (this->opengl_always || this->opengl_hud) { // Start the drawing thread if (!opengl_start(this)) return 0; } #endif #ifdef HAVE_XRENDER // Init the osd window if (!hud_osd_open(this)) return 0; #endif return 1; } /* * sxfe_display_config * * configure windows */ static int sxfe_display_config(frontend_t *this_gen, int xpos, int ypos, int width, int height, int fullscreen, int modeswitch, const char *modeline, int aspect, int scale_video) { sxfe_t *this = (sxfe_t*)this_gen; if(this->fullscreen_state_forced) fullscreen = this->fullscreen ? 1 : 0; if(!fullscreen && (this->x.width != width || this->x.height != height)) { this->x.width = width; this->x.height = height; XLockDisplay(this->display); XResizeWindow(this->display, this->window[0], this->x.width, this->x.height); XUnlockDisplay(this->display); if(!fullscreen && !this->fullscreen) xine_port_send_gui_data(this->x.video_port, XINE_GUI_SEND_DRAWABLE_CHANGED, (void*) this->window[0]); } if(fullscreen) update_screen_size(this); if(fullscreen != this->fullscreen) { Window tmp_win; int tmp_x, tmp_y; XLockDisplay(this->display); XUnmapWindow(this->display, this->window[this->fullscreen ? 1 : 0]); this->fullscreen = fullscreen ? 1 : 0; if(fullscreen) set_fullscreen_props(this); else set_above(this, this->stay_above); XMapRaised(this->display, this->window[this->fullscreen ? 1 : 0]); if(!fullscreen) { XResizeWindow(this->display, this->window[0], this->x.width, this->x.height); XMoveWindow(this->display, this->window[0], this->x.xpos, this->x.ypos); LOGDBG("sxfe_display_config: XMoveWindow called with x=%d and y=%d", this->x.xpos, this->x.ypos); this->check_move = 1; set_above(this, this->stay_above); } else { set_fullscreen_props(this); XResizeWindow(this->display, this->window[1], this->x.width, this->x.height); XMoveWindow(this->display, this->window[1], this->xinerama_x, this->xinerama_y); } XSync(this->display, False); if(XTranslateCoordinates(this->display, this->window[this->fullscreen ? 1 : 0], this->root_window, 0, 0, &tmp_x, &tmp_y, &tmp_win)) { this->x.xpos = tmp_x; this->x.ypos = tmp_y; } XUnlockDisplay(this->display); xine_port_send_gui_data(this->x.video_port, XINE_GUI_SEND_DRAWABLE_CHANGED, (void*) this->window[this->fullscreen ? 1 : 0]); } #if 0 if(!modeswitch && strcmp(modeline, this->modeline)) { free(this->modeline); this->modeline = strdup(modeline ?: ""); /* #warning TODO - switch vmode */ } #endif /*this->vmode_switch = modeswitch;*/ this->x.aspect = aspect; this->x.scale_video = scale_video; return 1; } static void sxfe_toggle_fullscreen(fe_t *this_gen, int fullscreen) { sxfe_t *this = (sxfe_t*)this_gen; int force = this->fullscreen_state_forced; this->fullscreen_state_forced = 0; if(!this->fullscreen) { this->origwidth = this->x.width; this->origheight = this->x.height; this->origxpos = this->x.xpos; this->origypos = this->x.ypos; } else { this->x.xpos = this->origxpos; this->x.ypos = this->origypos; } if (fullscreen < 0) fullscreen = !this->fullscreen; this->fe.fe_display_config((frontend_t*)this, -1, -1, this->origwidth, this->origheight, fullscreen, 0/*this->vmode_switch*/, NULL/*this->modeline*/, this->x.aspect, this->x.scale_video); this->fullscreen_state_forced = !force; } static unsigned char *sxfe_display_edid(frontend_t *this_gen, int *size) { sxfe_t *this = (sxfe_t*)this_gen; unsigned char *edid = NULL; #ifdef HAVE_XRANDR XLockDisplay(this->display); do { if (!this->display) break; int event_base, error_base; int major, minor; if (!XRRQueryExtension(this->display, &event_base, &error_base) || !XRRQueryVersion(this->display, &major, &minor)) { LOGMSG("edid: RandR extension missing"); break; } if (major < 1 || (major == 1 && minor < 2)) { LOGMSG("edid: RandR extension < 1.2"); break; } XRRScreenResources *res; res = XRRGetScreenResourcesCurrent(this->display, this->root_window); if (!res) { LOGMSG("edid: failed getting screen resources"); break; } int o; for (o = 0; o < res->noutput && !edid; o++) { XRROutputInfo *output_info = XRRGetOutputInfo(this->display, res, res->outputs[o]); if (!output_info) { LOGMSG("edid: failed getting output %d information", o); continue; } if (output_info->connection != RR_Connected) { LOGDBG("edid: output %s not connected", output_info->name); continue; } LOGDBG("edid: checking connected output %s", output_info->name); int nprop, j; Atom *props = XRRListOutputProperties(this->display, res->outputs[o], &nprop); for (j = 0; j < nprop && !edid; j++) { char *atom_name = XGetAtomName(this->display, props[j]); if (!strcmp(atom_name, "EDID")) { unsigned char *prop; int actual_format; unsigned long nitems, bytes_after; Atom actual_type; XRRGetOutputProperty(this->display, res->outputs[o], props[j], 0, 100, False, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop); if (actual_format == 8 && actual_type == XA_INTEGER) { LOGDBG("edid: Found EDID, %ld bytes", nitems); *size = nitems; edid = malloc(*size); memcpy(edid, prop, *size); break; } } } } } while (0); XUnlockDisplay(this->display); if (!edid) { LOGMSG("no EDID found"); } #endif /* HAVE_XRANDR */ return edid; } /* * X event loop */ /* * sxfe_interrupt * * - Interrupt X event loop (sxfe_run) * */ static void sxfe_interrupt(frontend_t *this_gen) { sxfe_t *this = (sxfe_t*)this_gen; XClientMessageEvent event = { .type = ClientMessage, .display = this->display, .window = this->window[this->fullscreen ? 1 : 0], .message_type = this->xa_SXFE_INTERRUPT, .format = 32, }; XLockDisplay (this->display); if(!XSendEvent(event.display, event.window, 1, /*KeyPressMask*/0, (XEvent *)&event)) LOGERR("sxfe_interrupt: XSendEvent(ClientMessage) FAILED\n"); XFlush(this->display); XUnlockDisplay (this->display); } /* * XKeyEvent handler * */ static void XKeyEvent_handler(sxfe_t *this, XKeyEvent *kev) { if(kev->keycode && kev->type == KeyPress) { KeySym ks; char buffer[20]; XComposeStatus status; const char *fe_event = NULL; const char *ks_name; /* resolve key symbol */ XLockDisplay (this->display); XLookupString(kev, buffer, sizeof(buffer), &ks, &status); ks_name = XKeysymToString(ks); XUnlockDisplay (this->display); /* hotkeys */ switch(ks) { case XK_f: case XK_F: if (this->gui_hotkeys) fe_event = "TOGGLE_FULLSCREEN"; break; case XK_d: case XK_D: if (this->gui_hotkeys) fe_event = "TOGGLE_DEINTERLACE"; break; case XK_p: case XK_P: if (this->gui_hotkeys) fe_event = "POWER_OFF"; break; case XK_Escape: if (!this->fe.fe_message_cb) /* ESC exits only in remote mode */ fe_event = "QUIT"; break; default:; } if (fe_event) { this->x.fe.send_event((frontend_t*)this, fe_event); return; } /* send key event to VDR */ if (!this->no_x_kbd && ks_name) { /* check for key modifiers (Alt/Ctrl) */ char keyname[40] = ""; if (kev->state & (Mod1Mask|ControlMask)) { if (kev->state & Mod1Mask) { strcat(keyname, "Alt+"); } if (kev->state & ControlMask) { strcat(keyname, "Ctrl+"); } strncat(keyname, ks_name, sizeof(keyname) - 11); ks_name = keyname; } this->x.fe.send_input_event((frontend_t*)this, "XKeySym", ks_name, 0, 0); } } } /* * XConfigureEvent handler * */ static void XConfigureEvent_handler(sxfe_t *this, XConfigureEvent *cev) { #ifdef HAVE_OPENGL if (cev->window == this->opengl_window) return; #endif /* root window size changed ? */ if (cev->window != (Window)this->window_id && cev->window == this->root_window) { if (this->fullscreen) { if (cev->width != this->x.width || cev->height != this->x.height) { LOGMSG("Root window size changed. Resizing video window from %dx%d to %dx%d", this->x.width, this->x.height, cev->width, cev->height); XLockDisplay(this->display); XResizeWindow(this->display, this->window[1], cev->width, cev->height); XMoveWindow(this->display, this->window[1], this->xinerama_x, this->xinerama_y); XUnlockDisplay(this->display); } } return; } /* Move and resize HUD along with main or fullscreen window */ #ifdef HAVE_XRENDER if(this->hud) hud_osd_resize(this, cev->window, cev->width, cev->height); #endif /* update video window size */ if (this->x.width != cev->width || this->x.height != cev->height) { if ( ( this->fullscreen && this->window[1] == cev->window) || (!this->fullscreen && this->window[0] == cev->window)) { LOGDBG("Video window size changed from %dx%d to %dx%d", this->x.width, this->x.height, cev->width, cev->height); this->x.width = cev->width; this->x.height = cev->height; /* inform VDR about new size */ char str[128]; int w = this->x.width; int h = this->x.height; #ifdef HAVE_XRENDER // XXX quick fix for UHD displays. Should be fixed in overlay handling, not here. if (this->hud) { if (w > HUD_MAX_WIDTH) w = HUD_MAX_WIDTH; if (h > HUD_MAX_HEIGHT) h = HUD_MAX_HEIGHT; } #endif snprintf(str, sizeof(str), "INFO WINDOW %dx%d", w, h); this->x.fe.send_event((frontend_t*)this, str); } } if(this->window[0] == cev->window && this->check_move) { LOGDBG("ConfigureNotify reveived with x=%d, y=%d, check_move=%d", cev->x, cev->y, this->check_move); this->check_move = 0; if(this->x.xpos != cev->x && this->x.ypos != cev->y) { XLockDisplay (this->display); XMoveWindow(this->display, this->window[0], cev->x, cev->y); XUnlockDisplay (this->display); } } if (!this->fullscreen) { if ((cev->x == 0) && (cev->y == 0)) { int tmp_x, tmp_y; Window tmp_win; XLockDisplay(this->display); if(XTranslateCoordinates(this->display, cev->window, this->root_window, 0, 0, &tmp_x, &tmp_y, &tmp_win)) { this->x.xpos = tmp_x; this->x.ypos = tmp_y; } XUnlockDisplay(this->display); } else { /* update video window position */ this->x.xpos = cev->x; this->x.ypos = cev->y; } } } /* * XMotionEvent handler * * Track mouse movement when Button1 is pressed down * - enable window dragging: user can simply drag window around screen * - useful when window is borderless (no title bar) */ static void XMotionEvent_handler(sxfe_t *this, XMotionEvent *mev) { // make mouse cursor visible if (this->mousecursor_timeout <= 0) { set_cursor(this->display, this->window[this->fullscreen ? 1 : 0], 1); } // start timeout this->mousecursor_timeout = MOUSECURSOR_TIMEOUT; if(this->dragging && !this->fullscreen) { Window tmp_win; int xpos, ypos; XLockDisplay(this->display); while(XCheckMaskEvent(this->display, ButtonMotionMask, (XEvent*)mev)); XTranslateCoordinates(this->display, this->window[0], this->root_window, 0, 0, &xpos, &ypos, &tmp_win); this->x.xpos = (xpos += mev->x_root - this->dragging_x); this->x.ypos = (ypos += mev->y_root - this->dragging_y); this->dragging_x = mev->x_root; this->dragging_y = mev->y_root; XMoveWindow(this->display, this->window[0], xpos, ypos); LOGDBG("MotionNotify: XMoveWindow called with x=%d and y=%d", xpos, ypos); XUnlockDisplay(this->display); } } /* * XButtonEvent handler * * - Double click switches between windowed and fullscreen mode * - Window can be moved by dragging it * - Right mouse button switches window state: * normal window -> borderless window -> always on top -> ... */ static void XButtonEvent_handler(sxfe_t *this, XButtonEvent *bev) { switch(bev->button) { case Button1: if (this->touchscreen) { int x = bev->x * 4 / this->x.width; int y = bev->y * 3 / this->x.height; static const char * const map[3][4] = { {"Menu", "Up", "Back", "Ok"}, {"Left", "Down", "Right", "Ok"}, {"Red", "Green", "Yellow", "Blue"}}; if (map[y][x]) { char tmp[128]; sprintf(tmp, "KEY %s", map[y][x]); this->x.fe.send_event((frontend_t*)this, tmp); } return; } /* Double-click toggles between fullscreen and windowed mode */ if(bev->time - this->prev_click_time < DOUBLECLICK_TIME) { /* Toggle fullscreen */ this->x.toggle_fullscreen_cb(&this->x, -1); this->prev_click_time = 0; /* don't react to third click ... */ } else { this->prev_click_time = bev->time; if(!this->fullscreen && this->no_border && !this->dragging) { /* start dragging window */ this->dragging = 1; this->dragging_x = bev->x_root; this->dragging_y = bev->y_root; } } break; case Button3: /* Toggle border and stacking */ if(!this->fullscreen) { if(!this->stay_above) { set_above(this, 1); } else if(!this->no_border) { set_border(this, this->window[0], 0); this->no_border = 1; } else { set_border(this, this->window[0], 1); this->no_border = 0; set_above(this, 0); } } break; default:; // ignore other buttons. } } /* * sxfe_run * * - main X event loop */ static void check_mouse_cursor_hide(sxfe_t *this, int64_t elapsed) { if (elapsed > 0 && elapsed < 500) { this->mousecursor_timeout -= elapsed; if (this->mousecursor_timeout <= 0) { // hide Cursor set_cursor(this->display, this->window[this->fullscreen ? 1 : 0], 0); } } } static int sxfe_run(frontend_t *this_gen) { sxfe_t *this = (sxfe_t*)this_gen; /* poll X server (connection socket). * (XNextEvent will block until events are queued). * We want to use timeout, blocking for long time usually causes vdr * watchdog to emergency exit ... */ if (! XPending(this->display)) { uint64_t poll_time = 0; const int poll_timeout = 50; struct pollfd pfd = { .fd = ConnectionNumber(this->display), .events = POLLIN, }; if (this->mousecursor_timeout > 0) { poll_time = time_ms(); } #ifdef HAVE_MCE_DBUS_NAMES # ifdef HAVE_DBUS_GLIB_1 /* Disable MCE screensaver */ if (++this->mce_blank_prevent_timer > 100) { gnome_screensaver_control(0); this->mce_blank_prevent_timer = 0; } # endif #endif if (poll(&pfd, 1, poll_timeout) < 1 || !(pfd.revents & POLLIN)) { if (this->mousecursor_timeout > 0) { check_mouse_cursor_hide(this, poll_timeout); } return !this->x.fe.xine_is_finished((frontend_t*)this, 0); } if (poll_time) { check_mouse_cursor_hide(this, elapsed(poll_time)); } } while (XPending(this->display) > 0) { XEvent event; XLockDisplay (this->display); XNextEvent (this->display, &event); XUnlockDisplay (this->display); switch (event.type) { case Expose: if (event.xexpose.count == 0) xine_port_send_gui_data (this->x.video_port, XINE_GUI_SEND_EXPOSE_EVENT, &event); break; case ConfigureNotify: XConfigureEvent_handler(this, (XConfigureEvent *) &event); break; #ifdef HAVE_XRENDER case FocusIn: case FocusOut: hud_osd_focus(this, (XFocusChangeEvent *) &event); break; #endif case ButtonRelease: this->dragging = 0; break; case MotionNotify: XMotionEvent_handler(this, (XMotionEvent *) &event); break; case ButtonPress: XButtonEvent_handler(this, (XButtonEvent *) &event); break; case KeyPress: case KeyRelease: XKeyEvent_handler(this, (XKeyEvent *) &event); break; case ClientMessage: { XClientMessageEvent *cmessage = (XClientMessageEvent *) &event; if ( cmessage->message_type == this->xa_SXFE_INTERRUPT ) LOGDBG("ClientMessage: sxfe_interrupt"); if ( (Atom)cmessage->data.l[0] == this->xa_WM_DELETE_WINDOW ) { /* we got a window deletion message from out window manager.*/ LOGDBG("ClientMessage: WM_DELETE_WINDOW"); this->x.fe.send_event((frontend_t*)this, "QUIT"); } break; } default:; // ignore other events. } #ifdef HAVE_XSHM if (event.type == this->xshm_completion_event) xine_port_send_gui_data (this->x.video_port, XINE_GUI_SEND_COMPLETION_EVENT, &event); #endif } return !this->x.fe.xine_is_finished((frontend_t*)this, 0); } static void sxfe_display_close(frontend_t *this_gen) { sxfe_t *this = (sxfe_t*)this_gen; if(!this) return; if(this->x.xine) this->fe.xine_exit(this_gen); if(this->display) { #ifdef HAVE_OPENGL if (this->opengl_always || this->opengl_hud) { void *status; this->opengl_deinit = 1; opengl_trigger_drawing_thread(this); if (pthread_join(this->opengl_drawing_thread, &status)) { LOGERR("sxfe_display_close: can not join opengl drawing thread!"); } free(this->opengl_osd_texture_img); } #endif #ifdef HAVE_XRENDER hud_osd_close(this); #endif #ifdef HAVE_DBUS_GLIB_1 /* Restore GNOE screensaver */ gnome_screensaver_control(1); #endif #ifdef HAVE_XDPMS if(this->dpms_state) DPMSEnable(this->display); #endif if(this->window_id <= 0) { XLockDisplay(this->display); XUnmapWindow(this->display, this->window[this->fullscreen ? 1 : 0]); XDestroyWindow(this->display, this->window[0]); XDestroyWindow(this->display, this->window[1]); XUnlockDisplay(this->display); } XCloseDisplay (this->display); this->display = NULL; pthread_mutex_destroy(&this->video_win_mutex); } free(this->x.aspect_controller); this->x.aspect_controller = NULL; #if 0 free(this->modeline); this->modeline = NULL; #endif } /* * sxfe_xine_open * * Override fe_xine_open: * - Set window name: append remote host address to title bar text */ static int sxfe_xine_open(frontend_t *this_gen, const char *mrl) { sxfe_t *this = (sxfe_t*)this_gen; int result = this->fe_xine_open(this_gen, mrl); if(result && mrl && !strncmp(mrl, MRL_ID, MRL_ID_LEN) && strstr(mrl, "//")) { char *name = NULL, *end; if (asprintf(&name, "VDR - %s", strstr(mrl, "//")+2) >= 0) { if (NULL != (end = strstr(name, ":37890")) || /* hide only default port */ NULL != (end = strchr(name, '#'))) /* hide attributes */ *end = 0; XStoreName(this->display, this->window[0], name); XStoreName(this->display, this->window[1], name); free(name); } } return result; } static int sxfe_xine_play(frontend_t *this_gen) { sxfe_t *this = (sxfe_t*)this_gen; int result = this->fe_xine_play(this_gen); if (result && this->x.input_plugin) { #ifdef HAVE_XRENDER if (this->hud) { LOGDBG("sxfe_xine_play: Enabling HUD OSD"); this->x.input_plugin->f.fe_handle = this_gen; this->x.input_plugin->f.intercept_osd = hud_osd_command; this->x.fe.send_event((frontend_t*)this, "INFO ARGBOSD"); } #endif /* HAVE_XRENDER */ #ifdef HAVE_OPENGL if (this->opengl_always || this->opengl_hud) { LOGDBG("sxfe_xine_play: Enabling OpenGL OSD"); this->x.input_plugin->f.fe_handle = this_gen; this->x.input_plugin->f.intercept_osd = opengl_osd_command; this->x.fe.send_event((frontend_t*)this, "INFO ARGBOSD"); } #endif /* HAVE_OPENGL */ } return result; } static frontend_t *sxfe_get_frontend(void) { sxfe_t *this = calloc(1, sizeof(sxfe_t)); init_fe((fe_t*)this); this->window_id = -1; this->fe.fe_display_open = sxfe_display_open; this->fe.fe_display_config = sxfe_display_config; this->fe.fe_display_edid = sxfe_display_edid; this->fe.fe_display_close = sxfe_display_close; this->fe.fe_run = sxfe_run; this->fe.fe_interrupt = sxfe_interrupt; this->x.toggle_fullscreen_cb = sxfe_toggle_fullscreen; /* override */ this->fe_xine_open = this->fe.xine_open; this->fe_xine_play = this->fe.xine_play; this->fe.xine_open = sxfe_xine_open; this->fe.xine_play = sxfe_xine_play; return (frontend_t*)this; } /* ENTRY POINT */ const fe_creator_f fe_creator __attribute__((visibility("default"))) = sxfe_get_frontend; xineliboutput-2.0.0/xine_post_swscale.c0000644000175000017500000020612613061253352016130 0ustar phph/* * Copyright (C) 2000-2007 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-1301 USA * * $Id$ * * Simple (faster) resize for avisynth * Copyright (C) 2002 Tom Barry * * Very simple 2 tap linear interpolation. * It is unfiltered which means it will not soften much. * * WarpedResize will do a non-linear stretch/squeeze in both the horizontal * and vertical dimensions. This can be useful when you want to change the * aspect ratio of a video clip and have it mostly distorted at the * top, bottom, and side edges. * * * Ported to linux/xine by Petri Hintukainen * - Added x86_64 support * - Added PIC support (do not clobber ebx in x86, access only local variables from asm) * - Fixed yv12 stretched warp tables generation */ #include #include #include /* DBL_MIN */ /* * tools */ #ifndef ALIGN # define ALIGN(b,p) ((void*)((((unsigned long)(p)) + (b)-1) & (~((b)-1)))) #endif #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX # define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif #ifndef FABS # define FABS(x) ((x) < 0.0 ? -(x) : (x)) #endif static int doublecmp(double a, double b) { if (FABS(a-b) <= 2*DBL_MIN) return 0; // equal else if (a > b) return 1; else return -1; } /*#define DBG(x...)*/ #define DBG(x...) do { if(this->config.debug) fprintf(stderr, "post_warp: " x); } while (0) /*#define STREAMING_STORE_TMP*/ /*#define STREAMING_STORE*/ /*#define PREFETCH*/ /* streaming store and prefetch seems to be slower ... * Tested with P3 (128M L2) and C2D (4M L2). * Maybe access pattern is enough simple for HW prefetchers. */ /*#define VANILLA*/ /* * This function accepts a position from 0 to 1 and warps it, to 0 through 1 based * upon the wFact var. The warp equations are designed to: * * * Always be rising but yield results from 0 to 1 * * * Have a first derivative that doesn't go to 0 or infinity, at least close * to the center of the screen * * * Have a curvature (absolute val of 2nd derivative) that is small in the * center and smoothly rises towards the edges. We would like the curvature * to be everywhere = 0 when the warp factor = 1 */ static double WarpFactor(double position, double wFact) { double x; double z; double w; x = 2 * (position - .5); if (1) /*(wFact < 1.0)*/ /* For warp factor < 1 the warp is calculated as (1-w) * x^3 + w *x, centered * * The warp is calculated as z = (1 - w) * x^3 + w * x, centered * around .5 and ranging from 0 to 1. After some tinkering this seems * to give decent values and derivatives at the right places. */ w = 2.0 - wFact; /* reverse parm for compat with initial release */ if (x < 0.0) { z = -(1 - w) * x*x*x - w * x; /* -1 < x < 0, wFact < 1 */ return .5 - .5 * z; } else { z = (1 - w) * x*x*x + w * x; /* -1 < x < 0, wFact < 1 */ return .5 + .5 * z; /* amts to same formula as above for now */ } } /* * YV12 * * For each horizontal output pair of pixels there is are 2 qword masks followed by 2 int * offsets. The 2 masks are the weights to be used for the luma and chroma, respectively. * Each mask contains LeftWeight1, RightWeight1, LeftWeight2, RightWeight2. So a pair of pixels * will later be processed each pass through the horizontal resize loop. I think with my * current math the Horizontal Luma and Chroma contains the same values but since I may have screwed it * up I'll leave it this way for now. Vertical chroma is different. * * Note - try just using the luma calcs for both, seem to be the same. * * The weights are scaled 0-256 and the left and right weights will sum to 256 for each pixel. */ static void init_tables_yv12(int newwidth, int newheight, int oldwidth, int oldheight, int Interlaced, double hWarp, double vWarp, uint32_t *hControl, uint32_t *vOffsets, uint32_t *vWeights, uint32_t *hControlUV, uint32_t *vOffsetsUV, uint32_t *vWeightsUV) { int i; int j; int k; int wY1; int wY2; /* First set up horizontal table, use for both luma & chroma since * it seems to have the same equation. * We will geneerate these values in pairs, mostly because that's the way * I wrote it for YUY2 above. */ for(i=0; i < newwidth; i+=2) { /* first make even pixel control */ if (doublecmp(hWarp, 1.0) == 0) /* if no warp factor */ j = i * 256 * (oldwidth-1) / (newwidth-1); else /* stretch and warp somehow */ j = (int) (256 * WarpFactor(i / (newwidth-1.0), hWarp) * (oldwidth-1)); k = j>>8; wY2 = j - (k << 8); /* luma weight of right pixel */ wY1 = 256 - wY2; /* luma weight of left pixel */ if (k > oldwidth - 2) { hControl[i*3+4] = oldwidth - 1; /* point to last byte */ hControl[i*3] = 0x00000100; /* use 100% of rightmost Y */ } else { hControl[i*3+4] = k; /* pixel offset */ hControl[i*3] = wY2 << 16 | wY1; /* luma weights */ } /* now make odd pixel control */ if (doublecmp(hWarp, 1.0) == 0) /* if no warp factor */ j = (i+1) * 256 * (oldwidth-1) / (newwidth-1); else /* stretch and warp somehow */ j = (int) (256 * WarpFactor((i+1) / (newwidth-1.0), hWarp) * (oldwidth-1)); k = j>>8; wY2 = j - (k << 8); /* luma weight of right pixel */ wY1 = 256 - wY2; /* luma weight of left pixel */ if (k > oldwidth - 2) { hControl[i*3+5] = oldwidth - 1; /* point to last byte */ hControl[i*3+1] = 0x00000100; /* use 100% of rightmost Y */ } else { hControl[i*3+5] = k; /* pixel offset */ hControl[i*3+1] = wY2 << 16 | wY1; /* luma weights */ } } hControl[newwidth*3+4] = 2 * (oldwidth-1); /* give it something to prefetch at end */ hControl[newwidth*3+5] = 2 * (oldwidth-1); /* " */ #ifndef VANILLA // UV for(i=0; i < newwidth/2; i+=2) { /* first make even pixel control */ if (doublecmp(hWarp, 1.0) == 0) /* if no warp factor */ j = i * 256 * (oldwidth/2-1) / (newwidth/2-1); else /* stretch and warp somehow */ j = (int) (256 * WarpFactor(i / (newwidth/2-1.0), hWarp) * (oldwidth/2-1)); k = j>>8; wY2 = j - (k << 8); /* luma weight of right pixel */ wY1 = 256 - wY2; /* luma weight of left pixel */ if (k > oldwidth/2 - 2) { hControlUV[i*3+4] = oldwidth/2 - 1; /* point to last byte */ hControlUV[i*3] = 0x00000100; /* use 100% of rightmost Y */ } else { hControlUV[i*3+4] = k; /* pixel offset */ hControlUV[i*3] = wY2 << 16 | wY1; /* luma weights */ } /* now make odd pixel control */ if (doublecmp(hWarp, 1.0) == 0) /* if no warp factor */ j = (i+1) * 256 * (oldwidth/2-1) / (newwidth/2-1); else /* stretch and warp somehow */ j = (int) (256 * WarpFactor((i+1) / (newwidth/2-1.0), hWarp) * (oldwidth/2-1)); k = j>>8; wY2 = j - (k << 8); /* luma weight of right pixel */ wY1 = 256 - wY2; /* luma weight of left pixel */ if (k > oldwidth/2 - 2) { hControlUV[i*3+5] = oldwidth/2 - 1; /* point to last byte */ hControlUV[i*3+1] = 0x00000100; /* use 100% of rightmost Y */ } else { hControlUV[i*3+5] = k; /* pixel offset */ hControlUV[i*3+1] = wY2 << 16 | wY1; /* luma weights */ } } hControlUV[newwidth/2*3+4] = (oldwidth/2-1); /* give it something to prefetch at end */ hControlUV[newwidth/2*3+5] = (oldwidth/2-1); /* " */ #endif /* Next set up vertical tables. The offsets are measured in lines and will be mult */ /* by the source pitch later . */ /* For YV12 we need separate Luma and chroma tables */ /* First Luma Table */ for(i=0; i< newheight; ++i) { if (doublecmp(vWarp, 1.0) == 0) /* if no warp factor */ j = i * 256 * (oldheight-1) / (newheight-1); else /* stretch and warp somehow */ j = (int) (256 * WarpFactor(i / (newheight-1.0), vWarp) * (oldheight-1)); if (Interlaced) { /* do hard way? */ if (i%2) { /* is odd output line? */ if (j < 256) { /* before 1st odd input line */ vOffsets[i] = 1; /* all from line 1 */ vWeights[i] = 0; /* weight to give to 2nd line */ } else { k = (((j-256) >> 9) << 1) + 1; /* next lowest odd line */ vOffsets[i] = k; wY2 = j - (k << 8); vWeights[i] = wY2 >> 1; /* weight to give to 2nd line */ } } else { /* is even output line */ k = (j >> 9) << 1; /* next lower even line */ vOffsets[i] = k; wY2 = j - (k << 8); vWeights[i] = wY2 >> 1; /* weight to give to 2nd line */ } } else { /* simple way, do as progressive */ k = j >> 8; vOffsets[i] = k; wY2 = j - (k << 8); vWeights[i] = wY2; /* weight to give to 2nd line */ } } /* Vertical table for chroma */ for(i=0; i< newheight/2; ++i) { if (doublecmp(vWarp, 1.0) == 0) /* if no warp factor */ #ifdef VANILLA j = (int) ( (i+.25) * 256 * (oldheight-1) / (newheight-1.0) - 64 ); #else j = (int) ( (i+.25) * 256 * (oldheight/2-1) / (newheight/2-1.0) - 64 ); #endif else /* stretch and warp somehow */ #ifdef VANILLA j = (int) (256 * WarpFactor( (i+.25) / (newheight-1.0), vWarp) * (oldheight-1.0) ); #else j = (int) (256 * WarpFactor( (i+.25) / (newheight/2 - 1.0), vWarp) * (oldheight/2 - 1.0) ); #endif #ifndef VANILLA if(j<0) j=0; #endif if (Interlaced) { /* do hard way? */ if (i%2) { /* is odd output line? */ if (j < 256) { /* before 1st odd input line */ vOffsetsUV[i] = 1; /* all from line 1 */ vWeightsUV[i] = 0; /* weight to give to 2nd line */ } else { k = (((j-256) >> 9) << 1) + 1; /* next lowest odd line */ vOffsetsUV[i] = k; wY2 = j - (k << 8); vWeightsUV[i] = wY2 >> 1; /* weight to give to 2nd line */ } } else { /* is even output line */ #ifdef VANILLA k = (j >> 9) << 1; /* next lower even line */ vOffsetsUV[i] = k; wY2 = j - (k << 8); vWeightsUV[i] = wY2 >> 1; /* weight to give to 2nd line */ #else k = (j / 512) << 1; /* next lower even line */ vOffsetsUV[i] = k; wY2 = j - (k << 8); vWeightsUV[i] = wY2 >> 1; /* weight to give to 2nd line */ #endif } } else { /* simple way, do as progressive */ #ifdef VANILLA k = j >> 8; #else k = j / 256; /* j >> 8; does not work right if -256 < j < 0 */ #endif vOffsetsUV[i] = k; wY2 = j - (k << 8); vWeightsUV[i] = wY2; /* weight to give to 2nd line */ } } } /* * YUY2 * * For each horizontal output pair of pixels there is are 2 qword masks followed by 2 int * offsets. The 2 masks are the weights to be used for the luma and chroma, respectively. * Each mask contains LeftWeight1, RightWeight1, LeftWeight2, RightWeight2. So a pair of pixels * will later be processed each pass through the horizontal resize loop. * * The weights are scaled 0-256 and the left and right weights will sum to 256 for each pixel. */ static void init_tables_yuy2(int newwidth, int newheight, int oldwidth, int oldheight, int Interlaced, double hWarp, double vWarp, uint32_t *hControl, uint32_t *vOffsets, uint32_t *vWeights ) { int i; int j; int k; int wY1; int wY2; int wUV1; int wUV2; /* First set up horizontal table */ for(i=0; i < newwidth; i+=2) { /* first make even pixel control */ if (doublecmp(hWarp, 1.0) == 0) /* if no warp factor */ j = i * 256 * (oldwidth-1) / (newwidth-1); else /* stretch and warp somehow */ j = (int) (256 * WarpFactor(i / (newwidth-1.0), hWarp) * (oldwidth-1)); k = j>>8; wY2 = j - (k << 8); /* luma weight of right pixel */ wY1 = 256 - wY2; /* luma weight of left pixel */ wUV2 = (k%2) ? 128 + (wY2 >> 1) : wY2 >> 1; wUV1 = 256 - wUV2; if (k > oldwidth - 2) { hControl[i*3+4] = oldwidth - 1; /* point to last byte */ hControl[i*3] = 0x00000100; /* use 100% of rightmost Y */ hControl[i*3+2] = 0x00000100; /* use 100% of rightmost U */ } else { hControl[i*3+4] = k; /* pixel offset */ hControl[i*3] = wY2 << 16 | wY1; /* luma weights */ hControl[i*3+2] = wUV2 << 16 | wUV1; /* chroma weights */ } /* now make odd pixel control */ if (doublecmp(hWarp, 1.0) == 0) /* if no warp factor */ j = (i+1) * 256 * (oldwidth-1) / (newwidth-1); else /* stretch and warp somehow */ j = (int) (256 * WarpFactor((i+1) / (newwidth-1.0), hWarp) * (oldwidth-1)); k = j>>8; wY2 = j - (k << 8); /* luma weight of right pixel */ wY1 = 256 - wY2; /* luma weight of left pixel */ wUV2 = (k%2) ? 128 + (wY2 >> 1) : wY2 >> 1; wUV1 = 256 - wUV2; if (k > oldwidth - 2) { hControl[i*3+5] = oldwidth - 1; /* point to last byte */ hControl[i*3+1] = 0x00000100; /* use 100% of rightmost Y */ hControl[i*3+3] = 0x00000100; /* use 100% of rightmost V */ } else { hControl[i*3+5] = k; /* pixel offset */ hControl[i*3+1] = wY2 << 16 | wY1; /* luma weights */ /* hControl[i*3+3] = wUV2 << 16 | wUV1; // chroma weights */ /* horiz chroma weights should be same as for even pixel - trbarry 09/16/2002 */ hControl[i*3+3] = hControl[i*3+2]; /* chroma weights */ } } hControl[newwidth*3+4] = 2 * (oldwidth-1); /* give it something to prefetch at end */ hControl[newwidth*3+5] = 2 * (oldwidth-1); /* Next set up vertical table. The offsets are measured in lines and will be mult */ /* by the source pitch later */ for(i=0; i< newheight; ++i) { if (doublecmp(vWarp, 1.0) == 0) /* if no warp factor */ j = i * 256 * (oldheight-1) / (newheight-1); else /* stretch and warp somehow */ j = (int) (256 * WarpFactor(i / (newheight-1.0), vWarp) * (oldheight-1)); if (Interlaced) { /* do hard way? */ if (i%2) { /* is odd output line? */ if (j < 256) { /* before 1st odd input line */ vOffsets[i] = 1; /* all from line 1 */ vWeights[i] = 0; /* weight to give to 2nd line */ } else { k = (((j-256) >> 9) << 1) + 1; /* next lowest odd line */ vOffsets[i] = k; wY2 = j - (k << 8); vWeights[i] = wY2 >> 1; /* weight to give to 2nd line */ } } else { /* is even output line */ k = (j >> 9) << 1; /* next lower even line */ vOffsets[i] = k; wY2 = j - (k << 8); vWeights[i] = wY2 >> 1; /* weight to give to 2nd line */ } } else { /* simple way, do as progressive */ k = j >> 8; vOffsets[i] = k; wY2 = j - (k << 8); vWeights[i] = wY2; /* weight to give to 2nd line */ } } } /* Register allocation */ /* index/counter registers (REGA, REGC) are loaded from 32bit vars/arrays ! */ #define REGEA "eax" #define REGEB "ebx" #if defined(__x86_64__) # define REGA "rax" # define REGB "rbx" # define REGC "ecx" # define REGD "rdx" # define REGDI "rdi" # define REGSI "rsi" #elif defined(__i386__) # define REGA "eax" # define REGB "ebx" # define REGC "ecx" # define REGD "edx" # define REGDI "edi" # define REGSI "esi" #endif /* variables accessed from assembler code */ #define _FPround1 "%0" #define _vWeight1 "%1" #define _vWeight2 "%2" #define _YMask "%3" #define _src_row_size "%4" #define _EndOffset "%5" #define _pControl "%6" #define _row_size "%7" #define _vWorkYW "%8" #define _dstp "%9" #define _vWorkUVW "%10" #define _FPround2 "%11" #define _srcp1 "%12" #define _srcp2 "%13" #if !defined(__x86_64__) #define _oldbx "%14" #define _SSEMMXenabledW "%15" #define _SSE2enabledW "%16" #endif /* Labels */ #define vMaybeSSEMMX "1" #define LessThan8 "2" #define LessThan4 "3" #define AllDone "4" #define LastOne "5" #define vLoopSSE2_Fetch "6" #define vLoopSSE2 "7" #define vLoopSSEMMX_Fetch "8" #define vLoopSSEMMX "9" #define vLoopMMX "10" #define MoreSpareChange "11" #define DoHorizontal "12" #define hLoopMMX "13" #define hLoopMMXSSE "14" /* structure for mmx constants */ typedef union { uint64_t uq[1]; /* Unsigned Quadword */ uint32_t ud[2]; /* Unsigned Doubleword */ } ATTR_ALIGN(16) mmx_t; /* structure for sse2 constants */ typedef union { uint64_t uq[2]; /* Unsigned Quadword */ uint32_t ud[4]; /* Unsigned Doubleword */ } ATTR_ALIGN(16) sse2_t; static int do_warp_yuy2(uint8_t *dst, const uint8_t *src, const int dst_pitch, const int src_pitch, const int dst_width, const int dst_height, const int src_width, const int src_height, const int Interlaced, const uint32_t * const hControl, const uint32_t * const vOffsets, const uint32_t * const vWeights, uint32_t *vWorkY, uint32_t *vWorkUV, int dst_start) { #if defined(__i386__) || defined(__x86_64__) sse2_t YMask = {uq:{UINT64_C(0x00ff00ff00ff00ff),UINT64_C(0x00ff00ff00ff00ff)}}; /* keeps only luma */ sse2_t FPround1 = {uq:{UINT64_C(0x0080008000800080),UINT64_C(0x0080008000800080)}}; /* round words */ sse2_t FPround2 = {uq:{UINT64_C(0x0000008000000080),UINT64_C(0x0000008000000080)}}; /* round dwords */ sse2_t vWeight1; sse2_t vWeight2; const uint32_t *pControl = &hControl[0]; const uint32_t *vWorkYW = vWorkY; const uint32_t *vWorkUVW = vWorkUV; const uint8_t *srcp = src; const uint8_t *srcp1; const uint8_t *srcp2; uint8_t *dstp = dst + dst_pitch*dst_start; const uint32_t src_row_size = src_width * 2; const uint32_t row_size = dst_width * 2; const uint32_t EndOffset = src_row_size / 2; #if !defined(__x86_64__) const int accel = xine_mm_accel(); const uint32_t SSE2enabledW = !!(accel & MM_ACCEL_X86_SSE2); /* in local storage for asm */ const uint32_t SSEMMXenabledW = !!(accel & MM_ACCEL_X86_MMXEXT); /* in local storage for asm */ long int oldbx; #endif int y; for (y = dst_start; y < dst_height; y++) { if((int)vOffsets[y] >= src_height) { /* slice completed */ /*DBG("do_warp_yuy2: max input height reached: need line %d, height %d\n -> Returning next output line: %d\n", vOffsets[y], src_height, y);*/ return y; } vWeight1.ud[0] = vWeight1.ud[1] = vWeight1.ud[2] = vWeight1.ud[3] = (256-vWeights[y]) << 16 | (256-vWeights[y]); vWeight2.ud[0] = vWeight2.ud[1] = vWeight2.ud[2] = vWeight2.ud[3] = vWeights[y] << 16 | vWeights[y]; srcp1 = srcp + vOffsets[y] * src_pitch; if (Interlaced) srcp2 = (y < dst_height-2) ? srcp1 + 2 * src_pitch : srcp1; else srcp2 = (y < dst_height-1) ? srcp1 + src_pitch : srcp1; __asm__ __volatile__ ( #if !defined(__x86_64__) /* store ebx (PIC) */ "mov %%"REGB", "_oldbx" \n\t" #endif "movl "_src_row_size", %%"REGC" \n\t" "shrl $3, %%"REGC" \n\t" /* 8 bytes a time */ "mov "_srcp1", %%"REGSI" \n\t" /* top of 2 src lines to get */ "mov "_srcp2", %%"REGD" \n\t" /* next " */ "mov "_vWorkYW", %%"REGDI" \n\t" /* luma work destination line */ "mov "_vWorkUVW", %%"REGB" \n\t" /* luma work destination line */ "xor %%"REGA", %%"REGA" \n\t" #if !defined(__x86_64__) /* Let's check here to see if we are on a P4 or higher and can use SSE2 instructions. * This first loop is not the performance bottleneck anyway but it is trivial to tune * using SSE2 if we have proper alignment. */ "testl $1, "_SSE2enabledW" \n\t" /* is SSE2 supported?*/ "jz "vMaybeSSEMMX"f \n\t" /* n, can't do anyway*/ #endif "cmpl $2, %%"REGC" \n\t" /* we have at least 16 bytes, 2 qwords? */ "jl "vMaybeSSEMMX"f \n\t" /* n, don't bother*/ "shrl $1, %%"REGC" \n\t" /* do 16 bytes at a time instead*/ "decl %%"REGC" \n" /* jigger loop ct */ ".align 16 \n\t" "movdqu "_FPround1", %%xmm0 \n\t" "movdqu "_vWeight1", %%xmm5 \n\t" "movdqu "_vWeight2", %%xmm6 \n\t" "movdqu "_YMask", %%xmm7 \n" ""vLoopSSE2_Fetch": \n\t" #ifdef PREFETCH " prefetcht0 16(%%"REGSI", %%"REGA", 2) \n\t" " prefetcht0 16(%%"REGD", %%"REGA", 2) \n" #endif ""vLoopSSE2": \n\t" " movdqu (%%"REGSI", %%"REGA", 2), %%xmm1 \n\t" /* top of 2 lines to interpolate */ " movdqu (%%"REGD", %%"REGA", 2), %%xmm2 \n\t" /* 2nd of 2 lines */ " movdqa %%xmm1, %%xmm3 \n\t" /* get chroma bytes */ " pand %%xmm7, %%xmm1 \n\t" /* keep only luma */ " psrlw $8, %%xmm3 \n\t" /* right just chroma */ " pmullw %%xmm5, %%xmm1 \n\t" /* mult by weighting factor */ " pmullw %%xmm5, %%xmm3 \n\t" /* mult by weighting factor */ " movdqa %%xmm2, %%xmm4 \n\t" /* get chroma bytes */ " pand %%xmm7, %%xmm2 \n\t" /* keep only luma */ " psrlw $8, %%xmm4 \n\t" /* right just chroma */ " pmullw %%xmm6, %%xmm2 \n\t" /* mult by weighting factor */ " pmullw %%xmm6, %%xmm4 \n\t" /* mult by weighting factor */ " paddw %%xmm2, %%xmm1 \n\t" /* combine lumas */ " paddusw %%xmm0, %%xmm1 \n\t" /* round */ " psrlw $8, %%xmm1 \n\t" /* right adjust luma */ #ifdef STREAMING_STORE_TMP " movntdq %%xmm1, (%%"REGDI", %%"REGA", 2) \n\t" /* save lumas in our work area */ #else " movdqu %%xmm1, (%%"REGDI", %%"REGA", 2) \n\t" /* save lumas in our work area */ #endif " paddw %%xmm4, %%xmm3 \n\t" /* combine chromas */ " paddusw %%xmm0, %%xmm3 \n\t" /* round */ " psrlw $8, %%xmm3 \n\t" /* right adjust chroma */ " packuswb %%xmm3, %%xmm3 \n\t" /* pack UV's into low dword */ " movdq2q %%xmm3, %%mm1 \n\t" /* save in our work area */ #ifdef STREAMING_STORE_TMP " movntq %%mm1, (%%"REGB", %%"REGA") \n\t" /* save in our work area */ #else " movq %%mm1, (%%"REGB", %%"REGA") \n\t" /* save in our work area */ #endif " lea 8(%%"REGA"), %%"REGA" \n\t" " decl %%"REGC" \n\t" " jg "vLoopSSE2_Fetch"b \n\t" /* if not on last one loop, prefetch */ " jz "vLoopSSE2"b \n\t" /* or just loop, or not */ /* done with our SSE2 fortified loop but we may need to pick up the spare change */ #ifdef STREAMING_STORE_TMP " sfence \n\t" #endif " movl "_src_row_size", %%"REGC" \n\t" /* get count again */ " andl $15, %%"REGC" \n\t" /* just need mod 16 */ " movq "_YMask", %%mm7 \n\t" /* useful luma mask constant - lazy dupl init */ " movq "_vWeight1", %%mm5 \n\t" " movq "_vWeight2", %%mm6 \n\t" " movq "_FPround1", %%mm0 \n\t" /* useful rounding constant */ " shrl $3, %%"REGC" \n\t" /* 8 bytes at a time, any? */ " jz "MoreSpareChange"f \n" /* n, did them all */ /* Let's check here to see if we are on a P2 or Athlon and can use SSEMMX instructions. * This first loop is not the performance bottleneck anyway but it is trivial to tune * using SSE if we have proper alignment. */ ""vMaybeSSEMMX": \n\t" " movq "_YMask", %%mm7 \n\t" /* useful luma mask constant - lazy dupl init */ " movq "_vWeight1", %%mm5 \n\t" " movq "_vWeight2", %%mm6 \n\t" " movq "_FPround1", %%mm0 \n\t" /* useful rounding constant */ #if !defined(__x86_64__) " testl $1, "_SSEMMXenabledW" \n\t" /* MMXEXTsupported? */ " jz "vLoopMMX"f \n\t" /* n, can't do anyway */ #endif " decl %%"REGC" \n" /* jigger loop ctr */ ".align 16 \n" ""vLoopSSEMMX_Fetch": \n\t" #ifdef PREFETCH " prefetcht0 8(%%"REGSI", %%"REGA", 2) \n\t" " prefetcht0 8(%%"REGD", %%"REGA", 2) \n" #endif ""vLoopSSEMMX": \n\t" " movq (%%"REGSI", %%"REGA", 2), %%mm1 \n\t" /* top of 2 lines to interpolate */ " movq (%%"REGD", %%"REGA", 2), %%mm2 \n\t" /* 2nd of 2 lines */ " movq %%mm1, %%mm3 \n\t" /* copy top bytes */ " pand %%mm7, %%mm1 \n\t" /* keep only luma */ " pxor %%mm1, %%mm3 \n\t" /* keep only chroma */ " psrlw $8, %%mm3 \n\t" /* right just chroma */ " pmullw %%mm5, %%mm1 \n\t" /* mult by weighting factor */ " pmullw %%mm5, %%mm3 \n\t" /* mult by weighting factor */ " movq %%mm2, %%mm4 \n\t" /* copy 2nd bytes */ " pand %%mm7, %%mm2 \n\t" /* keep only luma */ " pxor %%mm2, %%mm4 \n\t" /* keep only chroma */ " psrlw $8, %%mm4 \n\t" /* right just chroma */ " pmullw %%mm6, %%mm2 \n\t" /* mult by weighting factor */ " pmullw %%mm6, %%mm4 \n\t" /* mult by weighting factor */ " paddw %%mm2, %%mm1 \n\t" /* combine lumas */ " paddusw %%mm0, %%mm1 \n\t" /* round */ " psrlw $8, %%mm1 \n\t" /* right adjust luma */ #ifdef STREAMING_STORE_TMP " movntq %%mm1, (%%"REGDI", %%"REGA", 2) \n\t" /* save lumas in our work area */ #else " movq %%mm1, (%%"REGDI", %%"REGA", 2) \n\t" /* save lumas in our work area */ #endif " paddw %%mm4, %%mm3 \n\t" /* combine chromas */ " paddusw %%mm0, %%mm3 \n\t" /* round */ " psrlw $8, %%mm3 \n\t" /* right adjust chroma */ " packuswb %%mm3, %%mm3 \n\t" /* pack UV's into low dword */ " movd %%mm3, (%%"REGB", %%"REGA") \n\t" /* save in our work area */ " lea 4(%%"REGA"), %%"REGA" \n\t" " decl %%"REGC" \n\t" " jg "vLoopSSEMMX_Fetch"b \n\t" /* if not on last one loop, prefetch */ " jz "vLoopSSEMMX"b \n\t" /* or just loop, or not */ #ifdef STREAMING_STORE_TMP " sfence \n\t" #endif " jmp "MoreSpareChange"f \n" /* all done with vertical */ ".align 16 \n" ""vLoopMMX": \n\t" " movq (%%"REGSI", %%"REGA", 2), %%mm1 \n\t" /* top of 2 lines to interpolate */ " movq (%%"REGD", %%"REGA", 2), %%mm2 \n\t" /* 2nd of 2 lines */ " movq %%mm1, %%mm3 \n\t" /* copy top bytes */ " pand %%mm7, %%mm1 \n\t" /* keep only luma */ " pxor %%mm1, %%mm3 \n\t" /* keep only chroma */ " psrlw $8, %%mm3 \n\t" /* right just chroma */ " pmullw %%mm5, %%mm1 \n\t" /* mult by weighting factor */ " pmullw %%mm5, %%mm3 \n\t" /* mult by weighting factor */ " movq %%mm2, %%mm4 \n\t" /* copy 2nd bytes */ " pand %%mm7, %%mm2 \n\t" /* keep only luma */ " pxor %%mm2, %%mm4 \n\t" /* keep only chroma */ " psrlw $8, %%mm4 \n\t" /* right just chroma */ " pmullw %%mm6, %%mm2 \n\t" /* mult by weighting factor */ " pmullw %%mm6, %%mm4 \n\t" /* mult by weighting factor */ " paddw %%mm2, %%mm1 \n\t" /* combine lumas */ " paddusw %%mm0, %%mm1 \n\t" /* round */ " psrlw $8, %%mm1 \n\t" /* right adjust luma */ " movq %%mm1, (%%"REGDI", %%"REGA", 2) \n\t" /* save lumas in our work area */ " paddw %%mm4, %%mm3 \n\t" /* combine chromas */ " paddusw %%mm0, %%mm3 \n\t" /* round */ " psrlw $8, %%mm3 \n\t" /* right adjust chroma */ " packuswb %%mm3, %%mm3 \n\t" /* pack UV's into low dword */ " movd %%mm3, (%%"REGB", %%"REGA") \n\t" /* save in our work area */ " lea 4(%%"REGA"), %%"REGA" \n\t" " loop "vLoopMMX"b \n" /* Add a little code here to check if we have 2 more pixels to do and, if so, make one * more pass thru vLoopMMX. We were processing in multiples of 4 pixels and alway have * an even number so there will never be more than 2 left. trbarry 7/29/2002 */ ""MoreSpareChange": \n\t" " cmpl "_EndOffset", %%"REGEA" \n\t" /* did we get them all */ " jnl "DoHorizontal"f \n\t" /* yes, else have 2 left */ " movl $1, %%"REGC" \n\t" /* jigger loop ct */ " sub $2, %%"REGA" \n\t" /* back up 2 pixels (4 bytes, but eax carried as 1/2) */ " jmp "vLoopMMX"b \n" /* We've taken care of the vertical scaling, now do horizontal */ ""DoHorizontal": \n\t" " movq "_YMask", %%mm7 \n\t" /* useful 0U0U.. mask constant */ " movq "_FPround2", %%mm6 \n\t" /* useful rounding constant, dwords */ " mov "_pControl", %%"REGSI" \n\t" /* @ horiz control bytes */ " movl "_row_size", %%"REGC" \n\t" " shrl $2, %%"REGC" \n\t" /* bytes a time, 2 pixels */ " mov "_vWorkYW", %%"REGD" \n\t" /* our luma data, as 0Y0Y 0Y0Y.. */ " mov "_dstp", %%"REGDI" \n\t" /* the destination line */ " mov "_vWorkUVW", %%"REGB" \n" /* chroma data, as UVUV UVUV... */ ".align 16 \n" ""hLoopMMX": \n\t" /* x86_64: must use movl (accessing table of uint32's) */ " movl 16(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 1st pixel pair */ " movd (%%"REGD", %%"REGA", 2), %%mm0 \n\t" /* copy luma pair */ " shr $1, %%"REGA" \n\t" /* div offset by 2 */ " movd (%%"REGB", %%"REGA", 2), %%mm1 \n\t" /* copy UV pair VUVU */ " psllw $8, %%mm1 \n\t" /* shift out V, keep 0000U0U0 */ /* we need to use both even and odd croma from same location - trb 9/2002 */ " punpckldq (%%"REGB", %%"REGA", 2), %%mm1 \r\n" /* copy UV pair VUVU */ " psrlw $8, %%mm1 \r\n" /* shift out U0, keep 0V0V 0U0U */ " movl 20(%%"REGSI"), %%"REGEA" \r\n" /* get data offset in pixels, 2nd pixel pair */ " punpckldq (%%"REGD", %%"REGA", 2), %%mm0 \r\n" /* copy luma pair */ " pmaddwd (%%"REGSI"), %%mm0 \r\n" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm0 \r\n" /* round */ " psrlw $8, %%mm0 \r\n" /* right just 2 luma pixel value 000Y,000Y */ " pmaddwd 8(%%"REGSI"), %%mm1 \r\n" /* mult and sum chromas by ctl weights */ " paddusw %%mm6, %%mm1 \r\n" /* round */ " pslld $8, %%mm1 \r\n" /* shift into low bytes of different words */ " pand %%mm7, %%mm1 \r\n" /* keep only 2 chroma values 0V00,0U00 */ " por %%mm1, %%mm0 \r\n" /* combine luma and chroma, 0V0Y,0U0Y */ " packuswb %%mm0, %%mm0 \r\n" /* pack all into low dword, xxxxVYUY */ " movd %%mm0, (%%"REGDI") \n\t" /* done with 2 pixels */ " lea 24(%%"REGSI"), %%"REGSI" \n\t" /* bump to next control bytest */ " lea 4(%%"REGDI"), %%"REGDI" \n\t" /* bump to next output pixel addr */ " loop "hLoopMMX"b \n\t" /* loop for more */ "emms \n\t" /* done with one line */ #if !defined(__x86_64__) "mov "_oldbx", %%"REGB" \n\t" #endif :: "m" /*0*/(FPround1), "m" /*1*/(vWeight1), "m" /*2*/(vWeight2), "m" /*3*/(YMask), "m" /*4*/(src_row_size), "m" /*5*/(EndOffset), "m" /*6*/(pControl), "m" /*7*/(row_size), "m" /*8*/(vWorkYW), "m" /*9*/(dstp), "m" /*10*/(vWorkUVW), "m" /*11*/(FPround2), "m" /*12*/(srcp1), "m" /*13*/(srcp2) #if !defined(__x86_64__) , "m" /*14*/(oldbx), "m" /*15*/(SSEMMXenabledW), "m" /*16*/(SSE2enabledW) : REGA, /*REGB,*/ REGC, REGD, REGSI, REGDI #else : REGA, REGB, REGC, REGD, REGSI, REGDI #endif ); dstp += dst_pitch; } #endif return 0; } static int do_warp_yv12(uint8_t *dst, const uint8_t * const src, const int dst_pitch, const int src_pitch, const int dst_width, const int dst_height, const int src_width, const int src_height, const int Interlaced, const uint32_t * const hControl, const uint32_t * vOffsets, const uint32_t * vWeights, uint32_t *vWorkY, int dst_start) { #if defined(__i386__) || defined(__x86_64__) sse2_t FPround1 = {uq:{UINT64_C(0x0080008000800080),UINT64_C(0x0080008000800080)}}; /* round words */ sse2_t FPround2 = {uq:{UINT64_C(0x0000008000000080),UINT64_C(0x0000008000000080)}}; /* round dwords */ sse2_t vWeight1; sse2_t vWeight2; const uint32_t *pControl = &hControl[0]; const uint32_t *vWorkYW = vWorkY; const uint8_t *srcp = src; const uint8_t *srcp1; const uint8_t *srcp2; uint8_t *dstp = dst + dst_pitch*dst_start; const uint32_t src_row_size = src_width; const uint32_t row_size = dst_width; #if !defined(__x86_64__) const int accel = xine_mm_accel(); const uint32_t SSE2enabledW = !!(accel & MM_ACCEL_X86_SSE2); /* in local storage for asm */ const uint32_t SSEMMXenabledW = !!(accel & MM_ACCEL_X86_MMXEXT); /* in local storage for asm */ long int oldbx; #endif int y; /* Operation in sliced mode: * - continue until required next source line is out of slice * - return next output line * - at next call, continue from next souce line */ for (y = dst_start; y < dst_height; y++) { if((int)vOffsets[y] >= src_height) { /* slice completed */ /*DBG("do_warp_yv12: max input height reached: need line %d, height %d\n -> Returning next output line: %d , start was %d\n", (int)vOffsets[y], (int)src_height, (int)y, (int)dst_start);*/ return y; } vWeight1.ud[0] = vWeight1.ud[1] = vWeight1.ud[2] = vWeight1.ud[3] = (256-vWeights[y]) << 16 | (256-vWeights[y]); vWeight2.ud[0] = vWeight2.ud[1] = vWeight2.ud[2] = vWeight2.ud[3] = vWeights[y] << 16 | vWeights[y]; srcp1 = srcp + vOffsets[y] * src_pitch; if (Interlaced) srcp2 = (y < dst_height-2) ? srcp1 + 2 * src_pitch : srcp1; else srcp2 = (y < dst_height-1) ? srcp1 + src_pitch : srcp1; __asm__ __volatile__( "movl "_src_row_size", %%"REGC" \n\t" "shr $3, %%"REGC" \n\t" /* 8 bytes a time */ "mov "_srcp1", %%"REGSI" \n\t" /* top of 2 src lines to get */ "mov "_srcp2", %%"REGD" \n\t" /* next " */ "mov "_vWorkYW", %%"REGDI" \n\t" /* luma work destination line */ "xor %%"REGA", %%"REGA" \n\t" #if !defined(__x86_64__) /* Let's check here to see if we are on a P4 or higher and can use SSE2 instructions. * This first loop is not the performance bottleneck anyway but it is trivial to tune * using SSE2 if we have proper alignment. */ "testl $1, "_SSE2enabledW" \n\t" /* is SSE2 supported? */ "jz "vMaybeSSEMMX"f \n\t" /* n, can't do anyway */ #endif "cmpl $2, %%"REGC" \n\t" /* we have at least 16 byts, 2 qwords? */ "jl "vMaybeSSEMMX"f \n\t" /* n, don't bother */ "mov %%"REGSI", %%"REGB" \n\t" "or %%"REGD", %%"REGB" \n\t" "test $15, %%"REGB" \n\t" /* both src rows 16 byte aligned? */ "jnz "vMaybeSSEMMX"f \n\t" /* n, don't use sse2 */ "shr $1, %%"REGC" \n\t" /* do 16 bytes at a time instead */ "dec %%"REGC" \n\t" /* jigger loop ct */ "movdqu "_FPround1", %%xmm0 \n\t" "movdqu "_vWeight1", %%xmm5 \n\t" "movdqu "_vWeight2", %%xmm6 \n\t" "pxor %%xmm7, %%xmm7 \n" ".align 16 \n" ""vLoopSSE2_Fetch": \n\t" #ifdef PREFETCH " prefetcht0 16(%%"REGSI", %%"REGA", 2) \n\t" " prefetcht0 16(%%"REGD", %%"REGA", 2) \n" #endif ""vLoopSSE2": \n\t" /* we're already checked pointers to be on dqword aligned */ " movdqa (%%"REGSI", %%"REGA"), %%xmm1 \n\t" /* top of 2 lines to interpolate */ " movdqa (%%"REGD", %%"REGA"), %%xmm3 \n\t" /* 2nd of 2 lines */ " movdqa %%xmm1, %%xmm2 \n\t" " movdqa %%xmm3, %%xmm4 \n\t" " punpcklbw %%xmm7, %%xmm1 \n\t" /* make words */ " punpckhbw %%xmm7, %%xmm2 \n\t" /* " */ " punpcklbw %%xmm7, %%xmm3 \n\t" /* " */ " punpckhbw %%xmm7, %%xmm4 \n\t" /* " */ " pmullw %%xmm5, %%xmm1 \n\t" /* mult by top weighting factor */ " pmullw %%xmm5, %%xmm2 \n\t" /* " */ " pmullw %%xmm6, %%xmm3 \n\t" /* mult by bot weighting factor */ " pmullw %%xmm6, %%xmm4 \n\t" /* " */ " paddw %%xmm3, %%xmm1 \n\t" /* combine lumas low */ " paddw %%xmm4, %%xmm2 \n\t" /* combine lumas high */ " paddusw %%xmm0, %%xmm1 \n\t" /* round */ " paddusw %%xmm0, %%xmm2 \n\t" /* round */ " psrlw $8, %%xmm1 \n\t" /* right adjust luma */ " psrlw $8, %%xmm2 \n\t" /* right adjust luma */ " packuswb %%xmm2, %%xmm1 \n\t" /* pack words to our 16 byte answer */ #ifdef STREAMING_STORE_TMP " movntdq %%xmm1, (%%"REGDI", %%"REGA") \n\t" /* save lumas in our work area */ #else " movdqu %%xmm1, (%%"REGDI", %%"REGA") \n\t" /* save lumas in our work area */ #endif " lea 16(%%"REGA"), %%"REGA" \n\t" " decl %%"REGC" \n\t" " jg "vLoopSSE2_Fetch"b \n\t" /* if not on last one loop, prefetch */ " jz "vLoopSSE2"b \n\t" /* or just loop, or not */ /* done with our SSE2 fortified loop but we may need to pick up the spare change */ #ifdef STREAMING_STORE_TMP " sfence \n\t" #endif " movl "_src_row_size", %%"REGC" \n\t" /* get count again */ " andl $15, %%"REGC" \n\t" /* just need mod 16 */ " movq "_vWeight1", %%mm5 \n\t" " movq "_vWeight2", %%mm6 \n\t" " movq "_FPround1", %%mm0 \n\t" /* useful rounding constant */ " shrl $3, %%"REGC" \n\t" /* 8 bytes at a time, any? */ " jz "MoreSpareChange"f \n" /* n, did them all */ /* Let's check here to see if we are on a P2 or Athlon and can use SSEMMX instructions. * This first loop is not the performance bottleneck anyway but it is trivial to tune * using SSE if we have proper alignment. */ ""vMaybeSSEMMX": \n\t" " movq "_vWeight1", %%mm5 \n\t" " movq "_vWeight2", %%mm6 \n\t" " movq "_FPround1", %%mm0 \n\t" /* useful rounding constant */ " pxor %%mm7, %%mm7 \n\t" #if !defined(__x86_64__) " testl $1, "_SSEMMXenabledW" \n\t"/* MMXEXTsupported? */ " jz "vLoopMMX"f \n\t" /* n, can't do anyway */ #endif " decl %%"REGC" \n" /* jigger loop ctr */ ".align 16 \n" ""vLoopSSEMMX_Fetch": \n\t" #ifdef PREFETCH " prefetcht0 8(%%"REGSI", %%"REGA") \n\t" " prefetcht0 8(%%"REGD", %%"REGA") \n" #endif ""vLoopSSEMMX": \n\t" " movq (%%"REGSI", %%"REGA"), %%mm1 \n\t" /* top of 2 lines to interpolate */ " movq (%%"REGD", %%"REGA"), %%mm3 \n\t" /* 2nd of 2 lines */ " movq %%mm1, %%mm2 \n\t" " movq %%mm3, %%mm4 \n\t" " punpcklbw %%mm7, %%mm1 \n\t" /* make words */ " punpckhbw %%mm7, %%mm2 \n\t" /* " */ " punpcklbw %%mm7, %%mm3 \n\t" /* " */ " punpckhbw %%mm7, %%mm4 \n\t" /* " */ " pmullw %%mm5, %%mm1 \n\t" /* mult by top weighting factor */ " pmullw %%mm5, %%mm2 \n\t" /* " */ " pmullw %%mm6, %%mm3 \n\t" /* mult by bot weighting factor */ " pmullw %%mm6, %%mm4 \n\t" /* " */ " paddw %%mm3, %%mm1 \n\t" /* combine lumas low */ " paddw %%mm4, %%mm2 \n\t" /* combine lumas high */ " paddusw %%mm0, %%mm1 \n\t" /* round */ " paddusw %%mm0, %%mm2 \n\t" /* round */ " psrlw $8, %%mm1 \n\t" /* right adjust luma */ " psrlw $8, %%mm2 \n\t" /* right adjust luma */ " packuswb %%mm2, %%mm1 \n\t" /* pack words to our 8 byte answer */ #ifdef STREAMING_STORE_TMP " movntq %%mm1, (%%"REGDI", %%"REGA") \n\t" /* save lumas in our work area */ #else " movq %%mm1, (%%"REGDI", %%"REGA") \n\t" /* save lumas in our work area */ #endif " lea 8(%%"REGA"), %%"REGA" \n\t" " decl %%"REGC" \n\t" " jg "vLoopSSEMMX_Fetch"b \n\t" /* if not on last one loop, prefetch */ " jz "vLoopSSEMMX"b \n\t" /* or just loop, or not */ #ifdef STREAMING_STORE_TMP " sfence \n\t" #endif " jmp "MoreSpareChange"f \n" /* all done with vertical */ ".align 16 \n" ""vLoopMMX": \n\t" " movq (%%"REGSI", %%"REGA"), %%mm1 \n\t" /* top of 2 lines to interpolate */ " movq (%%"REGD", %%"REGA"), %%mm3 \n\t" /* 2nd of 2 lines */ " movq %%mm1, %%mm2 \n\t" " movq %%mm3, %%mm4 \n\t" " punpcklbw %%mm7, %%mm1 \n\t" /* make words */ " punpckhbw %%mm7, %%mm2 \n\t" /* " */ " punpcklbw %%mm7, %%mm3 \n\t" /* " */ " punpckhbw %%mm7, %%mm4 \n\t" /* " */ " pmullw %%mm5, %%mm1 \n\t" /* mult by top weighting factor */ " pmullw %%mm5, %%mm2 \n\t" /* " */ " pmullw %%mm6, %%mm3 \n\t" /* mult by bot weighting factor */ " pmullw %%mm6, %%mm4 \n\t" /* " */ " paddw %%mm3, %%mm1 \n\t" /* combine lumas low */ " paddw %%mm4, %%mm2 \n\t" /* combine lumas high */ " paddusw %%mm0, %%mm1 \n\t" /* round */ " paddusw %%mm0, %%mm2 \n\t" /* round */ " psrlw $8, %%mm1 \n\t" /* right adjust luma */ " psrlw $8, %%mm2 \n\t" /* right adjust luma */ " packuswb %%mm2, %%mm1 \n\t" /* pack words to our 8 byte answer */ " movq %%mm1, (%%"REGDI", %%"REGA") \n\t" /* save lumas in our work area */ " lea 8(%%"REGA"), %%"REGA" \n\t" " loop "vLoopMMX"b \n" /* Add a little code here to check if we have more pixels to do and, if so, make one * more pass thru vLoopMMX. We were processing in multiples of 8 pixels and alway have * an even number so there will never be more than 7 left. */ ""MoreSpareChange": \n\t" " cmpl "_src_row_size", %%"REGEA" \n\t" /* did we get them all */ " jnl "DoHorizontal"f \n\t" /* yes, else have 2 left */ " movl $1, %%"REGC" \n\t" /* jigger loop ct */ " movl "_src_row_size", %%"REGEA" \n\t" " sub $8, %%"REGA" \n\t" /* back up to last 8 pixels */ " jmp "vLoopMMX"b \n" /* We've taken care of the vertical scaling, now do horizontal */ ""DoHorizontal": \n\t" " pxor %%mm7, %%mm7 \n\t" " movq "_FPround2", %%mm6 \n\t" /* useful rounding constant, dwords */ " mov "_pControl", %%"REGSI" \n\t" /* @ horiz control bytes */ " movl "_row_size", %%"REGC" \n\t" " shrl $2, %%"REGC" \n\t" /* 4 bytes a time, 4 pixels */ " mov "_vWorkYW", %%"REGD" \n\t" /* our luma data, as 0Y0Y 0Y0Y.. */ " mov "_dstp", %%"REGDI" \n\t" /* the destination line */ #if !defined(__x86_64__) " testl $1, "_SSEMMXenabledW" \n\t" /* MMXEXTsupported? */ " jz "hLoopMMX"f \n\t" /* n, can't do anyway */ #endif /* With SSE support we will make 8 pixels (from 8 pairs) at a time */ " shrl $1, %%"REGC" \n\t" /* 8 bytes a time instead of 4 */ " jz "LessThan8"f \n" ".align 16 \n" ""hLoopMMXSSE": \n\t" /* handle first 2 pixels */ /* phi: must use movl here (x86_64, reading from table of uint_32's) */ " movl 16(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 1st pixel pair */ " movl 20(%%"REGSI"), %%"REGEB" \r\n" /* get data offset in pixels, 2nd pixel pair */ " movd (%%"REGD", %%"REGA"), %%mm0 \n\t" /* copy luma pair 0000xxYY */ " punpcklwd (%%"REGD", %%"REGB"), %%mm0 \r\n" /* 2nd luma pair, now xxxxYYYY */ " punpcklbw %%mm7, %%mm0 \n\t" /* make words out of bytes, 0Y0Y0Y0Y */ " movl 16+24(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 3st pixel pair */ " movl 20+24(%%"REGSI"), %%"REGEB" \r\n" /* get data offset in pixels, 4nd pixel pair */ " pmaddwd (%%"REGSI"), %%mm0 \n\t" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm0 \n\t" /* round */ " psrlw $8, %%mm0 \n\t" /* right just 4 luma pixel value 0Y0Y0Y0Y */ /* handle 3rd and 4th pixel pairs */ " movd (%%"REGD", %%"REGA"), %%mm1 \n\t" /* copy luma pair 0000xxYY */ " punpcklwd (%%"REGD", %%"REGB"), %%mm1 \r\n" /* 2nd luma pair, now xxxxYYYY */ " punpcklbw %%mm7, %%mm1 \n\t" /* make words out of bytes, 0Y0Y0Y0Y */ " movl 16+48(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 5st pixel pair */ " movl 20+48(%%"REGSI"), %%"REGEB" \r\n" /* get data offset in pixels, 6nd pixel pair */ " pmaddwd 24(%%"REGSI"), %%mm1 \n\t" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm1 \n\t" /* round */ " psrlw $8, %%mm1 \n\t" /* right just 4 luma pixel value 0Y0Y0Y0Y */ /* handle 5th and 6th pixel pairs */ " movd (%%"REGD", %%"REGA"), %%mm2 \n\t" /* copy luma pair 0000xxYY */ " punpcklwd (%%"REGD", %%"REGB"), %%mm2 \r\n" /* 2nd luma pair, now xxxxYYYY */ " punpcklbw %%mm7, %%mm2 \n\t" /* make words out of bytes, 0Y0Y0Y0Y */ " movl 16+72(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 7st pixel pair */ " movl 20+72(%%"REGSI"), %%"REGEB" \r\n" /* get data offset in pixels, 8nd pixel pair */ " pmaddwd 48(%%"REGSI"), %%mm2 \n\t" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm2 \n\t" /* round */ " psrlw $8, %%mm2 \n\t" /* right just 4 luma pixel value 0Y0Y0Y0Y */ /* handle 7th and 8th pixel pairs */ " movd (%%"REGD", %%"REGA"), %%mm3 \n\t" /* copy luma pair 0000xxYY */ " punpcklwd (%%"REGD", %%"REGB"), %%mm3 \r\n" /* 2nd luma pair, now xxxxYYYY */ " punpcklbw %%mm7, %%mm3 \n\t" /* make words out of bytes, 0Y0Y0Y0Y */ " pmaddwd 72(%%"REGSI"), %%mm3 \n\t" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm3 \n\t" /* round */ " psrlw $8, %%mm3 \n\t" /* right just 4 luma pixel value 0Y0Y0Y0Y */ /* combine, store, and loop */ " packuswb %%mm1, %%mm0 \n\t" /* pack into qword, 0Y0Y0Y0Y */ " packuswb %%mm3, %%mm2 \n\t" /* pack into qword, 0Y0Y0Y0Y */ " packuswb %%mm2, %%mm0 \n\t" /* and again into YYYYYYYY */ #ifdef STREAMING_STORE " movntq %%mm0, (%%"REGDI") \n\t" /* done with 4 pixels */ #else " movq %%mm0, (%%"REGDI") \n\t" /* done with 4 pixels */ #endif " lea 96(%%"REGSI"), %%"REGSI" \n\t" " lea 8(%%"REGDI"), %%"REGDI" \n\t" " decl %%"REGC" \n\t" " jg "hLoopMMXSSE"b \n\t" /* loop for more */ #ifdef STREAMING_STORE " sfence \n" #endif ""LessThan8": \n\t" " movl "_row_size", %%"REGC" \n\t" " andl $7, %%"REGC" \n\t" /* we have done all but maybe this */ " shrl $2, %%"REGC" \n\t" /* now do only 4 bytes at a time */ " jz "LessThan4"f \n" ".align 16 \n" ""hLoopMMX": \n\t" /* handle first 2 pixels */ " movl 16(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 1st pixel pair */ " movl 20(%%"REGSI"), %%"REGEB" \r\n" /* get data offset in pixels, 2nd pixel pair */ " movd (%%"REGD", %%"REGA"), %%mm0 \n\t" /* copy luma pair 0000xxYY */ " punpcklwd (%%"REGD", %%"REGB"), %%mm0 \r\n" /* 2nd luma pair, now xxxxYYYY */ " punpcklbw %%mm7, %%mm0 \n\t" /* make words out of bytes, 0Y0Y0Y0Y */ " movl 16+24(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 3st pixel pair */ " movl 20+24(%%"REGSI"), %%"REGEB" \r\n" /* get data offset in pixels, 4nd pixel pair */ " pmaddwd (%%"REGSI"), %%mm0 \n\t" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm0 \n\t" /* round */ " psrlw $8, %%mm0 \n\t" /* right just 4 luma pixel value 0Y0Y0Y0Y */ /* handle 3rd and 4th pixel pairs */ " movd (%%"REGD", %%"REGA"), %%mm1 \n\t" /* copy luma pair 0000xxYY */ " punpckldq (%%"REGD", %%"REGB"), %%mm1 \r\n" /* 2nd luma pair, now xxxxYYYY */ " punpcklbw %%mm7, %%mm1 \n\t" /* make words out of bytes, 0Y0Y0Y0Y */ " pmaddwd 24(%%"REGSI"), %%mm1 \n\t" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm1 \n\t" /* round */ " psrlw $8, %%mm1 \n\t" /* right just 4 luma pixel value 0Y0Y0Y0Y */ /* combine, store, and loop */ " packuswb %%mm1, %%mm0 \n\t" /* pack into qword, 0Y0Y0Y0Y */ " packuswb %%mm7, %%mm0 \n\t" /* and again into 0000YYYY */ " movd %%mm0, (%%"REGDI") \n\t" /* done with 4 pixels */ " lea 48(%%"REGSI"), %%"REGSI" \n\t" " lea 4(%%"REGDI"), %%"REGDI" \n\t" " loop "hLoopMMX"b \n" /* loop for more */ /* test to see if we have a mod 4 size row, if not then more spare change */ ""LessThan4": \n\t" " movl "_row_size", %%"REGC" \n\t" " andl $3, %%"REGC" \n\t" /* remainder side mod 4 */ " cmpl $2, %%"REGC" \n\t" " jl "LastOne"f \n\t" /* none, none */ /* handle 2 more pixels */ " movl 16(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 1st pixel pair */ " movl 20(%%"REGSI"), %%"REGEB" \r\n" /* get data offset in pixels, 2nd pixel pair */ " movd (%%"REGD", %%"REGA"), %%mm0 \n\t" /* copy luma pair 0000xxYY */ " punpcklwd (%%"REGD", %%"REGB"), %%mm0 \r\n" /* 2nd luma pair, now xxxxYYYY */ " punpcklbw %%mm7, %%mm0 \n\t" /* make words out of bytes, 0Y0Y0Y0Y */ " pmaddwd (%%"REGSI"), %%mm0 \n\t" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm0 \n\t" /* round */ " psrlw $8, %%mm0 \n\t" /* right just 4 luma pixel value 0Y0Y0Y0Y */ " packuswb %%mm7, %%mm0 \n\t" /* pack into qword, 00000Y0Y */ " packuswb %%mm7, %%mm0 \n\t" /* and again into 000000YY */ " movd %%mm0, (%%"REGDI") \n\t" /* store, we are guarrenteed room in buffer (8 byte mult) */ " subl $2, %%"REGC" \n\t" " lea 24(%%"REGSI"), %%"REGSI" \n\t" /* bump to next control bytes */ " lea 2(%%"REGDI"), %%"REGDI" \n" /* bump to next output pixel addr */ /* maybe one last pixel */ ""LastOne": \n\t" " cmpl $0, %%"REGC" \r\n" /* still more ? */ " jz "AllDone"f \r\n" /* n, done */ " movl 16(%%"REGSI"), %%"REGEA" \n\t" /* get data offset in pixels, 1st pixel pair */ " movd (%%"REGD", %%"REGA"), %%mm0 \n\t" /* copy luma pair 0000xxYY */ " punpcklbw %%mm7, %%mm0 \n\t" /* make words out of bytes, 0Y0Y0Y0Y */ " pmaddwd (%%"REGSI"), %%mm0 \n\t" /* mult and sum lumas by ctl weights */ " paddusw %%mm6, %%mm0 \n\t" /* round */ " psrlw $8, %%mm0 \n\t" /* right just 4 luma pixel value 0Y0Y0Y0Y */ " movd %%mm0, %%"REGEA" \n\t" " movb %%al, (%%"REGDI") \n" /* store last one */ ""AllDone": \n\t" " emms \n\t" #if !defined(__x86_64__) "mov "_oldbx", %%"REGB" \n\t" #endif :: "m" /*0*/(FPround1), "m" /*1*/(vWeight1), "m" /*2*/(vWeight2), "m" /*3*/(y/*YMask[0]*/), "m" /*4*/(src_row_size), "m" /*5*/(y/*EndOffset*/), "m" /*6*/(pControl), "m" /*7*/(row_size), "m" /*8*/(vWorkYW), "m" /*9*/(dstp), "m" /*10*/(y/*vWorkUVW*/), "m" /*11*/(FPround2), "m" /*12*/(srcp1), "m" /*13*/(srcp2) #if !defined(__x86_64__) , "m" /*14*/(oldbx), "m" /*15*/(SSEMMXenabledW), "m" /*16*/(SSE2enabledW) : REGA, /*REGB,*/ REGC, REGD, REGSI, REGDI #else : REGA, REGB, REGC, REGD, REGSI, REGDI #endif ); dstp += dst_pitch; } #endif return 0; } /* * xine plugin */ #define PLUGIN_ID "warp" #define PLUGIN_DESCR "(non-)linear software scaling post plugin"; #define PLUGIN_T warp_plugin_t /*#define POST_THREADS*/ /*#define POST_SLICES*/ #include "xine/post_util.h" /* plugin instance functions */ static void warp_dispose(post_plugin_t *this_gen); /* parameter functions */ static xine_post_api_descr_t *warp_get_param_descr(void); static int warp_set_parameters(xine_post_t *this_gen, void *param_gen); static int warp_get_parameters(xine_post_t *this_gen, void *param_gen); static char *warp_get_help(void); typedef struct warp_parameters_s { int output_width; int output_height; double output_aspect; int no_downscaling; int debug; } warp_parameters_t; START_PARAM_DESCR(warp_parameters_t) PARAM_ITEM(POST_PARAM_TYPE_INT, output_width, NULL, 640, 1920, 0, "output video width") PARAM_ITEM(POST_PARAM_TYPE_INT, output_height, NULL, 480, 1080, 0, "output video height") PARAM_ITEM(POST_PARAM_TYPE_DOUBLE, output_aspect, NULL, 1, 3, 0, "output video aspect ratio") PARAM_ITEM(POST_PARAM_TYPE_BOOL, no_downscaling,NULL, 0, 1, 0, "disable downscaling") PARAM_ITEM(POST_PARAM_TYPE_BOOL, debug, NULL, 0, 1, 0, "debug mode") END_PARAM_DESCR(warp_param_descr) typedef struct { post_plugin_t post; xine_post_in_t parameter_input; /* User config (changes to actual config are delayed) */ warp_parameters_t config; /* Current config */ int enable; int output_width; int output_height; double output_aspect; double factor_x; double factor_y; /* Last seen input frame */ int input_width; int input_height; int input_format; int input_interlaced; double input_aspect; /* working buffers */ uint32_t *vWorkY; uint32_t *vWorkUV; /* scaling tables */ uint32_t *hControl; uint32_t *hControlUV; uint32_t *vOffsets; uint32_t *vOffsetsUV; uint32_t *vWeights; uint32_t *vWeightsUV; /* memory for work areas and scaling tables */ void *pMem; } warp_plugin_t; /* * */ static void init_tables(warp_plugin_t *this) { #define BP(x) ((uint8_t*)(x)) /* allocate memory for scaling tables and workspace */ free(this->pMem); this->pMem = malloc(this->input_width*3 + this->output_width*sizeof(uint32_t)*3*2 + this->output_height*sizeof(uint32_t)*4 + 2*9*128); /* - aligned for P4 cache line */ this->vWorkY = (uint32_t*)ALIGN(128, this->pMem); this->vWorkUV = (uint32_t*)ALIGN(128, BP(this->vWorkY) + this->input_width*2 + 128); this->hControl = (uint32_t*)ALIGN(128, BP(this->vWorkUV) + this->input_width + 128); this->vOffsets = (uint32_t*)ALIGN(128, BP(this->hControl) + this->output_width * sizeof(uint32_t) * 3 + 128); this->vWeights = (uint32_t*)ALIGN(128, BP(this->vOffsets) + this->output_height * sizeof(uint32_t) + 128); if (this->input_format == XINE_IMGFMT_YV12) { this->vOffsetsUV = (uint32_t*)ALIGN(128, BP(this->vWeights) + this->output_height * sizeof(uint32_t) + 128); this->vWeightsUV = (uint32_t*)ALIGN(128, BP(this->vOffsetsUV) + this->output_height * sizeof(uint32_t) + 128); this->hControlUV = (uint32_t*)ALIGN(128, BP(this->vWeightsUV) + this->output_height * sizeof(uint32_t) + 128); DBG("init_yv12: %dx%d->%dx%d hWarp %1.3lf vWarp %1.3lf\n", this->input_width, this->input_height, this->output_width, this->output_height, this->factor_x, this->factor_y); init_tables_yv12(this->output_width, this->output_height, this->input_width, this->input_height, this->input_interlaced, this->factor_x, this->factor_y, this->hControl, this->vOffsets, this->vWeights, this->hControlUV, this->vOffsetsUV, this->vWeightsUV ); } else if (this->input_format == XINE_IMGFMT_YUY2) { DBG("init_yuy2: %dx%d->%dx%d hWarp %1.3lf vWarp %1.3lf\n", this->input_width, this->input_height, this->output_width, this->output_height, this->factor_x, this->factor_y); init_tables_yuy2(this->output_width, this->output_height, this->input_width, this->input_height, this->input_interlaced, this->factor_x, this->factor_y, this->hControl, this->vOffsets, this->vWeights ); } } static void calculate_factors(warp_plugin_t *this) { /* try to guess amount to stretch/shrink */ double adiff = this->input_aspect - this->output_aspect; this->factor_x = 1.0; this->factor_y = 1.0; if (adiff > 0.1) { if (adiff > 0.1 + ((16.0-12.0)/9.0)) { /* >16:9 -> >4:3 */ DBG("aspect ratio diff %1.3lf > 0 : too large !\n", adiff); this->factor_x = 0.95; this->factor_y = 1.15; this->output_aspect += (adiff - 4.0/9.0); DBG(" changing target ratio to %3.1lf\n", this->output_aspect); } else { /* 16:9 ... 12:9 -> 4:3 */ DBG("aspect ratio diff %1.3lf > 0 : 16.9...12:9 -> 4:3\n", adiff); this->factor_x = 1.0 - 0.05 * adiff * 9.0/4.0; this->factor_y = 1.0 + 0.15 * adiff * 9.0/4.0; } } else if (adiff < -0.1) { if(adiff < -0.1-((16.0-12.0)/9.0)) { /* <4:3 -> <16:9 */ DBG("aspect ratio diff %1.3lf > 0 : too large !\n", adiff); this->factor_x = 1.05; this->factor_y = 0.85; this->output_aspect += (adiff + 4.0/9.0); DBG(" changing target ratio to %3.1lf\n", this->output_aspect); } else { /* 4:3...16:9 -> 16:9 */ DBG("aspect ratio diff %1.3lf < 0 : 4:3...16:9 -> 16:9\n", adiff); this->factor_x = 1.0 + 0.05 * adiff * 9.0/4.0; this->factor_y = 1.0 - 0.15 * adiff * 9.0/4.0; } } else { DBG("aspect ratio matches, no warp\n"); this->factor_x = 1.0; this->factor_y = 1.0; } DBG("factor_x = %1.3lf factor_y = %1.3lf output ratio = %1.3lf\n", this->factor_x, this->factor_y, this->output_aspect); } /* * */ void *warp_init_plugin(xine_t *xine, void *data) { #if !defined(__x86_64__) /* Need at least MMX */ if (!(xine_mm_accel() & MM_ACCEL_X86_MMX)) { fprintf(stderr, "warp_init_plugin: ERROR: at least MMX required\n"); return NULL; } #endif return init_plugin(xine, data); } static post_plugin_t *open_plugin(post_class_t *class_gen, int inputs, xine_audio_port_t **audio_target, xine_video_port_t **video_target) { warp_plugin_t *this = calloc(1, sizeof(warp_plugin_t)); post_plugin_t *this_gen = (post_plugin_t *) this; post_in_t *input; post_out_t *output; xine_post_in_t *input_param; post_video_port_t *port; static xine_post_api_t post_api = { warp_set_parameters, warp_get_parameters, warp_get_param_descr, warp_get_help }; if (!this || !video_target || !video_target[0]) { free(this); return NULL; } _x_post_init(this_gen, 0, 1); port = _x_post_intercept_video_port(this_gen, video_target[0], &input, &output); port->intercept_frame = intercept_frame_yuy; port->new_frame->draw = post_draw; input->xine_in.name = "video"; output->xine_out.name = "video (scaled)"; this_gen->xine_post.video_input[0] = &port->new_port; this_gen->dispose = warp_dispose; input_param = &this->parameter_input; input_param->name = "parameters"; input_param->type = XINE_POST_DATA_PARAMETERS; input_param->data = &post_api; xine_list_push_back(this_gen->input, input_param); this->config.output_aspect = 0.0; /* -> do not change aspect ratio */ this->config.output_width = 0; /* -> do not change width */ this->config.output_height = 0; /* -> do not change height */ this->config.no_downscaling = 0; this->input_width = 0; /* not known yet, triggers initialization later */ this->input_height = 0; return this_gen; } static void warp_dispose(post_plugin_t *this_gen) { if (_x_post_dispose(this_gen)) { warp_plugin_t *this = (warp_plugin_t *) this_gen; DBG("dispose\n"); free(this->pMem); free(this); } } static vo_frame_t *got_frame(vo_frame_t *frame) { post_video_port_t *port = (post_video_port_t *)frame->port; warp_plugin_t *this = (warp_plugin_t *)port->post; double adiff = this->input_aspect - frame->ratio; if (this->input_width != frame->width || this->input_height != frame->height || this->input_format != frame->format || FABS(adiff)>0.1 || this->input_interlaced != !!(frame->flags & VO_INTERLACED_FLAG)) { DBG("detected frame format change: %dx%d -> %dx%d, interlaced %d->%d, aspect %1.3lf->%1.3lf, %s->%s\n", this->input_width, this->input_height, frame->width, frame->height, this->input_interlaced, !!(frame->flags & VO_INTERLACED_FLAG), this->input_aspect, frame->ratio, this->input_format==XINE_IMGFMT_YV12 ? "yv12":"yuy2", frame->format==XINE_IMGFMT_YV12 ? "yv12":"yuy2" ); /* free tables and buffers */ free(this->pMem); this->pMem = NULL; /* remember frame properties to detect changes in video format */ this->input_width = frame->width; this->input_height = frame->height; this->input_format = frame->format; this->input_aspect = frame->ratio; this->input_interlaced = !!(frame->flags & VO_INTERLACED_FLAG); /* re-configure target size and aspect ratio */ if (doublecmp(this->config.output_aspect, 0.0) == 0) this->output_aspect = frame->ratio; else this->output_aspect = this->config.output_aspect; if (!this->config.no_downscaling) { this->output_width = this->config.output_width ?: frame->width; this->output_height = this->config.output_height ?: frame->height; } else { this->output_width = MAX(this->config.output_width, frame->width); this->output_height = MAX(this->config.output_height, frame->height); } /* calculate warp function factors */ calculate_factors(this); adiff = this->input_aspect - this->output_aspect; if(this->output_width == frame->width && this->output_height == frame->height && adiff < 0.1 && adiff > -0.1 ) { this->enable = 0; DBG("--> nothing to do, disabling processing for now\n"); return NULL; } this->enable = 1; init_tables(this); } if (!this->enable) return NULL; return port->original_port->get_frame(port->original_port, this->output_width, this->output_height, this->output_aspect, frame->format, frame->flags | VO_BOTH_FIELDS); } static void draw_internal(vo_frame_t *frame, vo_frame_t *new_frame) { post_video_port_t *port = (post_video_port_t *)frame->port; warp_plugin_t *this = (warp_plugin_t *)port->post; int proc_height = frame->height; if (frame->format == XINE_IMGFMT_YV12) { do_warp_yv12(new_frame->base[0], frame->base[0], new_frame->pitches[0], frame->pitches[0], this->output_width, this->output_height, frame->width, proc_height, this->input_interlaced, this->hControl, this->vOffsets, this->vWeights, this->vWorkY, 0); proc_height /= 2; do_warp_yv12(new_frame->base[1], frame->base[1], new_frame->pitches[1], frame->pitches[1], this->output_width/2, this->output_height/2, frame->width/2, proc_height, this->input_interlaced, this->hControlUV, this->vOffsetsUV, this->vWeightsUV, this->vWorkUV, 0); do_warp_yv12(new_frame->base[2], frame->base[2], new_frame->pitches[2], frame->pitches[2], this->output_width/2, this->output_height/2, frame->width/2, proc_height, this->input_interlaced, this->hControlUV, this->vOffsetsUV, this->vWeightsUV, this->vWorkUV, 0); } else if (frame->format == XINE_IMGFMT_YUY2) { do_warp_yuy2(new_frame->base[0], frame->base[0], new_frame->pitches[0], frame->pitches[0], this->output_width, this->output_height, frame->width, proc_height, this->input_interlaced, this->hControl, this->vOffsets, this->vWeights, this->vWorkY, this->vWorkUV, 0); } } /* * parameter functions */ static xine_post_api_descr_t *warp_get_param_descr(void) { return &warp_param_descr; } static int warp_set_parameters(xine_post_t *this_gen, void *param_gen) { warp_plugin_t *this = (warp_plugin_t *)this_gen; warp_parameters_t *params = (warp_parameters_t *)param_gen; memcpy(&this->config, params, sizeof(warp_parameters_t)); this->input_width = this->input_height = 0; if(this->config.output_aspect > 999) this->config.output_aspect /= 1000.0; DBG("warp_set_parameters: " "output_width=%d, output_height=%d, output_aspect=%4.3lf, no_downscaling=%d, debug=%d\n", this->config.output_width, this->config.output_height, this->config.output_aspect, this->config.no_downscaling, this->config.debug); return 1; } static int warp_get_parameters(xine_post_t *this_gen, void *param_gen) { warp_plugin_t *this = (warp_plugin_t *)this_gen; warp_parameters_t *params = (warp_parameters_t *)param_gen; DBG("warp_get_parameters\n"); memcpy(params, &this->config, sizeof(warp_parameters_t)); return 1; } static char *warp_get_help(void) { return _( "The warp plugin scales video to another resolution. " "It supports non-linear stretching to change video aspect ratio. " "\n" "Parameters\n" " output_width: Scale video to width\n" " (0 -> do not change video width)\n" " output_height: Scale video to height\n" " (0 -> do not change video height)\n" " output_aspect: Adjust aspect ratio using non-linear scaling\n" " (0 -> do not change video aspect ratio)\n" " no_downscaling: Do not downscale video\n" " debug: Output debug messages\n" "\n" ); } /* * plugin info */ static post_info_t info = { XINE_POST_TYPE_VIDEO_FILTER }; const plugin_info_t xine_plugin_info[] __attribute__((visibility("default"))) = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_POST, POST_PLUGIN_IFACE_VERSION, "warp", XINE_VERSION_CODE, &info, &warp_init_plugin }, { PLUGIN_POST, POST_PLUGIN_IFACE_VERSION, "swscale", XINE_VERSION_CODE, &info, &warp_init_plugin }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } }; xineliboutput-2.0.0/xine_post_autocrop.c0000644000175000017500000017514213061253352016326 0ustar phph/* * xine_post_autocrop.c: xine post plugin * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ /* * Copyright (C) 2006 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-1301 USA * * * autocrop video filter by Petri Hintukainen 25/03/2006 * * Automatically crop 4:3 letterbox frames to 16:9 * * based on expand.c * * * TODO: * - more reliable border detection, including channel logo detection * - OSD re-positioning (?) * */ #include #include #include #ifndef FABS # define FABS(x) ((x) < 0.0 ? -(x) : (x)) #endif /* * Configuration */ /*#define MARK_FRAME Draw markers on detected boundaries */ /*#define ENABLE_64BIT 1 Force using of 64-bit routines */ /*#undef __MMX__ Disable MMX */ /*#undef __SSE__ Disable SSE */ /*#define FILTER2 Tighter Y-filter */ #undef LOG_MODULE #define LOG_MODULE "autocrop" #define LOG_INFO (this->debug_level > 0) #define LOG_DEBUG (this->debug_level > 1) #define LOG_TRACE (this->debug_level > 2) #define LOG_ACCEL 1 #define DEFAULT_AUTODETECT_RATE 4 /* unit: frames */ #define DEFAULT_STABILIZE_TIME (5*25) /* 5 seconds, unit: frames */ #define DEFAULT_SOFT_START_STEP 4 /* unit: lines per frame */ #define DEFAULT_SUBS_DETECT_LIFETIME (60*25) /* 1 minute, unit: frames */ #define DEFAULT_SUBS_DETECT_STABILIZE_TIME 12 /* unit: frames */ #define DEFAULT_LOGO_WIDTH 20 /* percentage of frame width */ /* parameters for avards algorithm */ #define DEFAULT_OVERSCAN_COMPENSATE 0 /* percentage of frame height for overscan compensation */ #define DEFAULT_BAR_TONE_TOLERANCE 0 /* 0...255 */ /* * Plugin */ typedef struct autocrop_parameters_s { int enable_autodetect; int autodetect_rate; int enable_subs_detect; int subs_detect_lifetime; int subs_detect_stabilize_time; int soft_start; int soft_start_step; int stabilize; int stabilize_time; int logo_width; int use_driver_crop; int use_avards_analysis; int overscan_compensate; int bar_tone_tolerance; int debug_level; } autocrop_parameters_t; START_PARAM_DESCR(autocrop_parameters_t) PARAM_ITEM(POST_PARAM_TYPE_BOOL, enable_autodetect, NULL, 0, 1, 0, "enable automatic border detecton") PARAM_ITEM(POST_PARAM_TYPE_INT, autodetect_rate, NULL, 1, 30, 0, "rate of automatic letterbox detection") PARAM_ITEM(POST_PARAM_TYPE_INT, stabilize_time, NULL, 1, 9999, 0, "number of frames with equal bars before cropping value changes") PARAM_ITEM(POST_PARAM_TYPE_BOOL, enable_subs_detect, NULL, 0, 1, 0, "enable automatic subtitle detecton") PARAM_ITEM(POST_PARAM_TYPE_INT, subs_detect_lifetime, NULL, 0, 9999, 0, "lifetime of automatic subtitle detection") PARAM_ITEM(POST_PARAM_TYPE_INT, subs_detect_stabilize_time, NULL, 0, 9999, 0, "stabilize time of automatic subtitle detection") PARAM_ITEM(POST_PARAM_TYPE_BOOL, soft_start, NULL, 0, 1, 0, "enable soft start of cropping") PARAM_ITEM(POST_PARAM_TYPE_INT, soft_start_step, NULL, 1, 999, 0, "soft start step width of cropping") PARAM_ITEM(POST_PARAM_TYPE_BOOL, stabilize, NULL, 0, 1, 0, "stabilize cropping to 14:9, 16:9, (16:9+subs), 20:9, (20:9+subs)") PARAM_ITEM(POST_PARAM_TYPE_INT, logo_width, NULL, 0, 99, 0, "maximum width of logo for automatic logo detection (percentage of frame width)") PARAM_ITEM(POST_PARAM_TYPE_BOOL, use_driver_crop, NULL, 0, 1, 0, "use video driver to crop frames (default is to copy frames in post plugin)") PARAM_ITEM(POST_PARAM_TYPE_BOOL, use_avards_analysis, NULL, 0, 1, 0, "use avards algorithm for frame analysis") PARAM_ITEM(POST_PARAM_TYPE_INT, overscan_compensate, NULL, 0, 100, 0, "compensation of output device overscan applied when cropping frames (%1000 of frame height)") PARAM_ITEM(POST_PARAM_TYPE_INT, bar_tone_tolerance, NULL, 0, 255, 0, "tolerance of bar color") PARAM_ITEM(POST_PARAM_TYPE_INT, debug_level, NULL, 0, 4, 0, "debug level") END_PARAM_DESCR(autocrop_param_descr) typedef struct autocrop_post_plugin_s { post_plugin_t post_plugin; xine_post_in_t parameter_input; /* setup */ int autodetect; int autodetect_rate; int subs_detect; int subs_detect_lifetime; int subs_detect_stabilize_time; int soft_start; int soft_start_step; int stabilize; int stabilize_time; int logo_width; int always_use_driver_crop; int use_avards_analysis; int overscan_compensate; int bar_tone_tolerance; int debug_level; /* Current cropping status */ int cropping_active; /* Detected bars */ int detected_end_line; int prev_detected_end_line; /* Stabilized bars */ int stabilized_start_line; int stabilized_end_line; /* Active bars */ int start_line; int end_line; int crop_total; /* Delayed start for cropping */ int stabilize_timer; /* frame counter (used to select frames for analysis) */ int analyze_timer; /* Last seen frame */ uint32_t prev_height; uint32_t prev_width; int64_t prev_pts; /* eliminate jumping when there are subtitles inside bottom bar: - when cropping is active and one frame has larger end_line than previous, we enlarge frame. - after this, cropping is not reseted to previous value unless bottom bar has been empty for certain time */ int height_limit_active; /* true if detected possible subtitles in bottom area */ int height_limit; /* do not crop bottom above this value (bottom of subtitles) */ int height_limit_timer; /* counter how many following frames must have black bottom bar until returning to full cropping (used to reset height_limit when there are no subtitles) */ int end_line_stabilize_timer; /* counter for detecting changes of end line */ int use_driver_crop; /* true if non standard frame format (e.g. vdpau) used */ int has_driver_crop; /* true if driver has cropping capability */ int has_unscaled_overlay; /* true if driver has unscaled overlay capability */ int prev_autodetect_rate; pthread_mutex_t crop_lock; /* retrieval of standard frame format image */ vo_frame_t frame; uint8_t *img; int img_size; } autocrop_post_plugin_t; #if (XINE_VERSION_CODE < 10190) && defined(VO_CAP_ARGB_LAYER_OVERLAY) /* xine-lib-vdpau r134+ */ # define HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA #endif #if XINE_VERSION_CODE >= 10190 /* xine-lib 1.2 r10718+ (2008-12-30) */ # define HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA #endif # if defined(__SSE__) # include # elif defined(__MMX__) # include # endif #if defined(__WORDSIZE) # if __WORDSIZE == 64 # define ENABLE_64BIT (sizeof(int) > 32) # endif #endif /* * Constants */ #define YNOISEFILTER (0xE0U) #define YSHIFTUP (0x05U) #define UVBLACK (0x80U) #define UVSHIFTUP (0x03U) #define UVNOISEFILTER (0xF8U) /* YV12 */ #define YNOISEFILTER32 (YNOISEFILTER * 0x01010101U) #define YSHIFTUP32 (YSHIFTUP * 0x01010101U) #define UVBLACK32 (UVBLACK * 0x01010101U) #define UVSHIFTUP32 (UVSHIFTUP * 0x01010101U) #define UVNOISEFILTER32 (UVNOISEFILTER * 0x01010101U) #define YNOISEFILTER64 (YNOISEFILTER * UINT64_C(0x0101010101010101)) #define YSHIFTUP64 (YSHIFTUP * UINT64_C(0x0101010101010101)) #define UVBLACK64 (UVBLACK * UINT64_C(0x0101010101010101)) #define UVSHIFTUP64 (UVSHIFTUP * UINT64_C(0x0101010101010101)) #define UVNOISEFILTER64 (UVNOISEFILTER * UINT64_C(0x0101010101010101)) /* YUY2 */ /* TODO: should use normal/inverse order based on endianess */ #if 0 #define YUY2BLACK32 (UVBLACK * 0x00010001U) #define YUY2SHIFTUP32 (UVSHIFTUP * 0x00010001U) #define YUY2NOISEFILTER32 ((YNOISEFILTER * 0x01000100U)|(UVNOISEFILTER * 0x00010001U)) #else #define YUY2BLACK32 (UVBLACK * 0x01000100U) #define YUY2SHIFTUP32 (UVSHIFTUP * 0x01000100U) #define YUY2NOISEFILTER32 ((YNOISEFILTER * 0x00010001U)|(UVNOISEFILTER * 0x01000100U)) #endif #define YUY2BLACK64 (YUY2BLACK32 * UINT64_C(0x0000000100000001)) #define YUY2SHIFTUP64 (YUY2SHIFTUP32 * UINT64_C(0x0000000100000001)) #define YUY2NOISEFILTER64 (YUY2NOISEFILTER32 * UINT64_C(0x0000000100000001)) #ifdef FILTER2 /* tighter Y-filter: original black threshold is 0x1f ; here it is 0x1f - 0x0b = 0x14 */ # define YUY2SHIFTUP32 ((UVSHIFTUP * 0x00010001U)|(YSHIFTUP * 0x01000100U)) # undef __SSE__ #endif /* * Black bar detection * * Detect black lines with simple noise filtering. * Line is "black" if Y-valus are less than 0x20 and * U/V values inside range 0x7d...0x84. * ~ 32 first and last pixels are not checked. * */ static int blank_line_Y_C(uint8_t *data, int length); static int blank_line_UV_C(uint8_t *data, int length); static int blank_line_YUY2_C(uint8_t *data, int length); #if defined(ENABLE_64BIT) static int blank_line_Y_C64(uint8_t *data, int length); static int blank_line_UV_C64(uint8_t *data, int length); static int blank_line_YUY2_C64(uint8_t *data, int length); #endif #if defined(__MMX__) static int blank_line_Y_mmx(uint8_t *data, int length); static int blank_line_UV_mmx(uint8_t *data, int length); static int blank_line_YUY2_mmx(uint8_t *data, int length); #endif #if defined(__SSE__) static int blank_line_Y_sse(uint8_t *data, int length); static int blank_line_UV_sse(uint8_t *data, int length); static int blank_line_YUY2_sse(uint8_t *data, int length); #endif static int blank_line_Y_INIT(uint8_t *data, int length); static int blank_line_UV_INIT(uint8_t *data, int length); static int blank_line_YUY2_INIT(uint8_t *data, int length); static void autocrop_init_mm_accel(void); int (*blank_line_Y)(uint8_t *data, int length) = blank_line_Y_INIT; int (*blank_line_UV)(uint8_t *data, int length) = blank_line_UV_INIT; int (*blank_line_YUY2)(uint8_t *data, int length) = blank_line_YUY2_INIT; static int blank_line_Y_C(uint8_t *data, int length) { uint32_t *data32 = (uint32_t*)((((long int)data) + 32 + 3) & (~3)), r = 0; length -= 64; /* skip borders (2 x 32 pixels) */ length /= 4; /* 4 bytes / loop */ #ifdef FILTER2 while(length) { /* shiftdown needs saturated unsigned element-wise substraction, available only in MMX ...*/ /* -> use shiftup and looser noise filter for same result. */ /* this needs special handling for large values */ r = r | data32[--length]; /* this catches large values (0xf9 : 0xf9+0x7=0x100 === black) */ r = r | (data32[length] + YSHIFTUP32); /* this catches small walues (0x1d...0x1f) */ } #else while(length) r = r | data32[--length]; #endif return !(r & YNOISEFILTER32); } static int blank_line_UV_C(uint8_t *data, int length) { uint32_t *data32 = (uint32_t*)((((long int)data) + 16 + 3) & (~3)); uint32_t r1 = 0, r2 = 0; length -= 32; /* skip borders (2 x 32 pixels, 2 pix/byte) */ length /= 4; /* 2 x 4 bytes / loop */ while(length>0) { r1 = r1 | ((data32[--length] + UVSHIFTUP32) ^ UVBLACK32); r2 = r2 | ((data32[--length] + UVSHIFTUP32) ^ UVBLACK32); } return !((r1|r2) & (UVNOISEFILTER32)); } #if defined(ENABLE_64BIT) static int blank_line_Y_C64(uint8_t *data, int length) { uint64_t *data64 = (uint64_t*)((((long int)data) + 32 + 7) & (~7)), r = 0; length -= 64; /* skip borders (2 x 32 pixels) */ length /= 8; /* 8 bytes / loop */ #ifdef FILTER2 while(length) { r = r | data64[--length]; r = r | (data64[length] + YSHIFTUP64); } #else while(length) r = r | data64[--length]; #endif return !(r & YNOISEFILTER64); } #endif #if defined(ENABLE_64BIT) static int blank_line_UV_C64(uint8_t *data, int length) { uint64_t *data64 = (uint64_t*)((((long int)data) + 16 + 7) & (~7)); uint64_t r1 = UINT64_C(0), r2 = UINT64_C(0); length -= 32; /* skip borders (2x32 pixels, 2 pix/byte) */ length /= 8; /* 2 x 8 bytes / loop */ while(length>0) { r1 = r1 | ((data64[--length] + UVSHIFTUP64) ^ UVBLACK64); r2 = r2 | ((data64[--length] + UVSHIFTUP64) ^ UVBLACK64); } return !((r1|r2) & (UVNOISEFILTER64)); } #endif #if defined(__MMX__) typedef union { uint32_t u32[2]; __m64 m64; } __attribute__((__aligned__ (8))) __m64_wrapper; #endif #if defined(__MMX__) int blank_line_Y_mmx(uint8_t *data, int length) { #ifdef FILTER2 static const __m64_wrapper mask = {{YNOISEFILTER32, YNOISEFILTER32}}; static const __m64_wrapper gshift = {{YSHIFTUP32, YSHIFTUP32}}; register __m64 sum, sum2, shift = gshift.m64, val; #else static const __m64_wrapper mask = {{YNOISEFILTER32, YNOISEFILTER32}}; register __m64 sum; #endif __m64 *data64 = (__m64*)(((long int)(data + 32 + 7)) & (~7)); /*sum = _m_pxor(sum,sum);*/ __asm__("pxor %0,%0" : "=y"(sum)); length -= 64; /* skip borders (2 x 32 pixels) */ length /= 8; /* 8 bytes / loop */ #ifdef FILTER2 __asm__("pxor %0,%0" : "=y"(sum2)); while(length) { val = data64[--length]; sum = _m_por(sum, val); sum2 = _m_por(sum2, _m_paddb(val, shift)); } sum = _m_por(sum, sum2); #else while(length) sum = _m_por(sum, data64[--length]); #endif sum = _m_pand(sum, mask.m64); return 0 == _m_to_int(_m_packsswb(sum, sum)); } #endif #if defined(__MMX__) static int blank_line_UV_mmx(uint8_t *data, int length) { static const __m64_wrapper gm_03 = {{UVSHIFTUP32, UVSHIFTUP32}}; static const __m64_wrapper gm_f8 = {{UVNOISEFILTER32, UVNOISEFILTER32}}; static const __m64_wrapper gm_80 = {{UVBLACK32, UVBLACK32}}; __m64 *data64 = (__m64*)(((long int)(data) + 16 + 7) & (~7)); register __m64 sum1, sum2, m_03, /*m_f8,*/ m_80; /*sum1 = _m_pxor(sum1, sum1); sum1 = _mm_setzero_si64(); */ /*sum2 = _m_pxor(sum2, sum2); sum2 = _mm_setzero_si64(); */ __asm__("pxor %0,%0" : "=y"(sum1)); __asm__("pxor %0,%0" : "=y"(sum2)); /* fetch static data to MMX registers */ m_03 = gm_03.m64; /*m_f8 = gm_f8.m64;*/ m_80 = gm_80.m64; length -= 32; /* skip borders (2 x 32 pixels, 2pix/byte) */ length /= 8; /* 8 bytes / vector */ do { /* process two 8-byte vectors */ sum1 = _m_por(sum1, /* grab every byte that is not black (x ^ 0x80 != 0) */ _m_pxor( /* filter noise: U/V of each "black" pixel should be 0x7d..0x84 -> each black pixel should be 0x80 after (x+3) & 0xf8 */ /*_m_pand(*/ /* each black pixel should be 0x80..0x87 after adding 3 */ _m_paddb( data64[length-1], m_03)/*, m_f8)*/, m_80)); sum2 = _m_por(sum2, _m_pxor( /*_m_pand( */ _m_paddb( data64[length-2], m_03)/*, m_f8)*/, m_80)); length -= 2; } while(length>0); /* combine two result vectors (or), filter noise (and) */ sum1 = _m_pand(_m_por(sum1, sum2), gm_f8.m64); /* result vector of black line is 0 */ return 0 == _m_to_int(_m_packsswb(sum1, sum1)); } #endif #if defined(__SSE__) typedef union { uint32_t u32[4]; __m128 m128; } __attribute((__aligned__ (16))) __m128_wrapper; #endif #if defined(__SSE__) static int blank_line_Y_sse(uint8_t *data, int length) { static const __m128_wrapper gmask = {{YNOISEFILTER32, YNOISEFILTER32, YNOISEFILTER32, YNOISEFILTER32}}; __m128 *data128 = (__m128*)(((long int)(data) + 32 + 15) & (~15)); register __m128 sum1, sum2, zero; length -= 64; /* skip borders (2 x 32 pixels) */ length /= 16; /* 16 bytes / loop */ /* Start prefetching data to CPU cache */ _mm_prefetch(data128+length-1, _MM_HINT_NTA); _mm_prefetch(data128+length-3, _MM_HINT_NTA); /* * Process in two paraller loops, one 16 byte vector / each sub-loop * - grabs bytes with value larger than treshold */ zero = _mm_setzero_ps(); sum1 = zero; sum2 = zero; do { _mm_prefetch(data128+length-5, _MM_HINT_NTA); sum1 = _mm_or_ps(sum1, data128[--length]); sum2 = _mm_or_ps(sum2, data128[--length]); } while(length>0); return 0x0f == _mm_movemask_ps(_mm_cmpeq_ps(_mm_and_ps(_mm_or_ps(sum1, sum2), gmask.m128), _mm_setzero_ps())); } #endif #if defined(__SSE__) static int blank_line_UV_sse(uint8_t *data, int length) { uint8_t *top = data + length - 1; do { _mm_prefetch(top, _MM_HINT_NTA); _mm_prefetch(top-32, _MM_HINT_NTA); _mm_prefetch(top-64, _MM_HINT_NTA); _mm_prefetch(top-72, _MM_HINT_NTA); top -= 128; } while(top >= data); return blank_line_UV_mmx(data, length); } #endif static int blank_line_YUY2_C(uint8_t *data, int length) { uint32_t *data32 = (uint32_t*)((((long int)data) + 64 + 3) & (~3)); uint32_t r1 = 0, r2 = 0; length -= 128; /* skip borders (2 x 32 pixels, 2 bytes/pixel) */ length /= 4; /* 2 x 4 bytes / loop */ while(length) { r1 = r1 | ((data32[--length] + YUY2SHIFTUP32) ^ YUY2BLACK32); r2 = r2 | ((data32[--length] + YUY2SHIFTUP32) ^ YUY2BLACK32); } return !((r1|r2) & YUY2NOISEFILTER32); } #if defined(ENABLE_64BIT) static int blank_line_YUY2_C64(uint8_t *data, int length) { uint64_t *data64 = (uint64_t*)((((long int)data) + 64 + 7) & (~7)); uint64_t r1 = 0, r2 = 0; length -= 128; /* skip borders (2 x 32 pixels, 2 bytes/pixel) */ length /= 8; /* 2 x 8 bytes / loop */ while(length) { r1 = r1 | ((data64[--length] + YUY2SHIFTUP64) ^ YUY2BLACK64); r2 = r2 | ((data64[--length] + YUY2SHIFTUP64) ^ YUY2BLACK64); } return !((r1|r2) & YUY2NOISEFILTER64); } #endif #if defined(__MMX__) static int blank_line_YUY2_mmx(uint8_t *data, int length) { /* not implemented */ # if !defined(ENABLE_64BIT) return blank_line_YUY2_C(data, length); # else return blank_line_YUY2_C64(data, length); # endif } #endif #if defined(__SSE__) static int blank_line_YUY2_sse(uint8_t *data, int length) { uint8_t *top = data + length - 1; do { _mm_prefetch(top, _MM_HINT_NTA); _mm_prefetch(top-32, _MM_HINT_NTA); _mm_prefetch(top-64, _MM_HINT_NTA); _mm_prefetch(top-72, _MM_HINT_NTA); top -= 128; } while(top >= data); return blank_line_YUY2_mmx(data, length); } #endif static void autocrop_init_mm_accel(void) { blank_line_Y = blank_line_Y_C; blank_line_UV = blank_line_UV_C; blank_line_YUY2 = blank_line_YUY2_C; #if defined(__SSE__) if(xine_mm_accel() & MM_ACCEL_X86_SSE) { llprintf(LOG_ACCEL, "autocrop_init_mm_accel: using SSE\n"); blank_line_Y = blank_line_Y_sse; blank_line_UV = blank_line_UV_sse; blank_line_YUY2 = blank_line_YUY2_sse; return; } #endif #if defined(ENABLE_64BIT) if(ENABLE_64BIT) { llprintf(LOG_ACCEL, "autocrop_init_mm_accel: using 64-bit integer operations\n"); blank_line_Y = blank_line_Y_C64; blank_line_UV = blank_line_UV_C64; blank_line_YUY2 = blank_line_YUY2_C64; return; } #endif #if defined(__MMX__) if(xine_mm_accel() & MM_ACCEL_X86_MMX) { /* mmx not faster than normal x64 (?) */ llprintf(LOG_ACCEL, "autocrop_init_mm_accel: using MMX\n"); blank_line_Y = blank_line_Y_mmx; blank_line_UV = blank_line_UV_mmx; blank_line_YUY2 = blank_line_YUY2_mmx; return; } #endif llprintf(LOG_ACCEL, "autocrop_init_mm_accel: no compatible acceleration methods found\n"); } static int blank_line_Y_INIT(uint8_t *data, int length) { autocrop_init_mm_accel(); return (*blank_line_Y)(data, length); } static int blank_line_UV_INIT(uint8_t *data, int length) { autocrop_init_mm_accel(); return (*blank_line_UV)(data, length); } static int blank_line_YUY2_INIT(uint8_t *data, int length) { autocrop_init_mm_accel(); return (*blank_line_YUY2)(data, length); } /* * Analyze frame * - if frame needs cropping set crop_top & crop_bottom */ #ifdef MARK_FRAME int dbg_top=0, dbg_bottom=0; #endif static int analyze_frame_yv12(autocrop_post_plugin_t *this, vo_frame_t *frame, int *crop_top, int *crop_bottom) { int y; int ypitch = frame->pitches[0]; int upitch = frame->pitches[1]; int vpitch = frame->pitches[2]; uint8_t *ydata = frame->base[0]; uint8_t *udata = frame->base[1]; uint8_t *vdata = frame->base[2]; int max_crop = (frame->height / 4) / 2; /* 4:3 --> 16:9 */ int logo_width = frame->width * this->logo_width / 100; int check_width = frame->width - logo_width; /* from top -> down */ ydata += 8 * ypitch; /* skip 8 first lines */ udata += 4 * upitch; vdata += 4 * vpitch; for(y = 8; y <= max_crop *2 /* *2 = 20:9+subs -> 16:9 */ ; y += 2) { if( ! ( blank_line_UV(udata, check_width/2) || blank_line_UV(udata+logo_width/2, check_width/2) ) || ! ( blank_line_UV(vdata, check_width/2) || blank_line_UV(vdata+logo_width/2, check_width/2) ) || ! ( blank_line_Y( ydata, check_width ) || blank_line_Y( ydata+logo_width, check_width ) ) || ! ( blank_line_Y( ydata+ypitch, check_width ) || blank_line_Y( ydata+ypitch+logo_width,check_width ) )) { break; } else { ydata += 2 * ypitch; udata += upitch; vdata += vpitch; } } *crop_top = y>8 ? y : 0; /* from bottom -> up */ ydata = frame->base[0] + ((frame->height-4) -1 ) * ypitch; udata = frame->base[1] + ((frame->height-4)/2 -1 ) * upitch; vdata = frame->base[2] + ((frame->height-4)/2 -1 ) * vpitch; for(y = frame->height - 5; y >= frame->height-max_crop; y -=2 ) { if( ! blank_line_Y(ydata, frame->width) || ! blank_line_Y(ydata-ypitch, frame->width) || ! blank_line_UV(udata, frame->width/2) || ! blank_line_UV(vdata, frame->width/2)) { break; } else { ydata -= 2*ypitch; udata -= upitch; vdata -= vpitch; } } *crop_bottom = y; /* test for black in center - don't crop if frame is empty */ if(*crop_top >= max_crop*2 && *crop_bottom <= frame->height-max_crop) { int center_line = (frame->height >> 2) << 1; ydata = frame->base[0] + (center_line )*ypitch; udata = frame->base[1] + (center_line/2)*upitch; vdata = frame->base[2] + (center_line/2)*vpitch; if( blank_line_Y(ydata, frame->width) && blank_line_Y(ydata+ypitch, frame->width) && blank_line_UV(udata, frame->width/2) && blank_line_UV(vdata, frame->width/2)) { llprintf(LOG_DEBUG, "not cropping black frame\n"); return 0; } } return 1; } static int analyze_frame_yuy2(autocrop_post_plugin_t *this, vo_frame_t *frame, int *crop_top, int *crop_bottom) { int y; int pitch = frame->pitches[0]; uint8_t *data = frame->base[0]; int max_crop = (frame->height / 4) / 2; /* 4:3 --> 16:9 */ int logo_width = frame->width * this->logo_width / 100; int check_width = frame->width - logo_width; /* from top -> down */ data += 6 * pitch; /* skip 6 first lines */ for(y = 6; y <= max_crop *2 /* *2 = 20:9+subs -> 16:9 */ ; y ++) if( ! ( blank_line_YUY2(data, check_width*2) || blank_line_YUY2(data+2*logo_width, check_width*2))) break; else data += pitch; *crop_top = y; /* from bottom -> up */ data = frame->base[0] + ((frame->height-4) -1 ) * pitch; for(y = frame->height - 5; y >= frame->height-max_crop; y -- ) if( ! blank_line_YUY2(data, frame->width * 2)) break; else data -= pitch; *crop_bottom = y; /* test for black in center - don't crop if frame is empty */ if(*crop_top >= max_crop*2 && *crop_bottom <= frame->height-max_crop) { data = frame->base[0] + (frame->height/2)*pitch; if( blank_line_YUY2(data, frame->width * 2)) { llprintf(LOG_DEBUG, "not cropping black frame\n"); return 0; } } return 1; } #define AVARDS_FN(fn_name, pixel_pitch, top_overscan_compensate) \ static int fn_name (autocrop_post_plugin_t *this, vo_frame_t *frame, int *crop_top, int *crop_bottom) \ { \ int i, top, bottom, histogram[256]; \ uint8_t *left, *right; \ uint8_t *img = frame->base[0]; \ const int bottom_overscan_compensate = 4; \ const int ignored_side_width = 16 * pixel_pitch; \ const int width = frame->width * pixel_pitch; \ const int top_logo_width = (frame->width * this->logo_width / 100) * pixel_pitch; \ const int bottom_logo_width = this->subs_detect ? -1: top_logo_width - 1; \ const int pitch = frame->pitches[0]; \ const int half_height = frame->height / 2; \ \ memset(histogram, 0, sizeof(histogram)); \ left = img + top_overscan_compensate * pitch; \ right = left + width - ignored_side_width; \ left += ignored_side_width; \ while (left < right) { \ histogram[*left]++; \ left += pixel_pitch; \ } \ \ int bar_tone = 0; \ for (i = 1; i < 256; ++i) { \ if (histogram[i] > histogram[bar_tone]) \ bar_tone = i; \ } \ const uint8_t min_bar_tone = (bar_tone > this->bar_tone_tolerance) ? bar_tone - this->bar_tone_tolerance: 0; \ const uint8_t max_bar_tone = (bar_tone + this->bar_tone_tolerance < 255) ? bar_tone + this->bar_tone_tolerance: 255; \ \ for (top = top_overscan_compensate; top < half_height; ++top) { \ left = img + top * pitch; \ right = left + width - pixel_pitch - ignored_side_width; \ left += ignored_side_width; \ while (left <= right && *left >= min_bar_tone && *left <= max_bar_tone) \ left += pixel_pitch; \ while (right > left && *right >= min_bar_tone && *right <= max_bar_tone) \ right -= pixel_pitch; \ if ((right - left) > top_logo_width) \ break; \ } \ \ for (bottom = frame->height - 1 - bottom_overscan_compensate; bottom > half_height; --bottom) { \ left = img + bottom * pitch; \ right = left + width - pixel_pitch - ignored_side_width; \ left += ignored_side_width; \ while (left <= right && *left >= min_bar_tone && *left <= max_bar_tone) \ left += pixel_pitch; \ while (right > left && *right >= min_bar_tone && *right <= max_bar_tone) \ right -= pixel_pitch; \ if ((right - left) > bottom_logo_width) \ break; \ } \ \ *crop_top = top; \ *crop_bottom = bottom; \ return ((bottom - top) > 0); \ } AVARDS_FN(analyze_frame_yv12_avards, 1, 8) AVARDS_FN(analyze_frame_yuy2_avards, 2, 6) static void analyze_frame(vo_frame_t *frame, int *crop_top, int *crop_bottom) { post_video_port_t *port = (post_video_port_t *)frame->port; autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; int start_line, end_line; #ifdef HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA if ((frame->format != XINE_IMGFMT_YV12 && frame->format != XINE_IMGFMT_YUY2) && frame->proc_provide_standard_frame_data) { xine_current_frame_data_t data; memset(&data, 0, sizeof(data)); frame->proc_provide_standard_frame_data(frame->next, &data); if (data.img_size > this->img_size) { free(this->img); this->img = calloc(1, data.img_size); if (!this->img) { this->img_size = 0; return; } this->img_size = data.img_size; } data.img = this->img; frame->proc_provide_standard_frame_data(frame->next, &data); if (data.format == XINE_IMGFMT_YV12) { this->frame.pitches[0] = frame->width; this->frame.pitches[1] = frame->width / 2; this->frame.pitches[2] = frame->width / 2; this->frame.base[0] = this->img; this->frame.base[1] = this->img + frame->width * frame->height; this->frame.base[2] = this->img + frame->width * frame->height + frame->width * frame->height / 4; } else if (data.format == XINE_IMGFMT_YUY2) { this->frame.pitches[0] = frame->width * 2; this->frame.base[0] = this->img; } this->frame.format = data.format; this->frame.height = frame->height; this->frame.width = frame->width; frame = &this->frame; } #endif int result = 0; if (this->use_avards_analysis) { if (frame->format == XINE_IMGFMT_YV12) result = analyze_frame_yv12_avards(this, frame, &start_line, &end_line); else if (frame->format == XINE_IMGFMT_YUY2) result = analyze_frame_yuy2_avards(this, frame, &start_line, &end_line); } else { if(frame->format == XINE_IMGFMT_YV12) result = analyze_frame_yv12(this, frame, &start_line, &end_line); else if(frame->format == XINE_IMGFMT_YUY2) result = analyze_frame_yuy2(this, frame, &start_line, &end_line); #if defined(__MMX__) _mm_empty(); #endif } /* Ignore empty frames */ if(!result) { llprintf(LOG_TRACE, "not cropping black frame\n"); return; } #ifdef MARK_FRAME dbg_top = start_line; dbg_bottom = end_line; #endif if(this->stabilize) { int bottom = frame->height - end_line; int wide = 0; /* bottom bar size */ if(bottom < frame->height/32) { llprintf(LOG_TRACE, "bottom: %d -> 4:3 ", end_line); end_line = frame->height - 1; /* no cropping */ } else if(bottom < frame->height*3/32) { llprintf(LOG_TRACE, "bottom: %d -> 14:9 (%d) ", end_line, frame->height * 15 / 16 - 1); end_line = frame->height * 15 / 16 - 1; /* 14:9 */ } else if(bottom < frame->height*3/16) { llprintf(LOG_TRACE, "bottom: %d -> 16:9 (%d) ", end_line, frame->height * 7 / 8 - 1); end_line = frame->height * 7 / 8 - 1; /* 16:9 */ wide = 1; } else { llprintf(LOG_TRACE, "bottom: %d -> 20:9 (%d) ", end_line, frame->height * 3 / 4 - 1); end_line = frame->height * 3 / 4 - 1; /* 20:9 */ wide = 2; } /* top bar size */ if(start_line < frame->height/32) { llprintf(LOG_TRACE, "top: %3d -> 4:3 \n", start_line); start_line = 0; /* no cropping */ } else if(start_line < frame->height*3/32) { llprintf(LOG_TRACE, "top: %3d -> 14:9 (%d)\n", start_line, frame->height / 16); start_line = frame->height / 16; /* 14:9 */ } else if(start_line < frame->height*3/16 || wide) { llprintf(LOG_TRACE, "top: %3d -> 16:9 (%d)\n", start_line, frame->height / 8); start_line = frame->height / 8; /* 16:9 */ } else { llprintf(LOG_TRACE, "top: %3d -> 20:9 (%d)\n", start_line, frame->height / 4); start_line = frame->height / 4; /* 20:9 */ wide++; } switch(wide) { case 3: start_line -= frame->height / 8; if(start_line < 0) start_line = 0; llprintf(LOG_TRACE, " wide -> center top\n"); case 2: end_line += frame->height / 8; if(end_line >= frame->height) end_line = frame->height-1; llprintf(LOG_TRACE, " wide -> center bottom\n"); default:; } } else { if(start_line > (frame->height/8 *2)) /* *2 --> 20:9 -> 16:9 + subtitles */ start_line = frame->height/8 *2 ; if(end_line < (frame->height*7/8)) end_line = frame->height*7/8; if(start_line > (frame->height/8)) { /* if wider than 16:9, prefer cropping top if subtitles are inside bottom bar */ if(start_line + (frame->height - end_line) > frame->height/4) { int diff = start_line + (frame->height - end_line) - frame->height/4; diff &= ~1; llprintf(LOG_TRACE, "balance: %d,%d -> %d,%d\n", start_line, end_line, start_line, end_line + diff); #if 0 /* this moves image to top (crop only top) */ end_line += diff; #endif #if 0 /* this moves image to center */ /* may cause problems with subtitles ... */ start_line -= diff; #endif #if 1 /* this moves image to center when there are no detected subtitles inside bottom bar */ if(this->cropping_active && this->height_limit_active) { int reserved = this->height_limit - end_line; if(reserved>0) { end_line += reserved; diff -= reserved; } } start_line -= diff; #endif #if 0 /* do nothing - image will be centered in video out. - problems with subtitles using unscaled OSD */ #endif } } /* stay inside frame and forget very small bars */ if(start_line <= 8) start_line = 0; if(end_line >= (frame->height-6)) end_line = frame->height; if(start_line < frame->height/12 || end_line > frame->height*11/12) { /* Small bars -> crop only detected borders */ if(start_line || end_line < frame->height-1) { llprintf(LOG_TRACE, "Small bars -> <16:9 : start_line = %d end_line = %d (%s%d t%d)\n", start_line, end_line, this->height_limit_active ? "height limit " : "", this->height_limit, this->height_limit_active ? this->height_limit_timer : 0); } } else { /* Large bars -> crop to 16:9 */ llprintf(LOG_TRACE, "Large bars -> 16:9 : start_line = %d end_line = %d (%s%d t%d)\n", start_line, end_line, this->height_limit_active ? "height limit " : "", this->height_limit, this->height_limit_active ? this->height_limit_timer : 0); if(start_line < frame->height / 8) start_line = frame->height / 8; if(end_line < frame->height * 7 / 8) end_line = frame->height * 7 / 8; } } if (this->overscan_compensate > 0) { int h = (frame->height * this->overscan_compensate) / (2*1000); if (start_line < h) h = start_line; if ((frame->height - end_line) < h) h = frame->height - end_line; start_line -= h; end_line += h; } /* adjust start and stop to even lines */ (*crop_top) = (start_line) & (~1); (*crop_bottom) = (end_line + 1) & (~1); } #ifdef MARK_FRAME static void mark_frame_yv12(autocrop_post_plugin_t *this, vo_frame_t *frame, int *crop_top, int *crop_bottom) { int ypitch = frame->pitches[0]; int upitch = frame->pitches[1]; int vpitch = frame->pitches[2]; uint8_t *ydata = frame->base[0]; uint8_t *udata = frame->base[1]; uint8_t *vdata = frame->base[2]; /* draw markers to detected boundaries and expected boundaries */ if(*crop_top > 4 && *crop_top < 200) { ydata = frame->base[0] + ((*crop_top)-2)*ypitch; udata = frame->base[1] + ((*crop_top)/2 -1)*upitch; memset(ydata, 0xff, frame->width/10); memset(ydata+ypitch, 0xff, frame->width/10); memset(udata, 0xff, frame->width/2/10); if(dbg_top < *crop_top) dbg_top = *crop_top; ydata = frame->base[0] + ((dbg_top - *crop_top))*ypitch; udata = frame->base[1] + ((dbg_top - *crop_top)/2)*upitch; memset(ydata, 0x80, frame->width/2); memset(ydata+ypitch, 0x80, frame->width/2); memset(udata, 0xff, frame->width/2/2); } if(*crop_bottom > 300) { if(*crop_bottom < frame->height - 2) { ydata = frame->base[0] + (*crop_bottom + 2)*ypitch; udata = frame->base[1] + ((*crop_bottom)/2)*upitch; memset(ydata, 0xff, frame->width); memset(ydata+ypitch, 0xff, frame->width); memset(udata, 0xff, frame->width/2); } if(dbg_bottom - *crop_top - 2 < frame->height) { ydata = frame->base[0] + (dbg_bottom - *crop_top - 2)*ypitch; udata = frame->base[1] + (dbg_bottom - *crop_top - 2)/2*upitch; memset(ydata, 0x80, frame->width/2); memset(ydata+ypitch, 0xff, frame->width/2); memset(udata, 0xff, frame->width/2/2); } } if(frame->height > 500) { /* TODO: use frame height instead of assuming 576 ... -> 72 */ vdata = frame->base[2] + ((72-*crop_top)/2)*vpitch; vdata = frame->base[2] + (72/2)*vpitch; memset(vdata, 0xff, frame->width/2); vdata = frame->base[2] + ((frame->height-72+(576-*crop_bottom))/2)*vpitch; vdata = frame->base[2] + ((frame->height-72)/2)*vpitch; memset(vdata, 0xff, frame->width/2); } } #endif /* * crop frame by copying */ static int crop_copy_yv12(vo_frame_t *frame, xine_stream_t *stream) { post_video_port_t *port = (post_video_port_t *)frame->port; autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; vo_frame_t *new_frame; int y, result; int yp = frame->pitches[0], yp2; int up = frame->pitches[1], up2; int vp = frame->pitches[2], vp2; uint8_t *ydata = frame->base[0], *ydata2; uint8_t *udata = frame->base[1], *udata2; uint8_t *vdata = frame->base[2], *vdata2; int new_height; double new_ratio; /* top bar */ ydata += this->start_line * yp; udata += (this->start_line/2) * up; vdata += (this->start_line/2) * vp; new_height = this->end_line - this->start_line; new_ratio = 12.0/9.0 * ((double)frame->height / (double)new_height); new_frame = port->original_port->get_frame(port->original_port, frame->width, new_height, new_ratio, frame->format, frame->flags | VO_BOTH_FIELDS); /* ??? */ frame->ratio = new_frame->ratio; _x_post_frame_copy_down(frame, new_frame); yp2 = new_frame->pitches[0]; up2 = new_frame->pitches[1]; vp2 = new_frame->pitches[2]; ydata2 = new_frame->base[0]; udata2 = new_frame->base[1]; vdata2 = new_frame->base[2]; /* TODO: save channel logo mask (from top) - no changes in 3 sec -> next time crop it out ... */ for(y=0; y < new_height/2; y++) { xine_fast_memcpy(ydata2, ydata, frame->width); ydata += yp; ydata2 += yp2; xine_fast_memcpy(ydata2, ydata, frame->width); ydata += yp; ydata2 += yp2; xine_fast_memcpy(udata2, udata, frame->width/2); udata += up; udata2 += up2; xine_fast_memcpy(vdata2, vdata, frame->width/2); vdata += vp; vdata2 += vp2; } #ifdef MARK_FRAME mark_frame_yv12(this, new_frame, &this->start_line, &this->end_line); #endif result = new_frame->draw(new_frame, stream); _x_post_frame_copy_up(frame, new_frame); new_frame->free(new_frame); return result; } static int crop_copy_yuy2(vo_frame_t *frame, xine_stream_t *stream) { post_video_port_t *port = (post_video_port_t *)frame->port; autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; vo_frame_t *new_frame; int y, result; int p = frame->pitches[0], p2; uint8_t *data = frame->base[0], *data2; int new_height; double new_ratio; /* top bar */ data += this->start_line * p; new_height = this->end_line - this->start_line; new_ratio = 12.0/9.0 * ((double)frame->height / (double)new_height); new_frame = port->original_port->get_frame(port->original_port, frame->width, new_height, new_ratio, frame->format, frame->flags | VO_BOTH_FIELDS); /* ??? */ frame->ratio = new_frame->ratio; _x_post_frame_copy_down(frame, new_frame); p2 = new_frame->pitches[0]; data2 = new_frame->base[0]; for(y=0; y < new_height; y++) { xine_fast_memcpy(data2, data, frame->width); data += p; data2 += p2; } result = new_frame->draw(new_frame, stream); _x_post_frame_copy_up(frame, new_frame); new_frame->free(new_frame); return result; } /* * Frame handling */ static int autocrop_draw(vo_frame_t *frame, xine_stream_t *stream) { post_video_port_t *port = (post_video_port_t *)frame->port; autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; int result, start_line, end_line; if(!this->autodetect) { pthread_mutex_lock(&this->crop_lock); this->cropping_active = 1; this->start_line = frame->height/8; this->end_line = frame->height*7/8; this->crop_total = frame->height/4; this->use_driver_crop = this->always_use_driver_crop || (frame->format != XINE_IMGFMT_YV12 && frame->format != XINE_IMGFMT_YUY2); pthread_mutex_unlock(&this->crop_lock); if (frame->bad_frame || this->use_driver_crop) { _x_post_frame_copy_down(frame, frame->next); result = frame->next->draw(frame->next, stream); _x_post_frame_copy_up(frame, frame->next); } else if(frame->format == XINE_IMGFMT_YV12) result = crop_copy_yv12(frame, stream); else /*if(frame->format == XINE_IMGFMT_YUY2)*/ result = crop_copy_yuy2(frame, stream); return result; } int autodetect_rate = this->autodetect_rate; int cropping_active = this->cropping_active; /* use pts jumps to track stream changes (and seeks) */ if(cropping_active && frame->pts > 0) { if(this->prev_pts>0) { int64_t dpts = frame->pts - this->prev_pts; if(dpts < INT64_C(-30*90000) || dpts > INT64_C(30*90000)) { /* 30 sec */ if(this->height_limit_active) { this->height_limit_timer = this->subs_detect_lifetime / 2; llprintf(LOG_DEBUG, "short pts jump reseted height limit\n"); } } if(dpts < INT64_C(-30*60*90000) || dpts > INT64_C(30*60*90000)) { /* 30 min */ cropping_active = 0; llprintf(LOG_DEBUG, "long pts jump reseted cropping\n"); } } this->prev_pts = frame->pts; } if (cropping_active) { start_line = this->stabilized_start_line; end_line = this->stabilized_end_line; } else { start_line = 0; end_line = frame->height; } /* Analyze frame for letterbox borders */ if(!frame->bad_frame && (this->analyze_timer % autodetect_rate) == 0) { analyze_frame(frame, &start_line, &end_line); int detected_end_line = end_line; /* activate cropping if bars are large enough */ if(!cropping_active && (start_line > 10 || end_line < (frame->height - 10))) { cropping_active = 1; this->stabilized_start_line = this->start_line = 0; this->stabilized_end_line = this->end_line = this->detected_end_line = this->prev_detected_end_line = frame->height; this->stabilize_timer = this->stabilize_time; this->prev_pts = -1; this->height_limit_active = 0; this->end_line_stabilize_timer = this->subs_detect_stabilize_time; } if(cropping_active && this->subs_detect) { /* no change unless different values for several frames */ if (abs(this->detected_end_line - end_line) > 5) { this->end_line_stabilize_timer -= autodetect_rate; if (this->end_line_stabilize_timer <= 0) { this->detected_end_line = end_line; this->end_line_stabilize_timer = this->subs_detect_stabilize_time; } } else this->end_line_stabilize_timer = this->subs_detect_stabilize_time; if(this->height_limit_active) { this->height_limit_timer -= autodetect_rate; if (this->height_limit_timer <= 0) { this->height_limit_active = 0; llprintf(LOG_DEBUG, "height limit timer expired\n"); } } /* apply height limit */ if(this->height_limit_active && end_line < this->height_limit) end_line = this->height_limit; } /* no change unless different values for several frames */ if(abs(start_line - this->stabilized_start_line) > 5 || abs(end_line - this->stabilized_end_line) > 5) { this->stabilize_timer -= autodetect_rate; if(this->stabilize_timer > 0) { start_line = this->stabilized_start_line; end_line = this->stabilized_end_line; } else { /* ignore very small bars */ if(start_line <= 10 && end_line >= (frame->height - 10)) cropping_active = 0; this->stabilize_timer = this->stabilize_time; llprintf(LOG_DEBUG, "stabilized start: %d -> %d, end %d -> %d\n", this->stabilized_start_line, start_line, this->stabilized_end_line, end_line); } } else { this->stabilize_timer = this->stabilize_time; start_line = this->stabilized_start_line; end_line = this->stabilized_end_line; } /* handle fixed subtitles inside bottom bar */ if(cropping_active && this->subs_detect) { if(this->height_limit_active && abs(this->stabilized_start_line - start_line) > 5) { /* reset height limit if top bar changes */ this->height_limit_active = 0; end_line = this->detected_end_line = detected_end_line; llprintf(LOG_DEBUG, "height limit reset, top bar moved from %d -> %d, bottom now %d\n", this->stabilized_start_line, start_line, end_line); } else if (this->detected_end_line > (this->prev_detected_end_line + 5)) { if(!this->height_limit_active || this->height_limit < this->detected_end_line) { /* start or increase height limit */ if (this->height_limit_active) llprintf(LOG_DEBUG, "height limit %d -> %d, prev bottom %d\n", this->height_limit, this->detected_end_line, this->prev_detected_end_line); else llprintf(LOG_DEBUG, "activate height limit %d, prev bottom %d\n", this->detected_end_line, this->prev_detected_end_line); this->height_limit = this->detected_end_line; this->height_limit_timer = this->subs_detect_lifetime; this->height_limit_active = 1; } else if(this->height_limit_active && this->height_limit_timer < (this->subs_detect_lifetime / 4)) { /* keep height limit timer running */ this->height_limit_timer = this->subs_detect_lifetime / 2; llprintf(LOG_DEBUG, "height_limit_timer increment bottom %d;%d -> %d\n", this->prev_detected_end_line, this->detected_end_line, detected_end_line); } } this->prev_detected_end_line = this->detected_end_line; } this->stabilized_start_line = start_line; this->stabilized_end_line = end_line; } /* update frame counter */ ++this->analyze_timer; /* "soft start" */ if(cropping_active && this->soft_start) { if(this->start_line != start_line) { int diff = this->start_line - start_line; if(diff < -this->soft_start_step) diff = -this->soft_start_step; else if(diff > this->soft_start_step) diff = this->soft_start_step; start_line = this->start_line - diff; } if(this->end_line != end_line) { int diff = this->end_line - end_line; if(diff < -this->soft_start_step) diff = -this->soft_start_step; else if(diff > this->soft_start_step) diff = this->soft_start_step; end_line = this->end_line - diff; } } this->prev_height = frame->height; this->prev_width = frame->width; if (cropping_active && (start_line != this->start_line || end_line != this->end_line)) { llprintf(LOG_DEBUG, "active start: %d -> %d, end %d -> %d\n", this->start_line, start_line, this->end_line, end_line); } if (this->cropping_active != cropping_active) llprintf(LOG_DEBUG, "draw: active %d -> %d\n", this->cropping_active, cropping_active); pthread_mutex_lock(&this->crop_lock); this->cropping_active = cropping_active; this->start_line = start_line; this->end_line = end_line; this->crop_total = start_line + frame->height - end_line; this->use_driver_crop = this->always_use_driver_crop || (frame->format != XINE_IMGFMT_YV12 && frame->format != XINE_IMGFMT_YUY2); pthread_mutex_unlock(&this->crop_lock); /* * do cropping * - using frame->crop_... does not seem to work with my xv and xine-lib-1.1.1. * - maybe cropping could be done by adjusting y/u/v data pointers * and height of frame ? * -> no time-consuming copying */ if(frame->bad_frame || !cropping_active || this->use_driver_crop) { _x_post_frame_copy_down(frame, frame->next); result = frame->next->draw(frame->next, stream); _x_post_frame_copy_up(frame, frame->next); } else if(frame->format == XINE_IMGFMT_YV12) result = crop_copy_yv12(frame, stream); else /*if(frame->format == XINE_IMGFMT_YUY2)*/ result = crop_copy_yuy2(frame, stream); return result; } static vo_frame_t *autocrop_get_frame(xine_video_port_t *port_gen, uint32_t width, uint32_t height, double ratio, int format, int flags) { post_video_port_t *port = (post_video_port_t *)port_gen; post_plugin_t *this_gen = port->post; autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)this_gen; int cropping_active = this->cropping_active; if (ratio <= 0.0) { if (height > 1) ratio = (double)width / (double)height; } /* Crop only SDTV 4:3 frames ... */ int intercept = ((format == XINE_IMGFMT_YV12 || format == XINE_IMGFMT_YUY2 || this->has_driver_crop) && FABS(ratio - 4.0/3.0) < 0.1 && width >= 240 && width <= 768 && height >= 288 && height <= 576); if(cropping_active && !intercept) { cropping_active = 0; llprintf(LOG_DEBUG, "get_frame: deactivate ratio: %lf width: %d height: %d\n", ratio, width, height); } /* reset when format changes */ if (cropping_active && this->autodetect && (height != this->prev_height || width != this->prev_width)) { cropping_active = 0; llprintf(LOG_DEBUG, "get_frame: deactivate width %d -> %d height %d -> %d\n", this->prev_width, width, this->prev_height, height); } /* set new ratio when using driver crop */ if (cropping_active && this->use_driver_crop) { if (this->autodetect) { int new_height = this->end_line - this->start_line; if (new_height > 1 && new_height != (int)height) ratio *= (double)height / (double)new_height; } else { ratio *= 4.0 / 3.0; } } _x_post_rewire(this_gen); vo_frame_t *frame = port->original_port->get_frame(port->original_port, width, height, ratio, format, flags); if (frame) { /* set cropping when using driver crop */ if (cropping_active && this->use_driver_crop) { if (this->autodetect) { frame->crop_top = this->start_line; frame->crop_bottom = (height - this->end_line); } else { frame->crop_top = frame->crop_bottom = height / 8; } } /* intercept frame for analysis and crop-by-copy */ if (intercept && format != XINE_IMGFMT_YV12 && format != XINE_IMGFMT_YUY2) { #ifdef HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA if (!frame->proc_provide_standard_frame_data) { #endif llprintf(LOG_DEBUG, "get_frame: deactivate because missing provide_standard_frame_data feature\n"); cropping_active = 0; intercept = 0; #ifdef HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA } #endif } if (intercept) { _x_post_inc_usage(port); frame = _x_post_intercept_video_frame(frame, port); } } this->cropping_active = cropping_active; return frame; } static int autocrop_intercept_ovl(post_video_port_t *port) { autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; if (!this->cropping_active) return 0; return 1; } static int32_t autocrop_overlay_add_event(video_overlay_manager_t *this_gen, void *event_gen) { post_video_port_t *port = _x_post_ovl_manager_to_port(this_gen); autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; video_overlay_event_t *event = (video_overlay_event_t *)event_gen; pthread_mutex_lock(&this->crop_lock); int cropping_active = this->cropping_active; int crop_total = this->crop_total; int use_driver_crop = this->use_driver_crop; int start_line = this->start_line; pthread_mutex_unlock(&this->crop_lock); if(cropping_active && crop_total>10) { if (event->event_type == OVERLAY_EVENT_SHOW #ifdef VO_CAP_CUSTOM_EXTENT_OVERLAY /* Do not move overlay if video_out has independent video and OSD resolutions */ && event->object.overlay && ( event->object.overlay->extent_width <= 0 || event->object.overlay->extent_height <= 0) #endif ) { switch (event->object.object_type) { case 0: /* regular subtitle */ /* Subtitle overlays must be coming somewhere inside xine engine */ if (use_driver_crop) { if(this->has_driver_crop) { if(!event->object.overlay->unscaled || !this->has_unscaled_overlay) { event->object.overlay->y -= crop_total; } } else { /* object is moved crop_top amount in video_out */ if(event->object.overlay->unscaled && this->has_unscaled_overlay) { /* cancel incorrect move that will be done in video_out */ event->object.overlay->y += start_line; } else { /* move crop_bottom pixels up */ event->object.overlay->y -= (crop_total - start_line); } } /* when using cropping overlays are moved in video_out */ llprintf(LOG_INFO, "autocrop_overlay_add_event: subtitle event untouched\n"); } else { /* when cropping here subtitles coming from inside of xine must be re-positioned */ if(!event->object.overlay->unscaled || !this->has_unscaled_overlay) { event->object.overlay->y -= crop_total; llprintf(LOG_INFO, "autocrop_overlay_add_event: subtitle event moved up\n"); } } break; case 1: /* menu overlay */ /* All overlays coming from VDR have this type */ { #ifdef DVDTEST int dvd_menu = 0; if (stream->input_plugin) { if (stream->input_plugin->get_capabilities (stream->input_plugin) & INPUT_CAP_SPULANG) { *((int *)lang) = 0; /* channel */ if (stream->input_plugin->get_optional_data (stream->input_plugin, lang, INPUT_OPTIONAL_DATA_SPULANG) == INPUT_OPTIONAL_SUCCESS) if(!strcmp(lang, "menu")) dvd_menu = 1; /* -> cropping off */ /* should turn on when not in menu ... -> where ? */ } } #endif if (use_driver_crop) { if(!event->object.overlay->unscaled || !this->has_unscaled_overlay) { event->object.overlay->y += start_line;//crop_total; } } } break; default:; } } } return port->original_manager->add_event(port->original_manager, event_gen); } static void autocrop_video_close(xine_video_port_t *port_gen, xine_stream_t *stream) { post_video_port_t *port = (post_video_port_t *)port_gen; autocrop_post_plugin_t *this = (autocrop_post_plugin_t *) port->post; if (this->cropping_active) { this->cropping_active = 0; llprintf(LOG_DEBUG, "deactivate because video close\n"); } port->original_port->close(port->original_port, stream); port->stream = NULL; _x_post_dec_usage(port); } /* * Parameter functions */ static xine_post_api_descr_t *autocrop_get_param_descr(void) { return &autocrop_param_descr; } static int autocrop_set_parameters(xine_post_t *this_gen, void *param_gen) { autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)this_gen; autocrop_parameters_t *param = (autocrop_parameters_t *)param_gen; this->autodetect = param->enable_autodetect; this->autodetect_rate = param->autodetect_rate; this->subs_detect = param->enable_subs_detect; this->subs_detect_lifetime = param->subs_detect_lifetime; this->subs_detect_stabilize_time = param->subs_detect_stabilize_time; this->soft_start = param->soft_start; this->soft_start_step = param->soft_start_step; this->stabilize = param->stabilize; this->stabilize_time = param->stabilize_time; this->always_use_driver_crop = param->use_driver_crop && this->has_driver_crop; this->logo_width = param->logo_width; this->use_avards_analysis = param->use_avards_analysis; this->overscan_compensate = param->overscan_compensate; this->bar_tone_tolerance = param->bar_tone_tolerance; this->debug_level = param->debug_level; llprintf(LOG_DEBUG, "autocrop_set_parameters: " "autodetect=%d autodetect_rate=%d logo_width=%d " "subs_detect=%d subs_detect_lifetime=%d subs_detect_stabilize_time=%d " "soft_start=%d soft_start_step=%d " "stabilize=%d stabilize_time=%d use_driver_crop=%d " "use_avards_analysis=%d overscan_compensate=%d bar_tone_tolerance=%d\n", this->autodetect, this->autodetect_rate, this->logo_width, this->subs_detect, this->subs_detect_lifetime, this->subs_detect_stabilize_time, this->soft_start, this->soft_start_step, this->stabilize, this->stabilize_time, this->always_use_driver_crop, this->use_avards_analysis, this->overscan_compensate, this->bar_tone_tolerance); return 1; } static int autocrop_get_parameters(xine_post_t *this_gen, void *param_gen) { autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)this_gen; autocrop_parameters_t *param = (autocrop_parameters_t *)param_gen; param->enable_autodetect = this->autodetect; param->autodetect_rate = this->autodetect_rate; param->enable_subs_detect = this->subs_detect; param->subs_detect_lifetime = this->subs_detect_lifetime; param->subs_detect_stabilize_time = this->subs_detect_stabilize_time; param->soft_start = this->soft_start; param->soft_start_step = this->soft_start_step; param->stabilize = this->stabilize; param->stabilize_time = this->stabilize_time; param->use_driver_crop = this->always_use_driver_crop; param->logo_width = this->logo_width; param->use_avards_analysis = this->use_avards_analysis; param->overscan_compensate = this->overscan_compensate; param->bar_tone_tolerance = this->bar_tone_tolerance; param->debug_level = this->debug_level; llprintf(LOG_DEBUG, "autocrop_get_parameters: " "autodetect=%d autodetect_rate=%d logo_width=%d " "subs_detect=%d subs_detect_lifetime=%d subs_detect_stabilize_time=%d " "soft_start=%d soft_start_step=%d " "stabilize=%d stabilize_time=%d use_driver_crop=%d " "use_avards_analysis=%d overscan_compensate=%d bar_tone_tolerance=%d\n", this->autodetect, this->autodetect_rate, this->logo_width, this->subs_detect, this->subs_detect_lifetime, this->subs_detect_stabilize_time, this->soft_start, this->soft_start_step, this->stabilize, this->stabilize_time, this->always_use_driver_crop, this->use_avards_analysis, this->overscan_compensate, this->bar_tone_tolerance); return 1; } static char *autocrop_get_help(void) { return _("The autocrop plugin is meant to take 4:3 letterboxed frames and " "convert them to 16:9 by removing black bars on the top and bottom " "of the frame.\n" "\n" "Parameters\n" " enable_autodetect: Enable automatic letterbox detection\n" " autodetect_rate: Rate of automatic letterbox detection\n" " stabilize_time: Number of frames with equal bars before cropping value changes\n" " enable_subs_detect: Enable automatic subtitle detection inside bottom bar\n" " subs_detect_lifetime: Lifetime of automatic subtitle detection\n" " subs_detect_stabilize_time: Stabilize time of automatic subtitle detection\n" " soft_start: Enable soft start of cropping\n" " soft_start_step: Soft start step width of cropping\n" " stabilize: Stabilize cropping to 14:9, 16:9, (16:9+subs), 20:9, (20:9+subs)\n" " use_driver_crop: Always use video driver crop\n" " logo_width: Width of logo (percentage of frame width) for automatic logo detection\n" " use_avards_analysis: Use AVARDS algorithm for frame analysis\n" " overscan_compensate: Compensation of output device overscan applied when cropping frames (%1000 of frame height)\n" " bar_tone_tolerance: Tolerance of bar color (avards only)" " debug_level: Output debug messages. 0=none, 1=info, 2=debug, 3=trace.\n" "\n" ); } /* * Open/close */ static void autocrop_dispose(post_plugin_t *this_gen) { if (_x_post_dispose(this_gen)) { autocrop_post_plugin_t *this = (autocrop_post_plugin_t *) this_gen; pthread_mutex_destroy(&this->crop_lock); free(this->img); free(this); } } static post_plugin_t *autocrop_open_plugin(post_class_t *class_gen, int inputs, xine_audio_port_t **audio_target, xine_video_port_t **video_target) { if (video_target && video_target[ 0 ]) { autocrop_post_plugin_t *this = calloc(1, sizeof(autocrop_post_plugin_t)); post_in_t *input; post_out_t *output; post_video_port_t *port; xine_post_in_t *input_param; static xine_post_api_t post_api = { autocrop_set_parameters, autocrop_get_parameters, autocrop_get_param_descr, autocrop_get_help }; if (this) { _x_post_init(&this->post_plugin, 0, 1); port = _x_post_intercept_video_port(&this->post_plugin, video_target[ 0 ], &input, &output); input->xine_in.name = "video in"; output->xine_out.name = "video out"; port->intercept_ovl = autocrop_intercept_ovl; port->new_manager->add_event = autocrop_overlay_add_event; port->new_port.get_frame = autocrop_get_frame; port->new_port.close = autocrop_video_close; port->new_frame->draw = autocrop_draw; this->post_plugin.xine_post.video_input[ 0 ] = &port->new_port; this->post_plugin.dispose = autocrop_dispose; input_param = &this->parameter_input; input_param->name = "parameters"; input_param->type = XINE_POST_DATA_PARAMETERS; input_param->data = &post_api; #if XINE_VERSION_CODE >= 10102 xine_list_push_back(this->post_plugin.input, input_param); #else xine_list_append_content(this->post_plugin.input, input_param); #endif this->autodetect = 1; this->autodetect_rate = DEFAULT_AUTODETECT_RATE; this->stabilize_time = DEFAULT_STABILIZE_TIME; this->subs_detect = 1; this->subs_detect_lifetime = DEFAULT_SUBS_DETECT_LIFETIME; this->subs_detect_stabilize_time = DEFAULT_SUBS_DETECT_STABILIZE_TIME; this->soft_start = 1; this->soft_start_step = DEFAULT_SOFT_START_STEP; this->stabilize = 1; this->logo_width = DEFAULT_LOGO_WIDTH; this->use_avards_analysis = 0; this->overscan_compensate = DEFAULT_OVERSCAN_COMPENSATE; this->bar_tone_tolerance = DEFAULT_BAR_TONE_TOLERANCE; this->debug_level = 1; uint64_t caps = port->original_port->get_capabilities(port->original_port); this->has_driver_crop = caps & VO_CAP_CROP; this->has_unscaled_overlay = caps & VO_CAP_UNSCALED_OVERLAY; pthread_mutex_init(&this->crop_lock, NULL); return &this->post_plugin; } } return NULL; } /* * Plugin class */ #if POST_PLUGIN_IFACE_VERSION < 10 static char *autocrop_get_identifier(post_class_t *class_gen) { return "autocrop"; } static char *autocrop_get_description(post_class_t *class_gen) { return "Crop letterboxed 4:3 video to 16:9"; } static void autocrop_class_dispose(post_class_t *class_gen) { free(class_gen); } #endif static void *autocrop_init_plugin(xine_t *xine, void *data) { post_class_t *class = calloc(1, sizeof(post_class_t)); if(class) { class->open_plugin = autocrop_open_plugin; #if POST_PLUGIN_IFACE_VERSION < 10 class->get_identifier = autocrop_get_identifier; class->get_description = autocrop_get_description; class->dispose = autocrop_class_dispose; #else class->identifier = "autocrop"; class->description = N_("Crop letterboxed 4:3 video to 16:9"); class->dispose = default_post_class_dispose; #endif } return class; } static post_info_t info = { XINE_POST_TYPE_VIDEO_FILTER }; const plugin_info_t xine_plugin_info[] __attribute__((visibility("default"))) = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_POST, POST_PLUGIN_IFACE_VERSION, "autocrop", XINE_VERSION_CODE, &info, &autocrop_init_plugin }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } }; xineliboutput-2.0.0/xine_post_audiochannel.c0000644000175000017500000002126513061253352017120 0ustar phph/* * xine_post_audiochannel.c: xine post plugin * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ /* * Copyright (C) 2006 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-1301 USA * * $Id: * * audio channel selection post plugin by Petri Hintukainen 01/09/2006 * based on upmix.c * * Removes left or right channel from stereo audio track * and fills both channels with same data. * This is useful with some bi-lingual DVB transmissions where * two different languages are sent on same (stereo) audio track. * */ #include #include typedef struct audioch_parameters_s { int channel; } audioch_parameters_t; START_PARAM_DESCR(audioch_parameters_t) PARAM_ITEM(POST_PARAM_TYPE_BOOL, channel, NULL, 0, 1, 0, "select channel (0=left, 1=right)") END_PARAM_DESCR(audioch_param_descr) typedef struct audioch_post_plugin_s { post_plugin_t post_plugin; xine_post_in_t parameter_input; /* Config */ int channel; /* 0 - left, 1 - right */ /* Data */ int channels; } audioch_post_plugin_t; /* * Port functions */ #if 1 static int audioch_port_open(xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bits, uint32_t rate, int mode) #else static int audioch_port_open(xine_audio_port_t *port_gen, xine_stream_t *stream, ao_format_t format) #endif { post_audio_port_t *port = (post_audio_port_t *)port_gen; audioch_post_plugin_t *this = (audioch_post_plugin_t *)port->post; _x_post_rewire(&this->post_plugin); _x_post_inc_usage(port); port->stream = stream; #if 1 port->bits = bits; port->rate = rate; port->mode = mode; this->channels = _x_ao_mode2channels(mode); return port->original_port->open(port->original_port, stream, bits, rate, mode ); #else port->format = format; this->num_channels = _x_ao_mode2channels(format.mode); return port->original_port->open(port->original_port, stream, format); #endif } static void audioch_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; audioch_post_plugin_t *this = (audioch_post_plugin_t *)port->post; int i; if(this->channels == 2) { #if 1 int step = buf->format.bits / 8; #else int step = sample_bytes_table[buf->format.sample_format]; #endif audio_buffer_t *newbuf = port->original_port->get_buffer(port->original_port); newbuf->num_frames = buf->num_frames; newbuf->vpts = buf->vpts; newbuf->frame_header_count = buf->frame_header_count; newbuf->first_access_unit = buf->first_access_unit; #if 1 newbuf->format.bits = buf->format.bits; newbuf->format.rate = buf->format.rate; newbuf->format.mode = buf->format.mode; #else newbuf->format = buf->format; #endif _x_extra_info_merge( newbuf->extra_info, buf->extra_info); switch(step) { case 1: for(i=0; inum_frames; i++) newbuf->mem[i*2+1] = newbuf->mem[i*2] = buf->mem[i*2+this->channel]; break; case 2: for(i=0; inum_frames; i++) ((uint16_t*)newbuf->mem)[i*2+1] = ((uint16_t*)newbuf->mem)[i*2] = ((uint16_t*)buf->mem)[i*2+this->channel]; break; case 3: for(i=0; inum_frames*3; i+=3) { newbuf->mem[i*2+0] = newbuf->mem[i*2+3] = buf->mem[i*2+0+3*this->channel]; newbuf->mem[i*2+1] = newbuf->mem[i*2+4] = buf->mem[i*2+1+3*this->channel]; newbuf->mem[i*2+2] = newbuf->mem[i*2+5] = buf->mem[i*2+2+3*this->channel]; } break; case 4: for(i=0; inum_frames; i++) ((uint32_t*)newbuf->mem)[i*2+1] = ((uint32_t*)newbuf->mem)[i*2] = ((uint32_t*)buf->mem)[i*2+this->channel]; break; default: /* ??? */ memcpy(newbuf->mem, buf->mem, buf->num_frames*2*buf->format.bits); break; } /* pass data to original port */ port->original_port->put_buffer(port->original_port, newbuf, 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 ); } /* * Parameter functions */ static xine_post_api_descr_t *audioch_get_param_descr(void) { return &audioch_param_descr; } static int audioch_set_parameters(xine_post_t *this_gen, void *param_gen) { audioch_post_plugin_t *this = (audioch_post_plugin_t *)this_gen; audioch_parameters_t *param = (audioch_parameters_t *)param_gen; this->channel = param->channel; return 1; } static int audioch_get_parameters(xine_post_t *this_gen, void *param_gen) { audioch_post_plugin_t *this = (audioch_post_plugin_t *)this_gen; audioch_parameters_t *param = (audioch_parameters_t *)param_gen; param->channel = this->channel; return 1; } static char *audioch_get_help(void) { return _("The audiochannel plugin is meant to take stereo audio and \n" "remove left or right channel by replacing both channels \n" "with the same data. \n" "\n" "Parameters \n" " channel: Select channel (left=0, right=1) \n" "\n" ); } /* * Open / Close */ static void audioch_dispose(post_plugin_t *this_gen) { if (_x_post_dispose(this_gen)) free(this_gen); } static post_plugin_t *audioch_open_plugin(post_class_t *class_gen, int inputs, xine_audio_port_t **audio_target, xine_video_port_t **video_target) { audioch_post_plugin_t *this = calloc(1, sizeof(audioch_post_plugin_t)); post_in_t *input; post_out_t *output; post_audio_port_t *port; xine_post_in_t *input_param; static xine_post_api_t post_api = { audioch_set_parameters, audioch_get_parameters, audioch_get_param_descr, audioch_get_help }; if (!this || !audio_target || !audio_target[0] ) { free(this); return NULL; } _x_post_init(&this->post_plugin, 1, 0); port = _x_post_intercept_audio_port(&this->post_plugin, audio_target[ 0 ], &input, &output); port->new_port.open = audioch_port_open; port->new_port.put_buffer = audioch_port_put_buffer; input->xine_in.name = "audio in"; output->xine_out.name = "audio out"; this->post_plugin.xine_post.audio_input[ 0 ] = &port->new_port; this->post_plugin.dispose = audioch_dispose; input_param = &this->parameter_input; input_param->name = "parameters"; input_param->type = XINE_POST_DATA_PARAMETERS; input_param->data = &post_api; #if XINE_VERSION_CODE >= 10102 xine_list_push_back(this->post_plugin.input, input_param); #else xine_list_append_content(this->post_plugin.input, input_param); #endif this->channel = 0; this->channels = 0; return &this->post_plugin; } /* * Plugin class */ #if POST_PLUGIN_IFACE_VERSION < 10 static char *audioch_get_identifier(post_class_t *class_gen) { return "audiochannel"; } static char *audioch_get_description(post_class_t *class_gen) { return "Select audio channel"; } static void audioch_class_dispose(post_class_t *class_gen) { free(class_gen); } #endif static void *audioch_init_plugin(xine_t *xine, void *data) { post_class_t *class = calloc(1, sizeof(post_class_t)); if(!class) return NULL; class->open_plugin = audioch_open_plugin; #if POST_PLUGIN_IFACE_VERSION < 10 class->get_identifier = audioch_get_identifier; class->get_description = audioch_get_description; class->dispose = audioch_class_dispose; #else class->identifier = "audiochannel"; class->description = N_("Select audio channel"); class->dispose = default_post_class_dispose; #endif return class; } static post_info_t audioch_info = { XINE_POST_TYPE_AUDIO_FILTER }; #ifndef NO_INFO_EXPORT plugin_info_t xine_plugin_info[] __attribute__((visibility("default"))) = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_POST, POST_PLUGIN_IFACE_VERSION, "audiochannel", XINE_VERSION_CODE, &audioch_info, &audioch_init_plugin }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } }; #endif xineliboutput-2.0.0/xine_input_vdr_net.h0000644000175000017500000000773313061253352016312 0ustar phph/* * xine_input_vdr_net.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINE_INPUT_VDR_NET_H_ #define __XINE_INPUT_VDR_NET_H_ #include #if defined(__APPLE__) || defined(__FreeBSD__) # include #else # include #endif #ifndef PACKED # define PACKED __attribute__((packed)) #endif #include "tools/rtp.h" /* generic RTP headers */ /* * Default port(s) */ #ifndef DEFAULT_VDR_PORT # define DEFAULT_VDR_PORT 37890 #endif /* * Byte-order conversions */ #if __BYTE_ORDER == __BIG_ENDIAN #elif __BYTE_ORDER == __LITTLE_ENDIAN #else # error __BYTE_ORDER not defined ! #endif #if __BYTE_ORDER == __BIG_ENDIAN # define ntohll(val) (val) # define htonll(val) (val) # define ntohull(val) (val) # define htonull(val) (val) #else # define ntohll(val) ((int64_t)ntohull((uint64_t)val)) # define htonll(val) ((int64_t)htonull((uint64_t)val)) # define ntohull(val) \ ((uint64_t) ntohl((uint32_t)((val) >> 32)) | \ (uint64_t) ntohl((uint32_t)(val)) << 32) # define htonull(val) \ ((uint64_t) htonl((uint32_t)((val) >> 32)) | \ (uint64_t) htonl((uint32_t)(val)) << 32) #endif /* * Substreams */ enum eStreamId { sidVdr = 0, /* VDR primary video/audio (MPEG-PES or MPEG-TS) */ sidPipFirst = 1, /* VDR PIP video, first (MPEG-TS PAT+PMT+video) */ sidPipLast = 17, sidPadding = 0xfd, /* UDP/RTP padding */ sidOsd = 0xfe, /* OSD */ sidControl = 0xff, /* control messages */ }; /* * Network packet headers */ #if defined __cplusplus extern "C" { #endif /* * TCP / PIPE */ typedef struct { uint64_t pos; /* stream position of first byte */ uint32_t len; /* length of following PES packet */ uint8_t stream; uint8_t payload[0]; } PACKED stream_tcp_header_t; #define TCP_PAYLOAD(pkt) ((uint8_t*)(pkt)+sizeof(stream_tcp_header_t)) /* * UDP */ typedef struct { uint64_t pos; /* stream position of first byte */ /* -1ULL and first bytes of frame != 00 00 01 */ /* --> embedded control stream data */ uint16_t seq; /* packet sequence number (for re-ordering and detecting missing packets) */ uint8_t stream; uint8_t payload[0]; } PACKED stream_udp_header_t; #define UDP_SEQ_MASK 0xff #define UDP_PAYLOAD(pkt) ((uint8_t*)(pkt)+sizeof(stream_udp_header_t)) /* * RTP */ /* xineliboutput RTP header extension */ typedef struct { stream_rtp_header_ext_t hdr; union { uint8_t raw[12]; /* 3 DWORDs */ uint32_t rawd[3]; union { struct { uint8_t padding0; /* must be padded to full DWORDs */ stream_udp_header_t udphdr; } PACKED; struct { uint8_t padding1; /* must be padded to full DWORDs */ uint64_t pos; uint16_t seq; uint8_t stream; } PACKED; } PACKED; } PACKED; uint8_t payload[0]; } PACKED stream_rtp_header_ext_x_t; /* xineliboutput RTP header */ typedef struct stream_rtp_header_impl { stream_rtp_header_t rtp_hdr; stream_rtp_header_ext_x_t hdr_ext; uint8_t payload[0]; } PACKED stream_rtp_header_impl_t; #define RTP_VERSION 2 #define RTP_MARKER_BIT 0x80 #define RTP_HDREXT_BIT 0x10 #define RTP_PAYLOAD_TYPE_PES 96 /* application */ #define RTP_PAYLOAD_TYPE_TS 33 /* MPEG-TS */ #define RTP_VERSION_BYTE (RTP_VERSION<<6) #define RTP_PAYLOAD_TYPE_PES_M (RTP_PAYLOAD_TYPE_PES|RTP_MARKER_BIT) #define RTP_PAYLOAD_TYPE_TS_M (RTP_PAYLOAD_TYPE_TS |RTP_MARKER_BIT) #define RTP_HEADER_EXT_X_SIZE 3 /* dwords, not counting stream_rtp_header_ext_t */ #define RTP_HEADER_EXT_X_TYPE 0x54d3 #define RTP_PAYLOAD(pkt) ((uint8_t*)(pkt)+sizeof(stream_rtp_header_impl_t)) /* access UDP header inside RTP header extension */ #define RTP_UDP_PAYLOAD(pkt) (RTP_PAYLOAD(pkt)-sizeof(stream_udp_header_t)) #if defined __cplusplus } #endif #endif /*__XINE_INPUT_VDR_NET_H_*/ xineliboutput-2.0.0/xine_input_vdr_mrl.h0000644000175000017500000000050413061253352016303 0ustar phph/* * xine_input_vdr_mrl.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINE_INPUT_VDR_MRL_H #define XINE_INPUT_VDR_MRL_H # ifndef MRL_ID # define MRL_ID "xvdr" # undef MRL_ID_LEN # define MRL_ID_LEN 4 # endif #endif xineliboutput-2.0.0/xine_input_vdr.h0000644000175000017500000000326213061253352015435 0ustar phph/* * xine_input_vdr.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINE_INPUT_VDR_H_ #define __XINE_INPUT_VDR_H_ #if defined __cplusplus extern "C" { #endif #include #include "xine_input_vdr_mrl.h" struct input_plugin_s; struct vdr_input_plugin_if_s; struct osd_command_s; struct frontend_s; typedef struct vdr_input_plugin_funcs_s { /* VDR --> input plugin (only local mode) */ int (*push_input_write) (struct vdr_input_plugin_if_s *, int, uint64_t, const char *, int); int (*push_input_control)(struct vdr_input_plugin_if_s *, const char *); int (*push_input_osd) (struct vdr_input_plugin_if_s *, struct osd_command_s *); /* input plugin --> frontend (only local mode) */ void (*xine_input_event) (struct frontend_s *, const char *, const char *); /* input plugin --> frontend (remote mode) */ int (*intercept_osd) (struct frontend_s *, struct osd_command_s *); /* input plugin --> frontend */ void *(*fe_control) (struct frontend_s *, const char *); struct frontend_s *fe_handle; /* frontend --> input plugin (remote mode) */ int (*post_vdr_event) (struct vdr_input_plugin_if_s *, const char *); } vdr_input_plugin_funcs_t; typedef struct vdr_input_plugin_if_s { input_plugin_t input_plugin; vdr_input_plugin_funcs_t f; } vdr_input_plugin_if_t; #define CONTROL_OK 0 #define CONTROL_UNKNOWN -1 #define CONTROL_PARAM_ERROR -2 #define CONTROL_DISCONNECTED -3 typedef struct grab_data_s { size_t size; char *data; } grab_data_t; #if defined __cplusplus } #endif #endif /*__XINE_INPUT_VDR_H_*/ xineliboutput-2.0.0/xine_input_vdr.c0000644000175000017500000055110413061253352015433 0ustar phph/* * xine_input_vdr.c: xine VDR input plugin * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define XINE_ENGINE_INTERNAL #ifndef _GNU_SOURCE # define _GNU_SOURCE /* asprintf */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* setpriority() */ #include #include #include #if !defined(__APPLE__) && !defined(__FreeBSD__) # define DVD_STREAMING_SPEED #endif #ifdef DVD_STREAMING_SPEED # include # include #endif #include #include #include #include #include #include #include #ifndef XINE_VERSION_CODE # error XINE_VERSION_CODE undefined ! #endif #if XINE_VERSION_CODE >= 10190 && XINE_VERSION_CODE < 10207 # include "features.h" # ifdef HAVE_LIBAVUTIL # include # else # error "plugin was configured without libavutil. It can't be compiled against xine-lib 1.2 !" # endif #endif #include "xine/adjustable_scr.h" #include "xine/osd_manager.h" #include "xine/xvdr_metronom.h" #include "xine_input_vdr.h" #include "xine_input_vdr_net.h" #include "tools/osd_command.h" #include "tools/mpeg.h" #include "tools/pes.h" #include "tools/ts.h" #include "tools/rle.h" /***************************** DEFINES *********************************/ /*#define LOG_UDP*/ /*#define LOG_OSD*/ /*#define LOG_CMD*/ /*#define LOG_SCR*/ /*#define LOG_SCR_BUF_LEVEL_METER*/ /*#define LOG_TRACE*/ /*#define LOG_GRAPH*/ /*#define DEBUG_LOCKING*/ #define METRONOM_PREBUFFER_VAL (4 * 90000 / 25 ) #define HD_BUF_NUM_BUFS (2500) /* 2k payload * 2500 = 5MB */ #define HD_BUF_ELEM_SIZE (2048+64) #define RADIO_MAX_BUFFERS 10 #define SLAVE_VIDEO_FIFO_SIZE 1000 #ifndef NOSIGNAL_IMAGE_FILE # define NOSIGNAL_IMAGE_FILE "/usr/share/vdr/xineliboutput/nosignal.mpv" #endif #ifndef NOSIGNAL_MAX_SIZE # define NOSIGNAL_MAX_SIZE 0x10000 /* 64k */ #endif /* Note: I tried to set speed to something very small instead of full pause when pausing SCR but it didn't work in all systems. TEST_SCR_PAUSE replaces this by adding delay before stream is paused (pause is triggered by first received PES containing PTS). This should allow immediate processing of still frames and let video_out run in paused_loop when there is gap in feed (ex. channel can't be decrypted). Not running video_out in paused_loop caused very long delays in OSD updating in some setups. */ #define TEST_SCR_PAUSE /* picture-in-picture support */ /*#define TEST_PIP 1*/ #define CONTROL_BUF_BASE ( 0x0f000000) /* 0x0f000000 */ #define CONTROL_BUF_BLANK (CONTROL_BUF_BASE|0x00010000) /* 0x0f010000 */ #define CONTROL_BUF_CLEAR (CONTROL_BUF_BASE|0x00020000) /* 0x0f020000 */ #define BUF_NETWORK_BLOCK (BUF_DEMUX_BLOCK |0x00010000) /* 0x05010000 */ #define BUF_LOCAL_BLOCK (BUF_DEMUX_BLOCK |0x00020000) /* 0x05020000 */ typedef struct { uint64_t pos; uint8_t payload[0]; } stream_local_header_t; #define SPU_CHANNEL_NONE (-2) #define SPU_CHANNEL_AUTO (-1) /******************************* LOG ***********************************/ #if !defined(__APPLE__) && !defined(__FreeBSD__) # include /* syscall(__NR_gettid) */ #endif static const char module_revision[] = "$Id$"; static const char log_module_input_vdr[] = "[input_vdr] "; #define LOG_MODULENAME log_module_input_vdr #define SysLogLevel iSysLogLevel #include "logdefs.h" int iSysLogLevel = 1; /* 0:none, 1:errors, 2:info, 3:debug */ int bLogToSysLog = 0; int bSymbolsFound = 0; void x_syslog(int level, const char *module, const char *fmt, ...) { va_list argp; char buf[512]; va_start(argp, fmt); vsnprintf(buf, sizeof(buf), fmt, argp); buf[sizeof(buf)-1] = 0; #if defined(__APPLE__) || defined(__FreeBSD__) if(!bLogToSysLog) { fprintf(stderr, "%s%s\n", module, buf); } else { syslog(level, "%s%s", module, buf); } #else if(!bLogToSysLog) { fprintf(stderr,"[%ld] %s%s\n", syscall(__NR_gettid), module, buf); } else { syslog(level, "[%ld] %s%s", syscall(__NR_gettid), module, buf); } #endif va_end(argp); } static void SetupLogLevel(void) { void *lib = NULL; if( !(lib = dlopen (NULL, RTLD_LAZY | RTLD_GLOBAL))) { LOGERR("Can't dlopen self: %s", dlerror()); } else { int *pLogToSyslog = (int*)dlsym(lib, "LogToSysLog"); int *pSysLogLevel = (int*)dlsym(lib, "SysLogLevel"); bLogToSysLog = pLogToSyslog && *pLogToSyslog; iSysLogLevel = pSysLogLevel ? (*pSysLogLevel) : iSysLogLevel; LOGDBG("Symbol SysLogLevel %s : value %d", pSysLogLevel ? "found" : "not found", iSysLogLevel); LOGDBG("Symbol LogToSysLog %s : value %s", pLogToSyslog ? "found" : "not found", bLogToSysLog ? "yes" : "no"); bSymbolsFound = pSysLogLevel && pLogToSyslog; dlclose(lib); } } #ifdef LOG_SCR # define LOGSCR(x...) LOGMSG("SCR: " x) # define LOGSCR_VERBOSE(x...) LOGVERBOSE("SCR: " x) #else # define LOGSCR(x...) # define LOGSCR_VERBOSE(x...) #endif #ifdef LOG_OSD # define LOGOSD(x...) LOGMSG("OSD: " x) #else # define LOGOSD(x...) #endif #ifdef LOG_UDP # define LOGUDP(x...) LOGMSG("UDP:" x) #else # define LOGUDP(x...) #endif #ifdef LOG_CMD # define LOGCMD(x...) LOGMSG("CMD:" x) #else # define LOGCMD(x...) #endif #ifdef LOG_TRACE # undef TRACE # define TRACE(x...) printf(x) #else # undef TRACE # define TRACE(x...) #endif #ifdef DEBUG_LOCKING # include "tools/debug_mutex.h" #endif #ifndef MIN # define MIN(a,b) ((a)<(b)?(a):(b)) #endif #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) #endif #ifdef LOG_GRAPH static void log_graph(int val, int symb) { static char headr[] = "|<- 0 100% ->|"; static char meter[sizeof(headr)]; if (!symb || symb == 1) { time_t t; struct tm *tm; time(&t); tm = localtime(&t); printf("%02d:%02d:%02d %s", tm->tm_hour, tm->tm_min, tm->tm_sec, symb ? meter : headr); memset(meter, ' ', sizeof(headr) - 1); return; } val = MIN(val, (int)sizeof(headr) - 2); val = MAX(val, 0); #if 0 if (symb == ':') { meter[val] = meter[val] == '%' ? '#' : symb; } else if (symb == '*') { meter[val] = meter[val] == '%' ? '1' : meter[val] == ':' ? '2' : meter[val] == '#' ? '3' : symb; } else { meter[val] = symb; } #else meter[val] = symb; #endif } #endif /******************************* DATA ***********************************/ #define KILOBYTE(x) (1024 * (x)) typedef struct udp_data_s udp_data_t; typedef struct slave_stream_s slave_stream_t; /* plugin class */ typedef struct vdr_input_class_s { input_class_t input_class; xine_t *xine; char *mrls[ 2 ]; int fast_osd_scaling; int smooth_scr_tuning; double scr_tuning_step; int num_buffers_hd; unsigned scr_treshold_sd; unsigned scr_treshold_hd; } vdr_input_class_t; struct slave_stream_s { xine_stream_t *stream; xine_event_queue_t *event_queue; }; /* input plugin */ typedef struct vdr_input_plugin_s { union { vdr_input_plugin_if_t iface; struct { input_plugin_t input_plugin; vdr_input_plugin_funcs_t funcs; }; }; /* plugin */ vdr_input_class_t *class; xine_stream_t *stream; xine_event_queue_t *event_queue; osd_manager_t *osd_manager; char *mrl; xine_stream_t *pip_stream; /* Sync */ pthread_mutex_t lock; pthread_mutex_t vdr_entry_lock; pthread_cond_t engine_flushed; /* Playback */ uint8_t read_timeouts; /* number of timeouts in read_block */ uint8_t write_overflows; uint8_t no_video : 1; uint8_t live_mode : 1; uint8_t still_mode : 1; uint8_t stream_start : 1; uint8_t hd_stream : 1; /* true if current stream is HD */ uint8_t sw_volume_control : 1; uint8_t config_ok : 1; /* metronom */ xvdr_metronom_t *metronom; /* SCR */ adjustable_scr_t *scr; int16_t scr_tuning; uint8_t fixed_scr : 1; uint8_t scr_live_sync : 1; uint8_t is_paused : 1; uint8_t is_trickspeed : 1; struct { /* buffer level data for scr tuning algorithm */ unsigned cnt; unsigned fill_avg; unsigned fill_min; unsigned fill_max; } scr_buf; unsigned I_frames; /* amount of I-frames passed to demux */ unsigned B_frames; unsigned P_frames; /* Network */ pthread_t control_thread; pthread_mutex_t fd_control_lock; buf_element_t *read_buffer; uint8_t threads_initialized; uint8_t tcp, udp, rtp; volatile int control_running; volatile int fd_data; volatile int fd_control; udp_data_t *udp_data; int client_id; int token; /* buffer */ fifo_buffer_t *block_buffer; /* blocks to be demuxed */ fifo_buffer_t *buffer_pool; /* stream's video fifo */ fifo_buffer_t *hd_buffer; /* more buffer for HD streams */ uint64_t discard_index; /* index of next byte to feed to demux; all data before this offset will be discarded */ uint64_t discard_index_ds; unsigned discard_frame; uint64_t guard_index; /* data before this offset will not be discarded */ unsigned guard_frame; uint64_t curpos; /* current position (demux side) */ unsigned curframe; unsigned reserved_buffers; /* media player */ slave_stream_t slave; /* slave stream (media player) data */ slave_stream_t bg_stream; /* background image stream data */ int autoplay_size; /* size of slave stream autoplaylist (ex. CD tracks) */ uint8_t loop_play : 1; uint8_t dvd_menu : 1; /* saved video properties */ uint8_t video_properties_saved; int orig_hue; int orig_brightness; int orig_saturation; int orig_sharpness; int orig_noise_reduction; int orig_contrast; int orig_vo_aspect_ratio; } vdr_input_plugin_t; /***************************** UDP DATA *********************************/ struct udp_data_s { /* Server address (used to validate incoming packets) */ struct sockaddr_in server_address; uint32_t ssrc; /* receiving queue for re-ordering and re-transmissions */ buf_element_t *queue[UDP_SEQ_MASK+1]; uint64_t queue_input_pos; /* stream position of next incoming byte */ uint16_t queued; /* count of frames in queue */ uint16_t next_seq; /* expected sequence number of next incoming packet */ uint16_t current_seq; /* sequence number of last received packet */ uint8_t is_padding; /* true, if last received packet was padding packet */ /* missing frames ratio statistics */ int16_t missed_frames; int16_t received_frames; /* SCR adjust */ uint8_t scr_jump_done; int resend_requested; }; /* UDP sequence number handling */ #define NEXTSEQ(x) ((x + 1) & UDP_SEQ_MASK) #define PREVSEQ(x) ((x + UDP_SEQ_MASK) & UDP_SEQ_MASK) #define INCSEQ(x) (x = NEXTSEQ(x)) #define ADDSEQ(x,n) ((x + UDP_SEQ_MASK + 1 + n) & UDP_SEQ_MASK) static udp_data_t *init_udp_data(void) { udp_data_t *data = calloc(1, sizeof(udp_data_t)); data->received_frames = -1; return data; } static void free_udp_data(udp_data_t *data) { int i; for (i = 0; i <= UDP_SEQ_MASK; i++) if (data->queue[i]) { data->queue[i]->free_buffer(data->queue[i]); data->queue[i] = NULL; } free(data); } /********************* cancellable mutex locking *************************/ /* * mutex cleanup() * * Unlock mutex. Used as thread cleanup handler. */ static void mutex_cleanup(void *arg) { pthread_mutex_unlock((pthread_mutex_t *)arg); } /* * mutex_lock_cancellable() / mutex_unlock_cancellable() * * mutex lock/unlock for cancellable sections * * - do not enter protected section if locking fails * - unlock mutex if thread is cancelled while holding the lock * * - lock/unlock must be used pairwise within the same lexical scope ! * */ #define mutex_lock_cancellable(mutex) \ if (pthread_mutex_lock(mutex)) { \ LOGERR("pthread_mutex_lock (%s) failed, skipping locked block !", #mutex); \ } else { \ pthread_cleanup_push(mutex_cleanup, (void*) mutex); #define mutex_unlock_cancellable(mutex) \ if (pthread_mutex_unlock(mutex)) \ LOGERR("pthread_mutex_unlock (%s) failed !", #mutex); \ pthread_cleanup_pop(0); \ } /****************************** DEBUG **********************************/ #define CHECK_LOCKED(lock) \ if (!pthread_mutex_trylock(&lock)) { \ LOGMSG("%s: assertion failed: lock %s unlocked !", __PRETTY_FUNCTION__, #lock); \ pthread_mutex_unlock(&lock); \ return; \ } #define CHECK_FALSE(flag) \ if (flag) { \ LOGMSG("%s: assertion failed: %s is true !", __PRETTY_FUNCTION__, #flag); \ return; \ } /******************************* SCR ***********************************/ /* * SCR fine tuning * * fine tuning is used to change playback speed in live mode * to keep in sync with mpeg source * */ #define SCR_TUNING_PAUSED -10000 #define SCR_TUNING_OFF 0 #ifdef LOG_SCR static inline const char *scr_tuning_str(int value) { switch (value) { case 2: return "SCR +2"; case 1: return "SCR +1"; case SCR_TUNING_OFF: return "SCR +0"; case -1: return "SCR -1"; case -2: return "SCR -2"; case SCR_TUNING_PAUSED: return "SCR PAUSED"; default: break; } return "ERROR"; } #endif #ifdef LOG_SCR static void log_buffer_fill(vdr_input_plugin_t *this, int num_used, int num_free, int num_vid) { /* * Trace current buffer and tuning status */ #ifndef LOG_SCR_BUF_LEVEL_METER static int cnt = 0; if ( ! ((cnt++) % 2500) || (this->scr_tuning == SCR_TUNING_PAUSED && !(cnt%20)) || (this->no_video && !(cnt%50))) { LOGSCR("Buffer %2d%% (%3d/%3d) %12s frames %d", 100 * num_used / (num_used + num_free), num_used, num_used + num_free, scr_tuning_str(this->scr_tuning), num_vid); } #else printf("Buffer %2d%% (%3d/%3d) %20s frames: %3d \r", 100 * num_used / (num_used + num_free), num_used, num_used + num_free, scr_tuning_str(this->scr_tuning), num_vid); #endif if (this->scr_tuning == SCR_TUNING_PAUSED) { if (_x_get_fine_speed(this->stream) != XINE_SPEED_PAUSE) { LOGMSG("ERROR: SCR PAUSED ; speed=%d bool=%d", _x_get_fine_speed(this->stream), (int)_x_get_fine_speed(this->stream) == XINE_SPEED_PAUSE); _x_set_fine_speed(this->stream, XINE_SPEED_PAUSE); } } } #else # define log_buffer_fill(this,used,free,num_vid) #endif static void scr_tuning_set_paused(vdr_input_plugin_t *this) { CHECK_LOCKED(this->lock); CHECK_FALSE(this->is_trickspeed); CHECK_FALSE(this->is_paused); CHECK_FALSE(this->slave.stream); if (this->still_mode) return; if (this->scr_tuning != SCR_TUNING_PAUSED) { this->scr_tuning = SCR_TUNING_PAUSED; /* marked as paused */ this->scr->set_speed_tuning(this->scr, 1.0); if (_x_get_fine_speed(this->stream) != XINE_SPEED_PAUSE) { _x_set_fine_speed(this->stream, XINE_SPEED_PAUSE); } this->I_frames = this->P_frames = this->B_frames = 0; } } static void reset_scr_tuning(vdr_input_plugin_t *this) { CHECK_LOCKED(this->lock); if (this->scr_tuning != SCR_TUNING_OFF) { CHECK_FALSE(this->is_trickspeed); CHECK_FALSE(this->is_paused); this->scr_tuning = SCR_TUNING_OFF; /* marked as normal */ this->scr->set_speed_tuning(this->scr, 1.0); if (_x_get_fine_speed(this->stream) != XINE_FINE_SPEED_NORMAL) { if (!this->is_paused) _x_set_fine_speed(this->stream, XINE_FINE_SPEED_NORMAL); else LOGDBG("reset_scr_tuning: playback is paused"); } this->scr->scr.set_fine_speed(&this->scr->scr, XINE_FINE_SPEED_NORMAL); } } static void vdr_adjust_realtime_speed(vdr_input_plugin_t *this) { CHECK_LOCKED(this->lock); CHECK_FALSE(this->still_mode); CHECK_FALSE(this->is_trickspeed); CHECK_FALSE(this->is_paused); /* * Grab current buffer usage */ unsigned num_used, num_free, fill_level; /* amount of free buffers */ if (this->hd_stream) num_free = this->hd_buffer->num_free(this->hd_buffer); else num_free = this->buffer_pool->num_free(this->buffer_pool); if (num_free < this->reserved_buffers) num_free = 0; else num_free -= this->reserved_buffers; /* amount of used buffers */ num_used = this->buffer_pool->size(this->buffer_pool) + this->block_buffer->size(this->block_buffer); if (this->stream->audio_fifo) num_used += this->stream->audio_fifo->size(this->stream->audio_fifo); /* fill level */ fill_level = 100 * num_used / (num_used + num_free); log_buffer_fill(this, num_used, num_free, num_frames); /* * */ int scr_tuning = this->scr_tuning; /* * SCR -> PAUSE * - If buffer is empty, pause SCR (playback) for a while */ if (num_used < 1 && scr_tuning != SCR_TUNING_PAUSED && !this->no_video && !this->still_mode && !this->is_trickspeed) { int num_frames = this->stream->video_out->get_property(this->stream->video_out, VO_PROP_BUFS_IN_FIFO); if (num_frames < 5) { LOGSCR("SCR paused by adjust_speed, vbufs = %d", num_frames); scr_tuning_set_paused(this); return; } LOGSCR("adjust_speed: no pause, enough vbufs queued (%d)", num_frames); } /* SCR -> RESUME * - If SCR (playback) is currently paused due to previous buffer underflow, * revert to normal if SCR-Treshold > SD-/HD-Treshold (configured in xine-config) * and Audio-Treshold > 0. * - SD- and HD-Streams will be handled separately to improve SCR-playback after channel switch. * - SCR-Treshold calculation based on configured SD-/HD-Buffers in xine-config. * - Audio-Treshold calculation based on fixed 500 Audio-Buffers, * to handle different data rates (DVB-S/DVB-C/DVB-T) and SD-/HD-channels with one treshold. */ if (scr_tuning == SCR_TUNING_PAUSED) { unsigned scr_treshold = 100 * num_used / (num_used + num_free); unsigned audio_treshold = 100 * this->stream->audio_fifo->size (this->stream->audio_fifo) / (this->stream->audio_fifo->size (this->stream->audio_fifo) + 500); LOGSCR("SCR-T %2d%%, FB %d, UB %d", scr_treshold, num_free, num_used); LOGSCR("Audio-T %2d%%, UB %d", audio_treshold, this->stream->audio_fifo->size (this->stream->audio_fifo)); if ( (this->hd_stream && scr_treshold > this->class->scr_treshold_hd && (audio_treshold > 0 || scr_treshold > 65)) || (!this->hd_stream && scr_treshold > this->class->scr_treshold_sd && (audio_treshold > 0 || scr_treshold > 65)) || (this->no_video && num_used > 5) || this->still_mode || this->is_trickspeed || (this->I_frames > 0 && (this->I_frames > 2 || this->P_frames > 6))) { LOGSCR("SCR tuning resetted by adjust_speed, " "I %d B %d P %d", this->I_frames, this->B_frames, this->P_frames); this->I_frames = 0; reset_scr_tuning(this); } /* * Adjust SCR rate * - Live data is coming in at rate defined by sending transponder, * there is no way to change it -> we must adapt to it * - when playing realtime (live) stream, adjust our SCR to keep * xine buffers half full. This efficiently synchronizes our SCR * to transponder SCR and prevents buffer under/overruns caused by * minor differences in clock speeds. * - if buffer is getting empty, slow don SCR by 0.5...1% * - if buffer is getting full, speed up SCR by 0.5...1% * * TODO: collect simple statistics and try to calculate more exact * clock rate difference to minimize SCR speed changes */ } else if (_x_get_fine_speed(this->stream) == XINE_FINE_SPEED_NORMAL) { if (!this->scr_live_sync) { scr_tuning = SCR_TUNING_OFF; } else if (this->no_video) { /* radio stream ? */ if( num_used >= RADIO_MAX_BUFFERS - 1) scr_tuning = +1; /* play faster */ else if( num_used <= RADIO_MAX_BUFFERS / 3) scr_tuning = -1; /* play slower */ else scr_tuning = SCR_TUNING_OFF; } else if (this->class->smooth_scr_tuning) { /* * Experimental only. * Major sync point displacements are handled by xine-lib. * This provides much faster sync after channel switch, replay start etc. */ int trim_rel, trim_act; #define DIVIDER 8000 #define WEIGHTING 2 #define CENTER_POS 0 #define MAX_TRIM_REL 1 #define MAX_TRIM_ABS 2 #define MIN_FILL_PER_CENT 0 #define MAX_FILL_PER_CENT 100 #define TARGET_FILL_PER_CENT 50 #ifdef LOG_GRAPH if (!this->scr_buf.cnt) { log_graph(0, 0); printf(" R\n"); } #endif trim_act = scr_tuning - CENTER_POS; this->scr_buf.fill_avg += fill_level; this->scr_buf.fill_min = MIN(this->scr_buf.fill_min, fill_level); this->scr_buf.fill_max = MAX(this->scr_buf.fill_max, fill_level); #ifdef LOG_GRAPH log_graph(fill_level, '.'); #endif ++this->scr_buf.cnt; if (!(this->scr_buf.cnt % DIVIDER)) { this->scr_buf.fill_avg /= DIVIDER; trim_rel = (this->scr_buf.fill_avg - TARGET_FILL_PER_CENT) / WEIGHTING; trim_rel = MIN(trim_rel, MAX_TRIM_REL); trim_rel = MAX(trim_rel, -MAX_TRIM_REL); #ifdef LOG_GRAPH log_graph(this->scr_buf.fill_avg, '|'); log_graph(0, 1); printf(" %2d%% %2d%% %2d%% [%3d%+4d]\n", this->scr_buf.fill_min, this->scr_buf.fill_max, this->scr_buf.fill_avg, trim_act, trim_rel); #endif this->scr_buf.fill_avg = 0; this->scr_buf.fill_min = MAX_FILL_PER_CENT; this->scr_buf.fill_max = MIN_FILL_PER_CENT; if (trim_rel) { trim_act += trim_rel; trim_act = MIN(trim_act, MAX_TRIM_ABS); trim_act = MAX(trim_act, -MAX_TRIM_ABS); /* reprog clock correction */ scr_tuning = trim_act + CENTER_POS; } } } else { if (fill_level > 85) { scr_tuning = +2; /* play 1% faster */ } else if (fill_level > 70) { scr_tuning = +1; /* play .5% faster */ } else if (fill_level < 15) { scr_tuning = -2; /* play 1% slower */ } else if (fill_level < 30) { scr_tuning = -1; /* play .5% slower */ } else if ((scr_tuning > 0 && num_free > num_used) || (scr_tuning < 0 && num_used > num_free)) { scr_tuning = SCR_TUNING_OFF; } } if (scr_tuning != this->scr_tuning) { LOGSCR("scr_tuning: %s -> %s (buffer %d/%d) (tuning now %f%%)", scr_tuning_str(this->scr_tuning), scr_tuning_str(scr_tuning), num_used, num_free, this->class->scr_tuning_step * scr_tuning * 100.0); this->scr_tuning = scr_tuning; /* make it play .5% / 1% faster or slower */ if (this->scr) this->scr->set_speed_tuning(this->scr, 1.0 + (this->class->scr_tuning_step * scr_tuning) ); } /* * SCR -> NORMAL * - If we are in replay (or trick speed) mode, switch SCR tuning off * as we can always have complete control on incoming data rate */ } else if (this->scr_tuning) { reset_scr_tuning(this); } } /******************************* TOOLS ***********************************/ static char *strn0cpy(char *dest, const char *src, int n) { char *s = dest; for ( ; --n && (*dest = *src) != 0; dest++, src++) ; *dest = 0; return s; } static char *unescape_filename(const char *fn) { char *d = strdup(fn), *s = d, *result = d; while(*s && *s != '#') { if(s[0] == '%' && s[1] && s[2]) { unsigned int c; if (sscanf(s+1, "%02x", &c) == 1) { *d++ = (char)c; s += 3; continue; } } *d++ = *s++; } *d = 0; return result; } static void create_timeout_time(struct timespec *abstime, int timeout_ms) { struct timeval now; gettimeofday(&now, NULL); now.tv_usec += timeout_ms * 1000; while (now.tv_usec >= 1000000) { /* take care of an overflow */ now.tv_sec++; now.tv_usec -= 1000000; } abstime->tv_sec = now.tv_sec; abstime->tv_nsec = now.tv_usec * 1000; } /**************************** socket I/O *********************************/ /* * io_select_rd() * * - poll socket for read * - timeouts in 500 ms * - returns XIO_* */ static int io_select_rd (int fd) { fd_set fdset, eset; int ret; struct timeval select_timeout; if(fd < 0) return XIO_ERROR; FD_ZERO (&fdset); FD_ZERO (&eset); FD_SET (fd, &fdset); FD_SET (fd, &eset); select_timeout.tv_sec = 0; select_timeout.tv_usec = 500*1000; /* 500 ms */ errno = 0; ret = select (fd + 1, &fdset, NULL, &eset, &select_timeout); if (ret == 0) return XIO_TIMEOUT; if (ret < 0) { if (errno == EINTR || errno == EAGAIN) return XIO_TIMEOUT; return XIO_ERROR; } if (FD_ISSET(fd, &eset)) return XIO_ERROR; if (FD_ISSET(fd, &fdset)) return XIO_READY; return XIO_TIMEOUT; } /* * write_control_data() * * - write len bytes to control socket. * - returns number of bytes written, < 0 on error. * * NOTE: caller must hold fd_control lock ! */ static ssize_t write_control_data(vdr_input_plugin_t *this, const void *str, size_t len) { size_t ret, result = len; while (len > 0) { if (!this->control_running) { LOGMSG("write_control aborted"); return -1; } /* poll the socket */ fd_set fdset, eset; struct timeval select_timeout; FD_ZERO (&fdset); FD_ZERO (&eset); FD_SET (this->fd_control, &fdset); FD_SET (this->fd_control, &eset); select_timeout.tv_sec = 0; select_timeout.tv_usec = 500*1000; /* 500 ms */ errno = 0; if (1 != select (this->fd_control + 1, NULL, &fdset, &eset, &select_timeout) || !FD_ISSET(this->fd_control, &fdset) || FD_ISSET(this->fd_control, &eset)) { LOGERR("write_control failed (poll timeout or error)"); this->control_running = 0; return -1; } if (!this->control_running) { LOGERR("write_control aborted"); return -1; } errno = 0; ret = write (this->fd_control, str, len); if (ret <= 0) { if (ret == 0) { LOGMSG("write_control: disconnected"); } else if (errno == EAGAIN) { LOGERR("write_control failed: EAGAIN"); continue; } else if (errno == EINTR) { LOGERR("write_control failed: EINTR"); pthread_testcancel(); continue; } else { LOGERR("write_control failed"); } this->control_running = 0; return -1; } len -= ret; str = (uint8_t*)str + ret; } return result; } /* * write_control() * * - write null-terminated string to control socket. * - returns number of bytes written, < 0 on error * * NOTE: caller should NOT hold fd_control lock ! */ static ssize_t write_control(vdr_input_plugin_t *this, const char *str) { ssize_t ret = -1; mutex_lock_cancellable (&this->fd_control_lock); ret = write_control_data(this, str, strlen(str)); mutex_unlock_cancellable (&this->fd_control_lock); return ret; } /* * printf_control() * * - returns number of bytes written, < 0 on error * * NOTE: caller should NOT hold fd_control lock ! */ static ssize_t printf_control(vdr_input_plugin_t *this, const char *fmt, ...) { va_list argp; char buf[512]; ssize_t result; va_start(argp, fmt); vsnprintf(buf, sizeof(buf), fmt, argp); buf[sizeof(buf)-1] = 0; result = write_control(this, buf); va_end(argp); return result; } /* * readline_control() * * - read CR/LF terminated string from control socket * - remove trailing CR/LF * - returns > 0 : length of string * = 0 : timeout * < 0 : error */ static ssize_t readline_control(vdr_input_plugin_t *this, char *buf, size_t maxlen, int timeout) { int poll_result; ssize_t read_result; size_t total_bytes = 0; *buf = 0; while (total_bytes < maxlen - 1) { if (!this->control_running && timeout < 0) return -1; pthread_testcancel(); poll_result = io_select_rd(this->fd_control); pthread_testcancel(); if (!this->control_running && timeout < 0) return -1; if (poll_result == XIO_TIMEOUT) { if (timeout == 0) return 0; if (timeout > 0) timeout--; continue; } if (poll_result == XIO_ABORTED) { LOGERR("readline_control: XIO_ABORTED at [%u]", (uint)total_bytes); continue; } if (poll_result != XIO_READY /* == XIO_ERROR */) { LOGERR("readline_control: poll error at [%u]", (uint)total_bytes); return -1; } errno = 0; read_result = read (this->fd_control, buf + total_bytes, 1); pthread_testcancel(); if (!this->control_running && timeout < 0) return -1; if (read_result <= 0) { if (read_result == 0) LOGERR("Control stream disconnected"); else LOGERR("readline_control: read error at [%u]", (uint)total_bytes); if (read_result < 0 && (errno == EINTR || errno == EAGAIN)) continue; return -1; } if (buf[total_bytes]) { if (buf[total_bytes] == '\r') { buf[total_bytes] = 0; } else if (buf[total_bytes] == '\n') { buf[total_bytes] = 0; break; } else { total_bytes ++; buf[total_bytes] = 0; } } TRACE("readline_control: %d bytes ... %s\n", len, buf); } TRACE("readline_control: %d bytes (max %d)\n", len, maxlen); return total_bytes; } /* * read_control() * * - read len bytes from control socket * - returns < 0 on error */ static ssize_t read_control(vdr_input_plugin_t *this, uint8_t *buf, size_t len) { int poll_result; ssize_t num_bytes; size_t total_bytes = 0; while (total_bytes < len) { if (!this->control_running) return -1; pthread_testcancel(); poll_result = io_select_rd(this->fd_control); pthread_testcancel(); if (!this->control_running) return -1; if (poll_result == XIO_TIMEOUT) { continue; } if (poll_result == XIO_ABORTED) { LOGERR("read_control: XIO_ABORTED"); continue; } if (poll_result == XIO_ERROR) { LOGERR("read_control: poll error"); return -1; } errno = 0; num_bytes = read (this->fd_control, buf + total_bytes, len - total_bytes); pthread_testcancel(); if (num_bytes <= 0) { if (this->control_running && num_bytes < 0) LOGERR("read_control read() error"); return -1; } total_bytes += num_bytes; } return total_bytes; } static void puts_vdr(vdr_input_plugin_t *this, const char *s) { if (this->fd_control < 0) { if (this->funcs.xine_input_event) { this->funcs.xine_input_event(this->funcs.fe_handle, s, NULL); } } else { write_control(this, s); } } static void printf_vdr(vdr_input_plugin_t *this, const char *fmt, ...) { va_list argp; char buf[512]; va_start(argp, fmt); vsnprintf(buf, sizeof(buf), fmt, argp); buf[sizeof(buf)-1] = 0; puts_vdr(this, buf); va_end(argp); } /************************** BUFFER HANDLING ******************************/ static void buffer_pool_free (buf_element_t *element) { fifo_buffer_t *this = (fifo_buffer_t *) element->source; pthread_mutex_lock (&this->buffer_pool_mutex); element->next = this->buffer_pool_top; this->buffer_pool_top = element; this->buffer_pool_num_free++; if (this->buffer_pool_num_free > this->buffer_pool_capacity) { LOGERR("xine-lib:buffer: There has been a fatal error: TOO MANY FREE's"); _x_abort(); } if(this->buffer_pool_num_free > 20) pthread_cond_signal (&this->buffer_pool_cond_not_empty); pthread_mutex_unlock (&this->buffer_pool_mutex); } static buf_element_t *fifo_buffer_try_get(fifo_buffer_t *fifo) { int i; buf_element_t *buf; pthread_mutex_lock (&fifo->mutex); if (fifo->first==NULL) { pthread_mutex_unlock (&fifo->mutex); return NULL; } buf = fifo->first; fifo->first = fifo->first->next; if (fifo->first==NULL) fifo->last = NULL; fifo->fifo_size--; fifo->fifo_data_size -= buf->size; for(i = 0; fifo->get_cb[i]; i++) fifo->get_cb[i](fifo, buf, fifo->get_cb_data[i]); pthread_mutex_unlock (&fifo->mutex); return buf; } static buf_element_t *fifo_buffer_timed_get(fifo_buffer_t *fifo, int timeout) { buf_element_t *buf = fifo_buffer_try_get (fifo); if (!buf) { struct timespec abstime; int result = 0; create_timeout_time (&abstime, timeout); mutex_lock_cancellable (&fifo->mutex); while (fifo->first == NULL && !result) result = pthread_cond_timedwait (&fifo->not_empty, &fifo->mutex, &abstime); mutex_unlock_cancellable (&fifo->mutex); buf = fifo_buffer_try_get (fifo); } return buf; } static void signal_buffer_pool_not_empty(vdr_input_plugin_t *this) { if (this->buffer_pool) { pthread_mutex_lock(&this->buffer_pool->buffer_pool_mutex); pthread_cond_broadcast(&this->buffer_pool->buffer_pool_cond_not_empty); pthread_mutex_unlock(&this->buffer_pool->buffer_pool_mutex); } if (this->hd_buffer) { pthread_mutex_lock(&this->hd_buffer->buffer_pool_mutex); pthread_cond_broadcast(&this->hd_buffer->buffer_pool_cond_not_empty); pthread_mutex_unlock(&this->hd_buffer->buffer_pool_mutex); } } static void signal_buffer_not_empty(vdr_input_plugin_t *this) { if(this->block_buffer) { pthread_mutex_lock(&this->block_buffer->mutex); pthread_cond_broadcast(&this->block_buffer->not_empty); pthread_mutex_unlock(&this->block_buffer->mutex); } } #if XINE_VERSION_CODE < 10190 # define fifo_buffer_new(stream, n, s) _x_fifo_buffer_new(n, s) #else static void fifo_buffer_dispose (fifo_buffer_t *this) { buf_element_t *buf, *next; int received = 0; this->clear( this ); buf = this->buffer_pool_top; while (buf != NULL) { next = buf->next; free (buf->extra_info); free (buf); received++; buf = next; } while (received < this->buffer_pool_capacity) { buf = this->get(this); free(buf->extra_info); free(buf); received++; } #if XINE_VERSION_CODE >= 10207 xine_free_aligned (this->buffer_pool_base); #else av_free (this->buffer_pool_base); #endif pthread_mutex_destroy(&this->mutex); pthread_cond_destroy(&this->not_empty); pthread_mutex_destroy(&this->buffer_pool_mutex); pthread_cond_destroy(&this->buffer_pool_cond_not_empty); free (this); } static fifo_buffer_t *fifo_buffer_new (xine_stream_t *stream, int num_buffers, uint32_t buf_size) { fifo_buffer_t *ref = stream->video_fifo; fifo_buffer_t *this; int i; unsigned char *multi_buffer; LOGDBG("fifo_buffer_new..."); this = calloc(1, sizeof (fifo_buffer_t)); this->first = NULL; this->last = NULL; this->fifo_size = 0; this->put = ref->put; this->insert = ref->insert; this->get = ref->get; this->clear = ref->clear; this->size = ref->size; this->num_free = ref->num_free; this->data_size = ref->data_size; this->dispose = fifo_buffer_dispose; this->register_alloc_cb = ref->register_alloc_cb; this->register_get_cb = ref->register_get_cb; this->register_put_cb = ref->register_put_cb; this->unregister_alloc_cb = ref->unregister_alloc_cb; this->unregister_get_cb = ref->unregister_get_cb; this->unregister_put_cb = ref->unregister_put_cb; pthread_mutex_init (&this->mutex, NULL); pthread_cond_init (&this->not_empty, NULL); /* * init buffer pool, allocate nNumBuffers of buf_size bytes each */ #if XINE_VERSION_CODE >= 10207 multi_buffer = this->buffer_pool_base = xine_malloc_aligned (num_buffers * buf_size); #else multi_buffer = this->buffer_pool_base = av_mallocz (num_buffers * buf_size); #endif pthread_mutex_init (&this->buffer_pool_mutex, NULL); pthread_cond_init (&this->buffer_pool_cond_not_empty, NULL); this->buffer_pool_capacity = num_buffers; this->buffer_pool_buf_size = buf_size; this->buffer_pool_alloc = ref->buffer_pool_alloc; this->buffer_pool_try_alloc = ref->buffer_pool_try_alloc; for (i = 0; imem = multi_buffer; multi_buffer += buf_size; buf->max_size = buf_size; buf->free_buffer = buffer_pool_free; buf->source = this; buf->extra_info = malloc(sizeof(extra_info_t)); buffer_pool_free (buf); } LOGDBG("fifo_buffer_new done."); return this; } #endif static void flush_all_fifos (vdr_input_plugin_t *this, int full) { LOGDBG("flush_all_fifos(%s)", full ? "full" : ""); if (this->read_buffer) { this->read_buffer->free_buffer(this->read_buffer); this->read_buffer = NULL; } if (this->udp_data) { int i; for (i = 0; i <= UDP_SEQ_MASK; i++) if (this->udp_data->queue[i]) { this->udp_data->queue[i]->free_buffer(this->udp_data->queue[i]); this->udp_data->queue[i] = NULL; } } if (full) { if (this->stream && this->stream->audio_fifo) this->stream->audio_fifo->clear(this->stream->audio_fifo); if (this->stream && this->stream->video_fifo) this->stream->video_fifo->clear(this->stream->video_fifo); } if (this->block_buffer) this->block_buffer->clear(this->block_buffer); if (this->hd_buffer) this->hd_buffer->clear(this->hd_buffer); } static void wait_fifos_empty(xine_stream_t *stream, int timeout_ms) { int V, A; do { V = stream->video_fifo->size(stream->video_fifo); A = stream->audio_fifo->size(stream->audio_fifo); LOGVERBOSE("wait_fifos_empty: video %d, audio %d", V, A); if (V <= 0 && A <= 0) return; xine_usec_sleep(5*1000); timeout_ms -= 5; } while (timeout_ms > 0); LOGMSG("wait_fifos_empty: timeout! video=%d audio=%d", V, A); } static buf_element_t *get_buf_element(vdr_input_plugin_t *this, int size, int force) { buf_element_t *buf = NULL; /* HD buffer */ if (this->hd_stream && size <= HD_BUF_ELEM_SIZE) { if (this->hd_buffer->buffer_pool_num_free > (int)this->reserved_buffers) buf = this->hd_buffer->buffer_pool_try_alloc(this->hd_buffer); if (!force && !buf) return NULL; } /* limit max. buffered data */ if(!force && !buf) { if (this->buffer_pool->buffer_pool_num_free < (int)this->reserved_buffers) return NULL; } /* get smallest possible buffer */ if(!buf) { if(size < 8000) buf = this->buffer_pool->buffer_pool_try_alloc(this->buffer_pool); else if(size < 0xffff) { buf = this->block_buffer->buffer_pool_try_alloc(this->block_buffer); LOGDBG("get_buf_element: big PES (%d bytes) !", size); } else { /* len>64k */ LOGDBG("get_buf_element: jumbo PES (%d bytes) !", size); } } /* final try from audio fifo */ if(!buf) buf = this->stream->audio_fifo->buffer_pool_try_alloc(this->stream->audio_fifo); /* set up defaults */ if (buf) { buf->content = buf->mem; buf->size = 0; buf->type = BUF_DEMUX_BLOCK; buf->pts = 0; buf->free_buffer = buffer_pool_free; } return buf; } static buf_element_t *get_buf_element_timed(vdr_input_plugin_t *this, int size, int timeout) { buf_element_t *buf = get_buf_element (this, size, 0); if (!buf) { int result = 0; fifo_buffer_t *fifo = this->hd_stream ? this->hd_buffer : this->buffer_pool; struct timespec abstime; create_timeout_time (&abstime, timeout); do { mutex_lock_cancellable (&fifo->buffer_pool_mutex); result = pthread_cond_timedwait (&fifo->buffer_pool_cond_not_empty, &fifo->buffer_pool_mutex, &abstime); mutex_unlock_cancellable (&fifo->buffer_pool_mutex); buf = get_buf_element (this, size, 0); } while (!buf && !result); } return buf; } /* * strip_network_headers() * * Remove network headers from buffer and update buffer type. * Update current stream position. * - caller must hold this->lock ! */ static void strip_network_headers(vdr_input_plugin_t *this, buf_element_t *buf) { CHECK_LOCKED(this->lock); if (buf->type == BUF_LOCAL_BLOCK) { stream_local_header_t *header = (stream_local_header_t *)buf->content; this->curpos = header->pos; buf->content += sizeof(stream_local_header_t); buf->size -= sizeof(stream_local_header_t); buf->type = BUF_DEMUX_BLOCK; return; } if (buf->type == BUF_NETWORK_BLOCK) { if (this->udp || this->rtp) { stream_udp_header_t *header = (stream_udp_header_t *)buf->content; this->curpos = header->pos; buf->content += sizeof(stream_udp_header_t); buf->size -= sizeof(stream_udp_header_t); } else { stream_tcp_header_t *header = (stream_tcp_header_t *)buf->content; this->curpos = header->pos; buf->content += sizeof(stream_tcp_header_t); buf->size -= sizeof(stream_tcp_header_t); } buf->type = BUF_DEMUX_BLOCK; } } static void put_control_buf(fifo_buffer_t *buffer, fifo_buffer_t *pool, int cmd) { buf_element_t *buf = pool->buffer_pool_try_alloc(pool); if(buf) { buf->type = cmd; buffer->put(buffer, buf); } } /* * set_buffer_limits() * * Set buffer usage limits depending on stream type. * - caller must hold this->lock ! */ static void set_buffer_limits(vdr_input_plugin_t *this) { CHECK_LOCKED(this->lock); int capacity = (this->hd_stream ? this->hd_buffer : this->buffer_pool)->buffer_pool_capacity; int max_buffers; if (this->no_video) { /* radio channel / recording. Limit buffers to 10 */ max_buffers = RADIO_MAX_BUFFERS; } else { max_buffers = capacity; /* replay in local mode --> Limit buffers to 75% */ if (!this->live_mode && this->fd_control < 0) { max_buffers -= (capacity >> 2); } /* always reserve few buffers for control messages and TS demuxer */ max_buffers -= 10; } this->reserved_buffers = capacity - max_buffers; /* sanity checks */ if (capacity < max_buffers) { LOGMSG("set_buffer_limits(): internal error: max=%d, capacity=%d", max_buffers, capacity); this->reserved_buffers = 10; } if (this->reserved_buffers < 2) { LOGMSG("set_buffer_limits(): internal error: reserved=%d", this->reserved_buffers); this->reserved_buffers = 2; } } /***************************** INTERNAL **********************************/ /* * set_still_mode() * * Set/reset still image mode * - caller must hold this->lock ! */ static void set_still_mode(vdr_input_plugin_t *this, int still_mode) { CHECK_LOCKED(this->lock); if (still_mode || this->still_mode) CHECK_FALSE(this->live_mode); pthread_mutex_lock (&this->stream->first_frame_lock); this->stream->first_frame_flag = 2; pthread_mutex_unlock (&this->stream->first_frame_lock); this->still_mode = !!still_mode; _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HAS_STILL, this->still_mode); if (this->still_mode) reset_scr_tuning(this); this->stream->metronom->set_option(this->stream->metronom, XVDR_METRONOM_STILL_MODE, still_mode); } /* * set_live_mode() * * Set/reset live TV mode * - caller must hold this->lock ! */ static void set_live_mode(vdr_input_plugin_t *this, int onoff) { CHECK_LOCKED(this->lock); if (this->live_mode != onoff) { config_values_t *config = this->class->xine->config; this->live_mode = onoff; this->stream->metronom->set_option(this->stream->metronom, METRONOM_PREBUFFER, METRONOM_PREBUFFER_VAL); if (this->live_mode || (this->fd_control >= 0 && !this->slave.stream)) config->update_num(config, "audio.synchronization.av_sync_method", 1); #if 0 /* does not work after playing music files (?) */ else config->update_num(config, "audio.synchronization.av_sync_method", 0); #endif } set_buffer_limits(this); set_still_mode(this, 0); /* SCR tuning */ if (!this->live_mode) { LOGSCR("reset scr tuning by set_live_mode"); reset_scr_tuning(this); } signal_buffer_pool_not_empty(this); } /* * set_trick_speed() * * Set replay speed * - caller must hold this->lock ! */ static void set_trick_speed(vdr_input_plugin_t *this, int speed, int backwards) { /* speed: <0 - show each abs(n)'th frame (drop other frames) * no audio 0 - paused * audio back if mute != 0 >0 - show each frame n times * no audio 1 - normal */ CHECK_LOCKED(this->lock); if (speed > 64 || speed < -64) return; this->is_paused = !!(speed == 0); if (!this->is_paused) set_still_mode(this, 0); if (this->slave.stream) backwards = 0; this->stream->metronom->set_option(this->stream->metronom, XVDR_METRONOM_TRICK_SPEED, backwards ? speed : 0); if (speed > 1 || speed < -1) { CHECK_FALSE(this->live_mode); reset_scr_tuning(this); this->is_trickspeed = 1; } else { this->is_trickspeed = 0; } _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HAS_STILL, this->still_mode || speed==0); if (speed > 0) speed = XINE_FINE_SPEED_NORMAL / speed; else speed = XINE_FINE_SPEED_NORMAL * (-speed); if (this->scr_tuning != SCR_TUNING_PAUSED && _x_get_fine_speed(this->stream) != speed) { _x_set_fine_speed (this->stream, speed); } if (this->slave.stream) { _x_set_fine_speed (this->slave.stream, speed); } } static void reset_trick_speed(vdr_input_plugin_t *this) { set_trick_speed(this, 1, 0); } /* * generated images */ static void queue_blank_yv12(vdr_input_plugin_t *this) { if(!this || !this->stream || !this->stream->video_out) return; vo_frame_t *img = NULL; int width = _x_stream_info_get(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH); int height = _x_stream_info_get(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT); int ratio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_VIDEO_RATIO); double dratio; if (width < 360 || height < 288 || width > 1920 || height > 1200) { LOGMSG("queue_blank_yv12: invalid dimensions %dx%d in stream_info !", width, height); return; } if (ratio > 13300 && ratio < 13400) dratio = 4.0 / 3.0; else if (ratio > 17700 && ratio < 17800) dratio = 16.0 / 9.0; else if (ratio > 21000 && ratio < 22000) dratio = 2.11 / 1.0; else dratio = ((double)ratio) / 10000.0; set_still_mode(this, 0); reset_scr_tuning(this); _x_demux_control_newpts(this->stream, 0, BUF_FLAG_SEEK); this->class->xine->port_ticket->acquire (this->class->xine->port_ticket, 1); img = this->stream->video_out->get_frame (this->stream->video_out, width, height, dratio, XINE_IMGFMT_YV12, VO_BOTH_FIELDS); this->class->xine->port_ticket->release (this->class->xine->port_ticket, 1); if (img) { if (img->format == XINE_IMGFMT_YV12 && img->base[0] && img->base[1] && img->base[2]) { memset(img->base[0], 0x00, img->pitches[0] * img->height); memset(img->base[1], 0x80, img->pitches[1] * img->height / 2); memset(img->base[2], 0x80, img->pitches[2] * img->height / 2); img->duration = 0; img->pts = 0; img->bad_frame = 0; wait_fifos_empty(this->stream, 100); this->stream->metronom->set_option(this->stream->metronom, METRONOM_PREBUFFER, 2000); img->draw(img, this->stream); this->stream->metronom->set_option(this->stream->metronom, METRONOM_PREBUFFER, METRONOM_PREBUFFER_VAL); } img->free(img); } } static void queue_nosignal(vdr_input_plugin_t *this) { #define extern static #include "nosignal_720x576.c" #undef extern char *data = NULL, *tmp = NULL; int datalen = 0, pos = 0; buf_element_t *buf = NULL; fifo_buffer_t *fifo = this->stream->video_fifo; char *path, *home; if (fifo->num_free(fifo) < 10) { LOGMSG("queue_nosignal: not enough free buffers (%d) !", fifo->num_free(fifo)); return; } if(asprintf(&home,"%s/.xine/nosignal.mpg", xine_get_homedir()) < 0) return; int fd = open(path=home, O_RDONLY); if(fd<0) fd = open(path="/etc/vdr/plugins/xineliboutput/nosignal.mpg", O_RDONLY); if(fd<0) fd = open(path="/etc/vdr/plugins/xine/noSignal.mpg", O_RDONLY); if(fd<0) fd = open(path="/video/plugins/xineliboutput/nosignal.mpg", O_RDONLY); if(fd<0) fd = open(path="/video/plugins/xine/noSignal.mpg", O_RDONLY); if(fd<0) fd = open(path=NOSIGNAL_IMAGE_FILE, O_RDONLY); if(fd>=0) { tmp = data = malloc(NOSIGNAL_MAX_SIZE); datalen = read(fd, data, NOSIGNAL_MAX_SIZE); if(datalen==NOSIGNAL_MAX_SIZE) { LOGMSG("WARNING: custom \"no signal\" image %s too large", path); } else if(datalen<=0) { LOGERR("error reading %s", path); } else { LOGMSG("using custom \"no signal\" image %s", path); } close(fd); } free(home); if(datalen<=0) { data = (char*)&v_mpg_nosignal[0]; datalen = v_mpg_nosignal_length; } /* need to reset decoder if video format is not the same */ _x_demux_control_start(this->stream); while(pos < datalen) { buf = fifo->buffer_pool_try_alloc(fifo); if(buf) { buf->content = buf->mem; buf->size = MIN(datalen - pos, buf->max_size); buf->type = BUF_VIDEO_MPEG; xine_fast_memcpy(buf->content, &data[pos], buf->size); pos += buf->size; if(pos >= datalen) buf->decoder_flags |= BUF_FLAG_FRAME_END; fifo->put(fifo, buf); } else { LOGMSG("Error: queue_nosignal: no buffers !"); break; } } put_control_buf(fifo, fifo, BUF_CONTROL_FLUSH_DECODER); put_control_buf(fifo, fifo, BUF_CONTROL_NOP); free(tmp); } /*************************** slave input (PIP stream) ********************/ typedef struct fifo_input_plugin_s { input_plugin_t i; vdr_input_plugin_t *master; xine_stream_t *stream; fifo_buffer_t *buffer; fifo_buffer_t *buffer_pool; off_t pos; } fifo_input_plugin_t; static int fifo_open(input_plugin_t *this_gen) { return 1; } static uint32_t fifo_get_capabilities (input_plugin_t *this_gen) { return INPUT_CAP_BLOCK; } static uint32_t fifo_get_blocksize (input_plugin_t *this_gen) { return 2 * 2048; } static off_t fifo_get_current_pos (input_plugin_t *this_gen) { return -1; } static off_t fifo_get_length (input_plugin_t *this_gen) { return -1; } static off_t fifo_seek (input_plugin_t *this_gen, off_t offset, int origin) { return offset; } static int fifo_get_optional_data (input_plugin_t *this_gen, void *data, int data_type) { return INPUT_OPTIONAL_UNSUPPORTED; } #if XINE_VERSION_CODE > 10103 static const char* fifo_get_mrl (input_plugin_t *this_gen) #else static char* fifo_get_mrl (input_plugin_t *this_gen) #endif { return MRL_ID "+slave:"; } #if XINE_VERSION_CODE < 10190 static off_t fifo_read (input_plugin_t *this_gen, char *buf, off_t len) #else static off_t fifo_read (input_plugin_t *this_gen, void *buf, off_t len) #endif { int got = 0; LOGERR("fifo_input_plugin::fifo_read() not implemented !"); exit(-1); /* assert(false); */ return got; } static buf_element_t *fifo_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo) { fifo_input_plugin_t *this = (fifo_input_plugin_t *) this_gen; /*LOGDBG("fifo_read_block");*/ while (!_x_action_pending(this->stream)) { buf_element_t *buf = fifo_buffer_try_get(this->buffer); if(buf) { /* LOGDBG("fifo_read_block: got, return"); */ return buf; } /* LOGDBG("fifo_read_block: no buf, poll..."); */ /* poll(NULL, 0, 10); */ xine_usec_sleep(5*1000); /* LOGDBG("fifo_read_block: poll timeout"); */ } LOGDBG("fifo_read_block: return NULL !"); errno = EAGAIN; return NULL; } static void fifo_dispose (input_plugin_t *this_gen) { fifo_input_plugin_t *this = (fifo_input_plugin_t *) this_gen; LOGDBG("fifo_dispose"); if(this) { if(this->buffer) this->buffer->dispose(this->buffer); free(this); } } static input_plugin_t *fifo_class_get_instance (input_class_t *class_gen, xine_stream_t *stream, const char *data) { fifo_input_plugin_t *slave = calloc(1, sizeof(fifo_input_plugin_t)); unsigned long int imaster; vdr_input_plugin_t *master; LOGDBG("fifo_class_get_instance"); sscanf(data+15, "%lx", &imaster); master = (vdr_input_plugin_t*)imaster; slave->master = (vdr_input_plugin_t*)master; slave->stream = stream; slave->buffer_pool = stream->video_fifo; slave->buffer = fifo_buffer_new(stream, 4, 4096); slave->i.open = fifo_open; slave->i.get_mrl = fifo_get_mrl; slave->i.dispose = fifo_dispose; slave->i.input_class = class_gen; slave->i.get_capabilities = fifo_get_capabilities; slave->i.read = fifo_read; slave->i.read_block = fifo_read_block; slave->i.seek = fifo_seek; slave->i.get_current_pos = fifo_get_current_pos; slave->i.get_length = fifo_get_length; slave->i.get_blocksize = fifo_get_blocksize; slave->i.get_optional_data = fifo_get_optional_data; return (input_plugin_t*)slave; } /******************************** OSD ************************************/ static osd_rle_elem_t *uncompress_osd_net(uint8_t *raw, int elems, int datalen) { osd_rle_elem_t *data = (osd_rle_elem_t *)malloc(elems * sizeof(osd_rle_elem_t)); int i; /* * xine-lib rle format: * - palette index and length are uint16_t * * network format: * - palette entry is uint8_t * - length is uint8_t for lengths <=0x7f and uint16_t for lengths >0x7f * - high-order bit of first byte is used to signal size of length field: * bit=0 -> 7-bit, bit=1 -> 15-bit */ for(i=0; ifd_control >= 0 && /* remote mode */ this->funcs.intercept_osd /* frontend handles OSD */ ) { return this->funcs.intercept_osd(this->funcs.fe_handle, cmd) ? CONTROL_OK : CONTROL_DISCONNECTED; } return this->osd_manager->command(this->osd_manager, cmd, this->slave.stream ?: this->stream); } /******************************* Control *********************************/ #if XINE_VERSION_CODE < 10111 # define DEMUX_MUTEX_LOCK # define DEMUX_MUTEX_UNLOCK # define DEMUX_RESUME_SIGNAL #else # define DEMUX_MUTEX_LOCK pthread_mutex_lock(&stream->demux_mutex) # define DEMUX_MUTEX_UNLOCK pthread_mutex_unlock(&stream->demux_mutex) # define DEMUX_RESUME_SIGNAL pthread_cond_signal(&this->stream->demux_resume) #endif #if XINE_VERSION_CODE < 10200 # define RAISE_ACTION_PENDING this->stream->demux_action_pending = 1 # define LOWER_ACTION_PENDING this->stream->demux_action_pending = 0 #else # define RAISE_ACTION_PENDING _x_action_raise(this->stream) # define LOWER_ACTION_PENDING _x_action_lower(this->stream) #endif static void suspend_demuxer(vdr_input_plugin_t *this) { if (this->is_paused) LOGMSG("WARNING: called suspend_demuxer in paused mode !"); /* request demuxer to release demux_lock */ RAISE_ACTION_PENDING; /* signal all possible sync points to speed up this */ pthread_cond_broadcast(&this->engine_flushed); signal_buffer_not_empty(this); /* let demuxer return from vdr_plugin_read_* */ if (pthread_mutex_unlock( &this->lock )) LOGERR("pthread_mutex_unlock failed !"); /* lock demuxer */ pthread_mutex_lock( &this->stream->demux_lock ); LOWER_ACTION_PENDING; pthread_mutex_lock( &this->lock ); /* must be paired with resume_demuxer !!! */ } static void resume_demuxer(vdr_input_plugin_t *this) { /* must be paired with suspend_demuxer !!! */ DEMUX_RESUME_SIGNAL; pthread_mutex_unlock( &this->stream->demux_lock ); } static void vdr_x_demux_control_newpts( xine_stream_t *stream, int64_t pts, uint32_t flags ) { buf_element_t *buf; DEMUX_MUTEX_LOCK; buf = stream->video_fifo ? stream->video_fifo->buffer_pool_try_alloc (stream->video_fifo) : NULL; if(buf) { buf->type = BUF_CONTROL_NEWPTS; buf->decoder_flags = flags; buf->disc_off = pts; stream->video_fifo->put (stream->video_fifo, buf); } else { LOGMSG("vdr_x_demux_control_newpts: video fifo full !"); } buf = stream->audio_fifo ? stream->audio_fifo->buffer_pool_try_alloc (stream->audio_fifo) : NULL; if (buf) { buf->type = BUF_CONTROL_NEWPTS; buf->decoder_flags = flags; buf->disc_off = pts; stream->audio_fifo->put (stream->audio_fifo, buf); } else { LOGMSG("vdr_x_demux_control_newpts: audio fifo full !"); } DEMUX_MUTEX_UNLOCK; } static void vdr_flush_engine(vdr_input_plugin_t *this, uint64_t discard_index) { CHECK_LOCKED(this->lock); if(this->stream_start) { LOGMSG("vdr_flush_engine: stream_start, flush skipped"); return; } if(this->curpos > discard_index) { if(this->curpos < this->guard_index) { LOGMSG("vdr_flush_engine: guard > curpos, flush skipped"); return; } LOGMSG("vdr_flush_engine: %"PRIu64" < current position %"PRIu64", flush skipped", discard_index, this->curpos); return; } /* reset speed */ if(xine_get_param(this->stream, XINE_PARAM_FINE_SPEED) <= 0) { LOGMSG("vdr_flush_engine: playback is paused <0>"); xine_set_param(this->stream, XINE_PARAM_FINE_SPEED, XINE_FINE_SPEED_NORMAL); } /* suspend demuxer */ suspend_demuxer(this); reset_scr_tuning(this); /* reset speed again (adjust_realtime_speed might have set pause) */ if(xine_get_param(this->stream, XINE_PARAM_FINE_SPEED) <= 0) { LOGMSG("vdr_flush_engine: playback is paused <1>"); xine_set_param(this->stream, XINE_PARAM_FINE_SPEED, XINE_FINE_SPEED_NORMAL); } #if 0 _x_demux_flush_engine (this->stream); /* warning: after clearing decoders fifos an absolute discontinuity * indication must be sent. relative discontinuities are likely * to cause "jumps" on metronom. */ #else this->stream->demux_plugin->seek (this->stream->demux_plugin, 0, 0, this->stream->demux_thread_running); #endif #if XINE_VERSION_CODE < 10104 /* disabled _x_demux_control_start as it causes alsa output driver to exit now and then ... */ #else _x_demux_control_start(this->stream); #endif this->stream_start = 1; this->I_frames = this->B_frames = this->P_frames = 0; this->discard_index = discard_index; resume_demuxer(this); } static int set_deinterlace_method(vdr_input_plugin_t *this, const char *method_name) { int method = 0; if(!strncasecmp(method_name,"bob",3)) { method = 1; } else if(!strncasecmp(method_name,"weave",5)) { method = 2; } else if(!strncasecmp(method_name,"greedy",6)) { method = 3; } else if(!strncasecmp(method_name,"onefield",8)) { method = 4; } else if(!strncasecmp(method_name,"onefield_xv",11)) { method = 5; } else if(!strncasecmp(method_name,"linearblend",11)) { method = 6; } else if(!strncasecmp(method_name,"none",4)) { method = 0; } else if(!*method_name) { method = 0; } else if(!strncasecmp(method_name,"tvtime",6)) { method = -1; /* old deinterlacing system must be switched off. tvtime will be configured as all other post plugins with "POST tvtime ..." control message */ } else return -2; this->class->xine->config->update_num(this->class->xine->config, "video.output.xv_deinterlace_method", method >= 0 ? method : 0); xine_set_param(this->stream, XINE_PARAM_VO_DEINTERLACE, !!method); return 0; } static int set_video_properties(vdr_input_plugin_t *this, int hue, int saturation, int brightness, int sharpness, int noise_reduction, int contrast, int vo_aspect_ratio) { pthread_mutex_lock(&this->lock); /* when changed first time, save original/default values */ if(!this->video_properties_saved && (hue>=0 || saturation>=0 || contrast>=0 || brightness>=0 || sharpness>=0 || noise_reduction>=0 || vo_aspect_ratio>=0)) { this->video_properties_saved = 1; this->orig_hue = xine_get_param(this->stream, XINE_PARAM_VO_HUE ); this->orig_saturation = xine_get_param(this->stream, XINE_PARAM_VO_SATURATION ); this->orig_brightness = xine_get_param(this->stream, XINE_PARAM_VO_BRIGHTNESS ); #ifdef XINE_PARAM_VO_SHARPNESS this->orig_sharpness = xine_get_param(this->stream, XINE_PARAM_VO_SHARPNESS ); #endif #ifdef XINE_PARAM_VO_NOISE_REDUCTION this->orig_noise_reduction = xine_get_param(this->stream, XINE_PARAM_VO_NOISE_REDUCTION ); #endif this->orig_contrast = xine_get_param(this->stream, XINE_PARAM_VO_CONTRAST ); this->orig_vo_aspect_ratio = xine_get_param(this->stream, XINE_PARAM_VO_ASPECT_RATIO ); } /* set new values, or restore default/original values */ if(hue>=0 || this->video_properties_saved) xine_set_param(this->stream, XINE_PARAM_VO_HUE, hue>=0 ? hue : this->orig_hue ); if(saturation>=0 || this->video_properties_saved) xine_set_param(this->stream, XINE_PARAM_VO_SATURATION, saturation>0 ? saturation : this->orig_saturation ); if(brightness>=0 || this->video_properties_saved) xine_set_param(this->stream, XINE_PARAM_VO_BRIGHTNESS, brightness>=0 ? brightness : this->orig_brightness ); #ifdef XINE_PARAM_VO_SHARPNESS if(sharpness>=0 || this->video_properties_saved) xine_set_param(this->stream, XINE_PARAM_VO_SHARPNESS, sharpness>=0 ? sharpness : this->orig_sharpness ); #endif #ifdef XINE_PARAM_VO_NOISE_REDUCTION if(noise_reduction>=0 || this->video_properties_saved) xine_set_param(this->stream, XINE_PARAM_VO_NOISE_REDUCTION, noise_reduction>=0 ? noise_reduction : this->orig_noise_reduction ); #endif if(contrast>=0 || this->video_properties_saved) xine_set_param(this->stream, XINE_PARAM_VO_CONTRAST, contrast>=0 ? contrast : this->orig_contrast ); if(vo_aspect_ratio>=0 || this->video_properties_saved) xine_set_param(this->stream, XINE_PARAM_VO_ASPECT_RATIO, vo_aspect_ratio>=0 ? vo_aspect_ratio : this->orig_vo_aspect_ratio ); if(hue<0 && saturation<0 && contrast<0 && brightness<0 && sharpness<0 && noise_reduction<0 && vo_aspect_ratio<0) this->video_properties_saved = 0; pthread_mutex_unlock(&this->lock); return 0; } /* * slave stream */ static void send_meta_info(vdr_input_plugin_t *this) { if (this->slave.stream) { /* send stream meta info */ const char *title = xine_get_meta_info(this->slave.stream, XINE_META_INFO_TITLE); const char *artist = xine_get_meta_info(this->slave.stream, XINE_META_INFO_ARTIST); const char *album = xine_get_meta_info(this->slave.stream, XINE_META_INFO_ALBUM); const char *tracknumber = xine_get_meta_info(this->slave.stream, XINE_META_INFO_TRACK_NUMBER); printf_vdr(this, "INFO METAINFO title=@%s@ artist=@%s@ album=@%s@ tracknumber=@%s@\r\n", title?:"", artist?:"", album?:"", tracknumber?:""); } } #ifdef DVD_STREAMING_SPEED static void dvd_set_speed(const char *device, int speed) { /* * Original idea & code from mplayer-dev-eng mailing list: * Date: Sun, 17 Dec 2006 09:15:30 +0100 * From: Tobias Diedrich * Subject: [MPlayer-dev-eng] Re: [PATCH] Add "-dvd-speed", use SET_STREAMING command to quieten DVD drives * (http://lists-archives.org/mplayer-dev-eng/14383-add-dvd-speed-use-set_streaming-command-to-quieten-dvd-drives.html) */ #if defined(__linux__) && defined(SG_IO) && defined(GPCMD_SET_STREAMING) unsigned char buffer[28], cmd[16], sense[16]; struct sg_io_hdr sghdr; struct stat st; int fd; /* remember previous device so we can restore default speed */ static int dvd_speed = 0; static const char *dvd_dev = NULL; if (speed < 0 && dvd_speed == 0) return; /* we haven't touched the speed setting */ if (!device) device = dvd_dev; /* use previous device */ if (!device) return; if (!speed) return; if (stat(device, &st) == -1) return; if (!S_ISBLK(st.st_mode)) return; /* not a block device */ if ((fd = open(device, O_RDWR | O_NONBLOCK)) == -1) { LOGMSG("set_dvd_speed: error opening DVD device %s for read/write", device); return; } memset(&sghdr, 0, sizeof(sghdr)); memset(buffer, 0, sizeof(buffer)); memset(sense, 0, sizeof(sense)); memset(cmd, 0, sizeof(cmd)); if(speed < 0) { /* restore default value */ speed = 0; buffer[0] = 4; /* restore default */ LOGMSG("Setting DVD streaming speed to "); } else { /* limit to KB/s */ LOGMSG("Setting DVD streaming speed to %d", speed); } sghdr.interface_id = 'S'; sghdr.timeout = 5000; sghdr.dxfer_direction = SG_DXFER_TO_DEV; sghdr.mx_sb_len = sizeof(sense); sghdr.dxfer_len = sizeof(buffer); sghdr.cmd_len = sizeof(cmd); sghdr.sbp = sense; sghdr.dxferp = buffer; sghdr.cmdp = cmd; cmd[0] = GPCMD_SET_STREAMING; cmd[10] = 28; buffer[8] = 0xff; buffer[9] = 0xff; buffer[10] = 0xff; buffer[11] = 0xff; buffer[12] = buffer[20] = (speed >> 24) & 0xff; /* kilobyte */ buffer[13] = buffer[21] = (speed >> 16) & 0xff; buffer[14] = buffer[22] = (speed >> 8) & 0xff; buffer[15] = buffer[23] = speed & 0xff; buffer[18] = buffer[26] = 0x03; /* 1 second */ buffer[19] = buffer[27] = 0xe8; if (ioctl(fd, SG_IO, &sghdr) < 0) LOGERR("Failed setting DVD streaming speed to %d", speed); else if(speed > 0) LOGMSG("DVD streaming speed set to %d", speed); else LOGMSG("DVD streaming speed set to "); dvd_speed = speed; dvd_dev = device; close(fd); #else # warning Changing DVD streaming speed not supported #endif } #endif static void vdr_event_cb (void *user_data, const xine_event_t *event); static void select_spu_channel(xine_stream_t *stream, int channel) { _x_select_spu_channel(stream, channel); if (channel == SPU_CHANNEL_NONE) { /* re-enable overlay for VDR OSD ... */ if (stream->video_out) { pthread_mutex_lock (&stream->frontend_lock); stream->xine->port_ticket->acquire (stream->xine->port_ticket, 0); stream->video_out->enable_ovl (stream->video_out, 1); stream->xine->port_ticket->release (stream->xine->port_ticket, 0); pthread_mutex_unlock (&stream->frontend_lock); } } } static void dvd_menu_domain(vdr_input_plugin_t *this, int value) { if (value) { LOGDBG("dvd_menu_domain(1)"); this->dvd_menu = 1; this->slave.stream->spu_channel_user = SPU_CHANNEL_AUTO; this->slave.stream->spu_channel = this->slave.stream->spu_channel_auto; } else { LOGDBG("dvd_menu_domain(0)"); this->dvd_menu = 0; } } static void close_slave_stream(vdr_input_plugin_t *this) { if (!this->slave.stream) return; if(this->bg_stream.stream) { LOGMSG("Closing background stream"); xine_stop(this->bg_stream.stream); if (this->bg_stream.event_queue) { xine_event_dispose_queue (this->bg_stream.event_queue); this->bg_stream.event_queue = NULL; } xine_close(this->bg_stream.stream); xine_dispose(this->bg_stream.stream); this->bg_stream.stream = NULL; } /* dispose event queue first to prevent processing of PLAYBACK FINISHED event */ if (this->slave.event_queue) { xine_event_dispose_queue (this->slave.event_queue); this->slave.event_queue = NULL; } xine_stop(this->slave.stream); if(this->funcs.fe_control) { this->funcs.fe_control(this->funcs.fe_handle, "POST 0 Off\r\n"); this->funcs.fe_control(this->funcs.fe_handle, "SLAVE 0x0\r\n"); } xine_close(this->slave.stream); xine_dispose(this->slave.stream); pthread_mutex_lock(&this->lock); this->slave.stream = NULL; pthread_mutex_unlock(&this->lock); if(this->funcs.fe_control) this->funcs.fe_control(this->funcs.fe_handle, "SLAVE CLOSED\r\n"); } static int handle_control_playfile(vdr_input_plugin_t *this, const char *cmd) { const char *pt = cmd + 9; char filename[4096], av[256+4096], *pav = av; int loop = 0, pos = 0, err = 0, avsize = sizeof(av)-2, mix_streams = 0; while(*pt==' ') pt++; if(!strncmp(pt, "Loop ", 5)) { loop = 1; pt += 5; while(*pt==' ') pt++; } pos = atoi(pt); while(*pt && *pt != ' ') pt++; while(*pt == ' ') pt++; /* audio visualization / audio/video mixing */ while(*pt && *pt != ' ' && --avsize) *pav++ = *pt++; *pav = 0; while(*pt == ' ') pt++; mix_streams = (!strcmp(av, "Audio")) || (!strcmp(av, "Video")); strn0cpy(filename, pt, sizeof(filename)); this->autoplay_size = -1; if(*filename) { int is_file_mrl = !strncmp(filename, "file:/", 6) ? 5 : 0; this->loop_play = 0; /* mrlbase is needed for filename and for bgimage in remote mode */ char mrlbase[256]; if(this->fd_control >= 0) { char *host = strdup(strstr(this->mrl, "//")+2); char *port = strchr(host, ':'); int iport = port ? atoi(port+1) : DEFAULT_VDR_PORT; if(port) *port = 0; snprintf(mrlbase, sizeof(mrlbase), "http://%s:%d/PLAYFILE", host?:"127.0.0.1", iport); free(host); } if (this->slave.stream) handle_control_playfile(this, "PLAYFILE 0"); LOGMSG("PLAYFILE (Loop: %d, Offset: %ds, File: %s %s)", loop, pos, av, filename); /* check if it is really a file (not mrl) and try to access it */ if(is_file_mrl || filename[0] == '/') { struct stat st; char *f = unescape_filename(filename); errno = 0; if(stat(f+is_file_mrl, &st)) { if(errno == EACCES || errno == ELOOP) LOGERR("Can't access file !"); if(errno == ENOENT || errno == ENOTDIR) LOGERR("File not found !"); if(this->fd_control >= 0) { char mrl[sizeof(filename)+256]; char *sub = strstr(filename, "#subtitle:"); if(sub) *sub = 0; sprintf(mrl, "%s%s", mrlbase, filename + is_file_mrl); if(sub) { sub += 10; /*strlen("#subtitle:");*/ strcat(mrl, "#subtitle:"); strcat(mrl, mrlbase); strcat(mrl, sub); } LOGMSG(" -> trying to stream from server (%s) ...", mrl); strn0cpy(filename, mrl, sizeof(filename)); } } free(f); } if(!strcmp(filename,"dvd:/")) { #if 0 /* input/media_helper.c */ eject_media(0); /* DVD tray in */ #endif #ifdef DVD_STREAMING_SPEED xine_cfg_entry_t device; if (xine_config_lookup_entry(this->class->xine, "media.dvd.device", &device)) dvd_set_speed(device.str_value, 2700); #endif } #if XINE_VERSION_CODE < 10109 else if(!strncmp(filename,"dvd:/", 5)) { /* DVD plugin 'bug': unescaping is not implemented ... */ char *mrl = unescape_filename(filename); strn0cpy(filename, mrl, sizeof(filename)); free(mrl); } #endif if (!this->slave.stream) { cfg_entry_t *e = this->class->xine->config->lookup_entry(this->class->xine->config, "engine.buffers.video_num_buffers"); int vbufs = e ? e->num_value : 250; this->class->xine->config->update_num(this->class->xine->config, "engine.buffers.video_num_buffers", SLAVE_VIDEO_FIFO_SIZE); LOGMSG("xine_stream_new(slave_stream): using %dMB video fifo", SLAVE_VIDEO_FIFO_SIZE*8/1024); this->slave.stream = xine_stream_new(this->class->xine, this->stream->audio_out, this->stream->video_out); this->class->xine->config->update_num(this->class->xine->config, "engine.buffers.video_num_buffers", vbufs); } if (!this->slave.event_queue) { this->slave.event_queue = xine_event_new_queue (this->slave.stream); xine_event_create_listener_thread (this->slave.event_queue, vdr_event_cb, this); } select_spu_channel(this->slave.stream, SPU_CHANNEL_AUTO); this->dvd_menu = 0; errno = 0; err = !xine_open(this->slave.stream, filename); if(err) { LOGERR("Error opening file ! (File not found ? Unknown format ?)"); *filename = 0; /* this triggers stop */ } else { #if 1 if(this->stream->video_fifo->size(this->stream->video_fifo)) LOGMSG("playfile: main stream video_fifo not empty ! (%d)", this->stream->video_fifo->size(this->stream->video_fifo)); /* flush decoders and output fifos, close decoders and free frames. */ _x_demux_control_start(this->stream); xine_usec_sleep(50*1000); /* keep our own demux happy while playing another stream */ pthread_mutex_lock(&this->lock); reset_trick_speed(this); this->live_mode = 1; set_live_mode(this, 0); reset_trick_speed(this); reset_scr_tuning(this); pthread_mutex_unlock(&this->lock); this->slave.stream->metronom->set_option(this->slave.stream->metronom, METRONOM_PREBUFFER, 90000); #endif this->loop_play = loop; err = !xine_play(this->slave.stream, 0, 1000 * pos); if(err) { LOGMSG("Error playing file"); *filename = 0; /* this triggers stop */ } else { send_meta_info(this); if(this->funcs.fe_control) { char tmp[128]; int has_video; sprintf(tmp, "SLAVE 0x%lx %s\r\n", (unsigned long int)this->slave.stream, mix_streams ? av : ""); this->funcs.fe_control(this->funcs.fe_handle, tmp); has_video = _x_stream_info_get(this->slave.stream, XINE_STREAM_INFO_HAS_VIDEO); /* Play background image */ if(!has_video && !mix_streams && *av && !strncmp(av, "image:", 6)) { const char *bgimage = av + 6; /* background image stream init */ if (!this->bg_stream.stream) { LOGDBG("handle_control_playfile: Background stream init"); this->bg_stream.stream = xine_stream_new(this->class->xine, NULL, this->slave.stream->video_out); xine_set_param(this->bg_stream.stream, XINE_PARAM_IGNORE_AUDIO, 1); xine_set_param(this->bg_stream.stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, -2); xine_set_param(this->bg_stream.stream, XINE_PARAM_SPU_CHANNEL, -2); xine_set_param(this->bg_stream.stream, XINE_PARAM_AUDIO_REPORT_LEVEL, 0); } if (!this->bg_stream.event_queue) { LOGDBG("handle_control_playfile: Background event queue init"); this->bg_stream.event_queue = xine_event_new_queue(this->bg_stream.stream); xine_event_create_listener_thread(this->bg_stream.event_queue, vdr_event_cb, this); } /* open background image */ if (!xine_open(this->bg_stream.stream, bgimage) || !xine_play(this->bg_stream.stream, 0, 0)) { LOGMSG("Error opening background image %s (File not found ? Unknown format ?)", bgimage); int is_bg_file_mrl = !strncmp(bgimage, "file:/", 6) ? 5 : 0; if (this->fd_control >= 0 && (bgimage[0] == '/' || is_bg_file_mrl)) { /* Remote mode */ char bgmrl[4096+256]; snprintf(bgmrl, sizeof(bgmrl), "%s%s", mrlbase, bgimage + is_bg_file_mrl); bgmrl[sizeof(bgmrl)-1] = 0; LOGMSG(" -> trying to stream background image from server (%s) ...", bgmrl); if (!xine_open(this->bg_stream.stream, bgmrl) || !xine_play(this->bg_stream.stream, 0, 0)) LOGMSG("Error streaming background image from server!"); } } has_video = 1; } this->funcs.fe_control(this->funcs.fe_handle, has_video ? "NOVIDEO 1\r\n" : "NOVIDEO 0\r\n"); if(!has_video && !mix_streams && *av && strcmp(av, "none")) { char str[128], *avopts; if(NULL != (avopts = strchr(av, ':'))) *avopts++ = 0; else avopts = ""; snprintf(str, sizeof(str), "POST %s On %s\r\n", av, avopts); str[sizeof(str)-1] = 0; this->funcs.fe_control(this->funcs.fe_handle, str); } else { this->funcs.fe_control(this->funcs.fe_handle, "POST 0 Off\r\n"); } } } } } /* next code is also executed after failed open, so no "} else { " */ if(!*filename) { LOGMSG("PLAYFILE : Closing slave stream"); this->loop_play = 0; if (this->slave.stream) { close_slave_stream(this); _x_demux_control_start(this->stream); #ifdef DVD_STREAMING_SPEED dvd_set_speed(NULL, -1); #endif } } return err ? CONTROL_PARAM_ERROR : CONTROL_OK; } /* * grab */ static int handle_control_grab(vdr_input_plugin_t *this, const char *cmd) { int quality, width, height, jpeg; jpeg = !strcmp(cmd+5,"JPEG"); if(3 == sscanf(cmd+5+4, "%d %d %d", &quality, &width, &height)) { if(this->fd_control >= 0) { grab_data_t *data = NULL; LOGDBG("GRAB: jpeg=%d quality=%d w=%d h=%d", jpeg, quality, width, height); /* grab takes long time and we don't want to lose data connection or interrupt video ... */ if(pthread_mutex_unlock(&this->vdr_entry_lock)) LOGERR("pthread_mutex_unlock failed"); if(this->funcs.fe_control) data = (grab_data_t*)(this->funcs.fe_control(this->funcs.fe_handle, cmd)); if(data && data->size>0 && data->data) { char s[128]; sprintf(s, "GRAB %d %lu\r\n", this->token, (unsigned long)data->size); mutex_lock_cancellable (&this->fd_control_lock); write_control_data(this, s, strlen(s)); write_control_data(this, data->data, data->size); mutex_unlock_cancellable (&this->fd_control_lock); } else { /* failed */ printf_control(this, "GRAB %d 0\r\n", this->token); } pthread_mutex_lock(&this->vdr_entry_lock); if(data) { free(data->data); free(data); } return CONTROL_OK; } } return CONTROL_PARAM_ERROR; } /* * PIP */ static int handle_control_substream(vdr_input_plugin_t *this, const char *cmd) { unsigned int pid; if(1 == sscanf(cmd, "SUBSTREAM 0x%x", &pid)) { pthread_mutex_lock(&this->lock); if(!this->funcs.fe_control) LOGERR("ERROR - no fe_control set !"); if((pid & 0xf0) == 0xe0 && this->funcs.fe_control) { /* video 0...15 */ if(!this->pip_stream) { LOGMSG("create pip stream %s", cmd); this->pip_stream = this->funcs.fe_control(this->funcs.fe_handle, cmd); LOGMSG(" pip stream created"); } } else { /*} else if(audio) {*/ if(this->pip_stream && this->funcs.fe_control) { LOGMSG("close pip stream"); this->pip_stream = NULL; this->funcs.fe_control(this->funcs.fe_handle, cmd); /* xine_stop(this->pip_stream); */ /* xine_close(this->pip_stream); */ /* xine_dispose(this->pip_stream); */ } } pthread_mutex_unlock(&this->lock); return CONTROL_OK; } return CONTROL_PARAM_ERROR; } /* * OSD */ static int handle_control_osdcmd(vdr_input_plugin_t *this) { osd_command_t osdcmd = {0}; int err = CONTROL_OK; if (!this->control_running) return CONTROL_DISCONNECTED; /* read struct size first */ size_t todo, expect = sizeof(osd_command_t); uint8_t *pt = (uint8_t*)&osdcmd; if (read_control(this, pt, sizeof(osdcmd.size)) != sizeof(osdcmd.size)) { LOGMSG("control: error reading OSDCMD data length"); return CONTROL_DISCONNECTED; } pt += sizeof(osdcmd.size); expect -= sizeof(osdcmd.size); todo = osdcmd.size - sizeof(osdcmd.size); /* read data */ ssize_t bytes = MIN(todo, expect); if (read_control(this, pt, bytes) != bytes) { LOGMSG("control: error reading OSDCMD data"); return CONTROL_DISCONNECTED; } if (expect < todo) { /* server uses larger struct, discard rest of data */ ssize_t skip = todo - expect; uint8_t dummy[skip]; LOGMSG("osd_command_t size %d, expected %zu", osdcmd.size, expect); if (read_control(this, dummy, skip) != skip) { LOGMSG("control: error reading OSDCMD data (unknown part)"); return CONTROL_DISCONNECTED; } } ntoh_osdcmd(osdcmd); /* read palette */ if (osdcmd.palette && osdcmd.colors>0) { ssize_t bytes = sizeof(osd_clut_t) * osdcmd.colors; osdcmd.palette = malloc(bytes); if (read_control(this, (unsigned char *)osdcmd.palette, bytes) != bytes) { LOGMSG("control: error reading OSDCMD palette"); err = CONTROL_DISCONNECTED; } } else { osdcmd.palette = NULL; } /* read (RLE) data */ if (err == CONTROL_OK && osdcmd.data && osdcmd.datalen>0) { osdcmd.data = (osd_rle_elem_t *)malloc(osdcmd.datalen); if(read_control(this, (unsigned char *)osdcmd.data, osdcmd.datalen) != (ssize_t)osdcmd.datalen) { LOGMSG("control: error reading OSDCMD bitmap"); err = CONTROL_DISCONNECTED; } else { if (osdcmd.cmd == OSD_Set_HDMV) { uint8_t *raw = osdcmd.raw_data; int n = rle_uncompress_hdmv(&osdcmd.data, osdcmd.w, osdcmd.h, raw, osdcmd.num_rle, osdcmd.datalen); if (n < 1) { LOGMSG("HDMV mode OSD uncompress error"); osdcmd.raw_data = raw; } else { osdcmd.cmd = OSD_Set_RLE; osdcmd.datalen = osdcmd.num_rle*4; free(raw); } } else if (osdcmd.cmd == OSD_Set_RLE) { uint8_t *raw = osdcmd.raw_data; osdcmd.data = uncompress_osd_net(raw, osdcmd.num_rle, osdcmd.datalen); osdcmd.datalen = osdcmd.num_rle*4; free(raw); } } } else { osdcmd.data = NULL; } if (err == CONTROL_OK) err = vdr_plugin_exec_osd_command(&this->iface, &osdcmd); free(osdcmd.data); free(osdcmd.palette); return err; } /************************** Control from VDR ******************************/ #define VDR_ENTRY_LOCK(ret...) \ do { \ if(pthread_mutex_lock(&this->vdr_entry_lock)) { \ LOGERR("%s:%d: pthread_mutex_lock failed", __PRETTY_FUNCTION__, __LINE__); \ return ret ; \ } \ } while(0) #define VDR_ENTRY_UNLOCK() \ do { \ if(pthread_mutex_unlock(&this->vdr_entry_lock)) { \ LOGERR("%s:%d: pthread_mutex_unlock failed", __PRETTY_FUNCTION__, __LINE__); \ } \ } while(0) static const struct { const uint32_t type; const char name[28]; } eventmap[] = { {XINE_EVENT_INPUT_UP, "XINE_EVENT_INPUT_UP"}, {XINE_EVENT_INPUT_DOWN, "XINE_EVENT_INPUT_DOWN"}, {XINE_EVENT_INPUT_LEFT, "XINE_EVENT_INPUT_LEFT"}, {XINE_EVENT_INPUT_RIGHT, "XINE_EVENT_INPUT_RIGHT"}, {XINE_EVENT_INPUT_SELECT, "XINE_EVENT_INPUT_SELECT"}, {XINE_EVENT_INPUT_MENU1, "XINE_EVENT_INPUT_MENU1"}, {XINE_EVENT_INPUT_MENU2, "XINE_EVENT_INPUT_MENU2"}, {XINE_EVENT_INPUT_MENU3, "XINE_EVENT_INPUT_MENU3"}, {XINE_EVENT_INPUT_MENU4, "XINE_EVENT_INPUT_MENU4"}, {XINE_EVENT_INPUT_MENU5, "XINE_EVENT_INPUT_MENU5"}, {XINE_EVENT_INPUT_NEXT, "XINE_EVENT_INPUT_NEXT"}, {XINE_EVENT_INPUT_PREVIOUS,"XINE_EVENT_INPUT_PREVIOUS"}, }; /* * vdr_plugin_poll() * * Query buffer state * Returns amount of free PES buffer blocks in queue. */ static int vdr_plugin_poll(vdr_input_plugin_t *this, int timeout_ms) { struct timespec abstime; fifo_buffer_t *fifo = this->hd_stream ? this->hd_buffer : this->buffer_pool; int reserved_bufs = this->reserved_buffers; int result = 0; /* Caller must have locked this->vdr_entry_lock ! */ if (this->slave.stream) { LOGMSG("vdr_plugin_poll: called while playing slave stream !"); return 1; } TRACE("vdr_plugin_poll (%d ms), fifo: blocks=%d, bytes=%d", timeout_ms, fifo->size(fifo), fifo->data_size(fifo)); pthread_mutex_lock (&fifo->buffer_pool_mutex); result = fifo->buffer_pool_num_free - reserved_bufs; pthread_mutex_unlock (&fifo->buffer_pool_mutex); if (timeout_ms > 0 && result <= 0) { if (timeout_ms > 250) { LOGMSG("vdr_plugin_poll: timeout too large (%d ms), forced to 250ms", timeout_ms); timeout_ms = 250; } create_timeout_time(&abstime, timeout_ms); pthread_mutex_lock(&this->lock); if (this->scr_tuning == SCR_TUNING_PAUSED) { LOGSCR("scr tuning reset by POLL"); reset_scr_tuning(this); } pthread_mutex_unlock(&this->lock); signal_buffer_not_empty(this); VDR_ENTRY_UNLOCK(); pthread_mutex_lock (&fifo->buffer_pool_mutex); while (result <= 5) { if (pthread_cond_timedwait (&fifo->buffer_pool_cond_not_empty, &fifo->buffer_pool_mutex, &abstime) == ETIMEDOUT) break; result = fifo->buffer_pool_num_free - reserved_bufs; } pthread_mutex_unlock (&fifo->buffer_pool_mutex); VDR_ENTRY_LOCK(0); } TRACE("vdr_plugin_poll returns, %d free (%d used, %d bytes)\n", result, fifo->size(fifo), fifo->data_size(fifo)); /* handle priority problem in paused mode when data source has higher priority than control source */ if (result <= 0) { result = 0; xine_usec_sleep(3*1000); } return result; } /* * vdr_plugin_flush() * * Flush all data from buffers to output devices. * Returns 0 when there is no data or frames in stream buffers. */ static int vdr_plugin_flush(vdr_input_plugin_t *this, int timeout_ms) { struct timespec abstime; fifo_buffer_t *pool = this->buffer_pool; fifo_buffer_t *buffer = this->block_buffer; int result = 0, waitresult=0; /* Caller must have locked this->vdr_entry_lock ! */ if (this->slave.stream) { LOGDBG("vdr_plugin_flush: called while playing slave stream !"); return 0; } TRACE("vdr_plugin_flush (%d ms) blocks=%d+%d, frames=%d", timeout_ms, buffer->size(buffer), pool->size(pool), this->stream->video_out->get_property(this->stream->video_out, VO_PROP_BUFS_IN_FIFO)); if(this->live_mode /*&& this->fd_control < 0*/) { /* No flush in live mode */ return 1; } this->class->xine->port_ticket->acquire(this->class->xine->port_ticket, 1); result = MAX(0, pool->size(pool)) + MAX(0, buffer->size(buffer)) + this->stream->video_out->get_property(this->stream->video_out, VO_PROP_BUFS_IN_FIFO); this->class->xine->port_ticket->release(this->class->xine->port_ticket, 1); put_control_buf(buffer, pool, BUF_CONTROL_FLUSH_DECODER); put_control_buf(buffer, pool, BUF_CONTROL_NOP); if (result <= 0) return 0; create_timeout_time(&abstime, timeout_ms); while(result > 0 && waitresult != ETIMEDOUT) { TRACE("vdr_plugin_flush waiting (max %d ms), %d+%d buffers used, " "%d frames (rd pos=%" PRIu64 ")\n", timeout_ms, pool->size(pool), buffer->size(buffer), (int)this->stream->video_out->get_property(this->stream->video_out, VO_PROP_BUFS_IN_FIFO), this->curpos); pthread_mutex_lock(&pool->buffer_pool_mutex); waitresult = pthread_cond_timedwait (&pool->buffer_pool_cond_not_empty, &pool->buffer_pool_mutex, &abstime); pthread_mutex_unlock(&pool->buffer_pool_mutex); this->class->xine->port_ticket->acquire(this->class->xine->port_ticket, 1); result = MAX(0, pool->size(pool)) + MAX(0, buffer->size(buffer)) + this->stream->video_out->get_property(this->stream->video_out, VO_PROP_BUFS_IN_FIFO); this->class->xine->port_ticket->release(this->class->xine->port_ticket, 1); } TRACE("vdr_plugin_flush returns %d (%d+%d used, %d frames)\n", result, pool->size(pool), buffer->size(buffer), (int)this->stream->video_out->get_property(this->stream->video_out, VO_PROP_BUFS_IN_FIFO)); return result; } /* * vdr_plugin_flush_remote() * * vdr_plugin_flush() Wrapper for remote mode * - wait for data in network buffers */ static int vdr_plugin_flush_remote(vdr_input_plugin_t *this, int timeout_ms, uint64_t offset, int frame) { int r, live_mode; pthread_mutex_lock(&this->lock); live_mode = this->live_mode; this->live_mode = 0; /* --> 1 again when data arrives ... */ LOGSCR("reset scr tuning by flush_remote"); reset_scr_tuning(this); /* wait until all data has been received */ while(this->curpos < offset && timeout_ms > 0) { TRACE("FLUSH: wait position (%" PRIu64 " ; need %" PRIu64 ")", this->curpos, offset); pthread_mutex_unlock(&this->lock); xine_usec_sleep(3*1000); pthread_mutex_lock(&this->lock); timeout_ms -= 3; } LOGSCR("reset scr tuning by flush_remote"); reset_scr_tuning(this); pthread_mutex_unlock(&this->lock); r = vdr_plugin_flush(this, MAX(5, timeout_ms)); printf_control(this, "RESULT %d %d\r\n", this->token, r); pthread_mutex_lock(&this->lock); this->live_mode = live_mode; this->stream->metronom->set_option(this->stream->metronom, METRONOM_PREBUFFER, METRONOM_PREBUFFER_VAL); this->guard_index = offset; pthread_mutex_unlock(&this->lock); return CONTROL_OK; } static int is_lang_code(const char *s, int len) { while (len--) if (!islower(*(s++))) return 0; return !isalpha(*s); } static int vdr_plugin_parse_control(vdr_input_plugin_if_t *this_if, const char *cmd) { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_if; int err = CONTROL_OK; int /*int32_t*/ tmp32 = 0; uint64_t tmp64 = 0ULL; xine_stream_t *stream = this->stream; static const char str_poll[] = "POLL"; char *pt; VDR_ENTRY_LOCK(CONTROL_DISCONNECTED); LOGCMD("vdr_plugin_parse_control(): %s", cmd); if( !memcmp(cmd, str_poll, 4) || !strncasecmp(cmd, "POLL ", 5)) { tmp32 = atoi(cmd+5); if(tmp32 >= 0 && tmp32 < 1000) { if(this->fd_control >= 0) { printf_control(this, "POLL %d\r\n", vdr_plugin_poll(this, tmp32)); } else { err = vdr_plugin_poll(this, tmp32); } } else { err = CONTROL_PARAM_ERROR; } VDR_ENTRY_UNLOCK(); return err; } if (this->slave.stream) stream = this->slave.stream; if(NULL != (pt = strstr(cmd, "\r\n"))) *((char*)pt) = 0; /* auts */ LOGVERBOSE(" %s",cmd); if(!strncasecmp(cmd, "OSDCMD", 6)) { err = handle_control_osdcmd(this); } else if(!strncasecmp(cmd, "VIDEO_PROPERTIES ", 17)) { int hue, saturation, brightness, sharpness, noise_reduction, contrast, vo_aspect_ratio; if(7 == sscanf(cmd+17, "%d %d %d %d %d %d %d", &hue, &saturation, &brightness, &sharpness, &noise_reduction, &contrast, &vo_aspect_ratio)) err = set_video_properties(this, hue, saturation, brightness, sharpness, noise_reduction, contrast, vo_aspect_ratio); else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "OVERSCAN ", 9)) { if(!this->funcs.fe_control) LOGMSG("No fe_control function! %s failed.", cmd); else this->funcs.fe_control(this->funcs.fe_handle, cmd); } else if(!strncasecmp(cmd, "VO_ASPECT ", 10)) { if(1 == sscanf(cmd+10, "%d", &tmp32)) { xine_set_param(stream, XINE_PARAM_VO_ASPECT_RATIO, tmp32); } else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "DEINTERLACE ", 12)) { if(this->fd_control < 0) err = set_deinterlace_method(this, cmd+12); } else if(!strncasecmp(cmd, "EVENT ", 6)) { unsigned i; char *pt = strchr(cmd, '\n'); if(pt) *pt=0; pt = strstr(cmd+6, " CHAPTER"); if(pt) { *pt = 0; this->class->xine->config->update_num(this->class->xine->config, "media.dvd.skip_behaviour", 1); this->class->xine->config->update_num(this->class->xine->config, "media.bluray.skip_behaviour", 0); } pt = strstr(cmd+6, " TITLE"); if(pt) { *pt = 0; this->class->xine->config->update_num(this->class->xine->config, "media.dvd.skip_behaviour", 2); this->class->xine->config->update_num(this->class->xine->config, "media.bluray.skip_behaviour", 1); } for(i=0; islave.stream ?: this->stream, /* tag event to prevent circular input events (vdr -> here -> event_listener -> vdr -> ...) */ .data = "VDR", .data_length = 4, }; xine_event_send(ev.stream, &ev); break; } } else if(!strncasecmp(cmd, "VERSION ", 7)) { if(strncmp(XINELIBOUTPUT_VERSION " ", cmd+8, strlen(XINELIBOUTPUT_VERSION)+1)) { if(this->fd_control < 0) { /* Check should use protocol version. * In remote mode check is done in connect */ LOGMSG("WARNING! xineplug_inp_xvdr.so and libvdr-xineliboutput.so " "are from different version (%s and %s)", XINELIBOUTPUT_VERSION, cmd+8); LOGMSG("Re-install plugin !"); /*abort();*/ } } } else if(!strncasecmp(cmd, "HDMODE ", 7)) { if(1 == sscanf(cmd+7, "%d", &tmp32)) { pthread_mutex_lock(&this->lock); if (tmp32 && !this->hd_stream) { cfg_entry_t *e = this->class->xine->config->lookup_entry(this->class->xine->config, "engine.buffers.video_num_frames"); if (e && e->num_value < 32) { LOGMSG("WARNING: xine-engine setting \"engine.buffers.video_num_frames\":%d is " "too small for some HD channels", e->num_value); } } if(tmp32) { if(!this->hd_buffer) this->hd_buffer = fifo_buffer_new(this->stream, this->class->num_buffers_hd, HD_BUF_ELEM_SIZE); this->hd_stream = 1; } else { this->hd_stream = 0; } set_buffer_limits(this); pthread_mutex_unlock(&this->lock); } } else if(!strncasecmp(cmd, "NOVIDEO ", 8)) { if(1 == sscanf(cmd+8, "%d", &tmp32)) { pthread_mutex_lock(&this->lock); this->no_video = tmp32; set_buffer_limits(this); #if 0 if (tmp32) this->metronom->unwire(this->metronom); else this->metronom->wire(this->metronom); #endif pthread_mutex_unlock(&this->lock); } else err = CONTROL_PARAM_ERROR; signal_buffer_pool_not_empty(this); } else if(!strncasecmp(cmd, "DISCARD ", 8)) { if(2 == sscanf(cmd+8, "%" PRIu64 " %d", &tmp64, &tmp32)) { pthread_mutex_lock(&this->lock); if(this->discard_index < tmp64) { this->discard_frame = tmp32; vdr_flush_engine(this, tmp64); this->discard_index = tmp64; } else if(this->discard_index != tmp64) { LOGMSG("Ignoring delayed control message %s", cmd); } pthread_cond_broadcast(&this->engine_flushed); pthread_mutex_unlock(&this->lock); } else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "STREAMPOS ", 10)) { if(1 == sscanf(cmd+10, "%" PRIu64, &tmp64)) { pthread_mutex_lock(&this->lock); vdr_flush_engine(this, tmp64); this->curpos = tmp64; this->discard_index = this->curpos; this->guard_index = 0; pthread_mutex_unlock(&this->lock); } else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "TRICKSPEED ", 11)) { if (1 == sscanf(cmd+11, "%d", &tmp32)) { pthread_mutex_lock(&this->lock); set_trick_speed(this, tmp32, !!strstr(cmd+11, "Back")); pthread_mutex_unlock(&this->lock); } else { err = CONTROL_PARAM_ERROR; } } else if(!strncasecmp(cmd, "STILL ", 6)) { if(1 == sscanf(cmd+6, "%d", &tmp32)) { pthread_mutex_lock(&this->lock); set_still_mode(this, tmp32); pthread_mutex_unlock(&this->lock); } else { err = CONTROL_PARAM_ERROR; } } else if(!strncasecmp(cmd, "SCR ", 4)) { pthread_mutex_lock(&this->lock); if(1 == sscanf(cmd, "SCR Sync %d", &tmp32)) { this->scr_live_sync = 1; this->scr->set_speed_base(this->scr, tmp32); } else if(1 == sscanf(cmd, "SCR NoSync %d", &tmp32)) { this->scr_live_sync = 0; this->scr->set_speed_base(this->scr, tmp32); reset_scr_tuning(this); } pthread_mutex_unlock(&this->lock); } else if(!strncasecmp(cmd, "LIVE ", 5)) { if (1 == sscanf(cmd+5, "%d", &tmp32)) { pthread_mutex_lock(&this->lock); set_live_mode(this, tmp32); pthread_mutex_unlock(&this->lock); } else { err = CONTROL_PARAM_ERROR; } } else if(!strncasecmp(cmd, "MASTER ", 7)) { if(1 == sscanf(cmd+7, "%d", &tmp32)) this->fixed_scr = !!tmp32; else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "VOLUME ", 7)) { if(1 == sscanf(cmd+7, "%d", &tmp32)) { int sw = !!strstr(cmd, "SW"); if(!sw) { xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME, tmp32); xine_set_param(stream, XINE_PARAM_AUDIO_MUTE, tmp32<=0 ? 1 : 0); } else { xine_set_param(stream, XINE_PARAM_AUDIO_AMP_LEVEL, tmp32); xine_set_param(stream, XINE_PARAM_AUDIO_AMP_MUTE, tmp32<=0 ? 1 : 0); } if(sw != this->sw_volume_control) { this->sw_volume_control = sw; if(sw) { /* * XXX make sure libxine's internal copy of the mixer * volume is initialized before using XINE_PARAM_AUDIO_MUTE... * (this fixes mixer volume being reset to 45 here every time * at vdr-sxfe start when using software volume control.) */ tmp32 = xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME); xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME, tmp32); xine_set_param(stream, XINE_PARAM_AUDIO_MUTE, 0); } else { xine_set_param(stream, XINE_PARAM_AUDIO_AMP_LEVEL, 100); xine_set_param(stream, XINE_PARAM_AUDIO_AMP_MUTE, 0); } } } else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "AUDIOCOMPRESSION ",17)) { if(1 == sscanf(cmd+17, "%d", &tmp32)) { xine_set_param(stream, XINE_PARAM_AUDIO_COMPR_LEVEL, tmp32); } else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "AUDIOSURROUND ",14)) { if(1 == sscanf(cmd+14, "%d", &tmp32)) { this->class->xine->config->update_num(this->class->xine->config, "audio.a52.surround_downmix", tmp32?1:0); } else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "SPEAKERS ",9)) { if(1 == sscanf(cmd+9, "%d", &tmp32)) { if (this->fd_control < 0) this->class->xine->config->update_num(this->class->xine->config, "audio.output.speaker_arrangement", tmp32); } else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "EQUALIZER ", 10)) { int eqs[XINE_PARAM_EQ_16000HZ - XINE_PARAM_EQ_30HZ + 2] = {0}, i, j; sscanf(cmd+10,"%d %d %d %d %d %d %d %d %d %d", eqs,eqs+1,eqs+2,eqs+3,eqs+4,eqs+5,eqs+6,eqs+7,eqs+8,eqs+9); for(i=XINE_PARAM_EQ_30HZ,j=0; i<=XINE_PARAM_EQ_16000HZ; i++,j++) xine_set_param(stream, i, eqs[j]); } else if(!strncasecmp(cmd, "AUDIOSTREAM ", 12)) { if (!this->slave.stream) { #if 0 int ac3 = !strncmp(cmd+12, "AC3", 3); if(1 == sscanf(cmd+12 + 4*ac3, "%d", &tmp32)) { pthread_mutex_lock(&this->lock); this->audio_stream_id = tmp32; pthread_mutex_unlock(&this->lock); } else { err = CONTROL_PARAM_ERROR; } #endif } else { if(1 == sscanf(cmd+12, "AC3 %d", &tmp32)) { tmp32 &= 0xff; LOGDBG("Audio channel -> [%d]", tmp32); xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, tmp32); } LOGDBG("Audio channel selected: [%d]", _x_get_audio_channel (stream)); } } else if(!strncasecmp(cmd, "SPUSTREAM ", 10)) { int old_ch = _x_get_spu_channel(stream); int max_ch = xine_get_stream_info(stream, XINE_STREAM_INFO_MAX_SPU_CHANNEL); int ch = old_ch; int ch_auto = !!strstr(cmd+10, "auto"); int is_dvd = 0; if (this->slave.stream && this->slave.stream->input_plugin) { const char *mrl = this->slave.stream->input_plugin->get_mrl(this->slave.stream->input_plugin); is_dvd = !strncmp(mrl, "dvd:/", 5) || !strncmp(mrl, "bd:/", 4) || !strncmp(mrl, "bluray:/", 8); } if(strstr(cmd+10, "NEXT")) ch = ch < max_ch ? ch+1 : -2; else if(strstr(cmd+10, "PREV")) ch = ch > -2 ? ch-1 : max_ch-1; else if(1 == sscanf(cmd+10, "%d", &tmp32)) { ch = tmp32; } else if(is_lang_code(cmd+10, 2)) { /* ISO 639-1 language code */ const char spu_lang[3] = {cmd[10], cmd[11], 0}; LOGMSG("Preferred SPU language: %s", spu_lang); this->class->xine->config->update_string(this->class->xine->config, "media.dvd.language", spu_lang); ch = old_ch = 0; } else if(is_lang_code(cmd+10, 3)) { /* ISO 639-2 language code */ const char spu_lang[4] = {cmd[10], cmd[11], cmd[12], 0}; LOGMSG("Preferred SPU language: %s", spu_lang); this->class->xine->config->update_string(this->class->xine->config, "media.bluray.language", spu_lang); ch = old_ch = 0; } else err = CONTROL_PARAM_ERROR; if (old_ch == SPU_CHANNEL_AUTO) old_ch = stream->spu_channel_auto; if (ch != old_ch) { if (is_dvd && ch_auto && stream->spu_channel_user == SPU_CHANNEL_AUTO) { LOGDBG("Automatic SPU channel %d->%d ignored", old_ch, ch); } else { LOGDBG("Forced SPU channel %d->%d", old_ch, ch); select_spu_channel(stream, ch); } LOGDBG("SPU channel selected: [%d]", _x_get_spu_channel (stream)); } } else if(!strncasecmp(cmd, "AUDIODELAY ", 11)) { if(1 == sscanf(cmd+11, "%d", &tmp32)) xine_set_param(stream, XINE_PARAM_AV_OFFSET, tmp32*90000/1000); else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "SYNC", 4)) { if(this->fd_control >= 0) printf_control(this, "RESULT %d 1\r\n", this->token); } else if(!strncasecmp(cmd, "GETSTC", 6)) { int64_t pts = -1; pts = stream->metronom->get_option(stream->metronom, XVDR_METRONOM_LAST_VO_PTS); if (pts <= 0) { pts = xine_get_current_vpts(stream) - stream->metronom->get_option(stream->metronom, METRONOM_VPTS_OFFSET); } if(this->fd_control >= 0) { printf_control(this, "STC %" PRId64 "\r\n", pts); } else { *((int64_t *)cmd) = pts; } } else if(!strncasecmp(cmd, "FLUSH ", 6)) { if(1 == sscanf(cmd+6, "%d", &tmp32)) { if(this->fd_control >= 0) { uint32_t frame = 0; tmp64 = 0ULL; tmp32 = 0; sscanf(cmd+6, "%d %" PRIu64 " %d", &tmp32, &tmp64, &frame); err = vdr_plugin_flush_remote(this, tmp32, tmp64, frame); } else { err = vdr_plugin_flush(this, tmp32); } } else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "TOKEN ", 6)) { if(1 == sscanf(cmd+6, "%d", &tmp32)) this->token = tmp32; else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "SUBSTREAM ", 9)) { err = handle_control_substream(this, cmd); } else if(!strncasecmp(cmd, "POST ", 5)) { /* lock demuxer thread out of adjust_realtime_speed */ pthread_mutex_lock(&this->lock); if(!this->funcs.fe_control) LOGMSG("No fe_control function! %s failed.", cmd); else this->funcs.fe_control(this->funcs.fe_handle, cmd); pthread_mutex_unlock(&this->lock); } else if(!strncasecmp(cmd, "PLAYFILE ", 9)) { err = handle_control_playfile(this, cmd); if(this->fd_control >= 0) { printf_control(this, "RESULT %d %d\r\n", this->token, err); err = CONTROL_OK; } } else if(!strncasecmp(cmd, "SEEK ", 5)) { if (this->slave.stream) { int pos_stream=0, pos_time=0, length_time=0; xine_get_pos_length(this->slave.stream, &pos_stream, &pos_time, &length_time); if(cmd[5]=='+') pos_time += atoi(cmd+6) * 1000; else if(cmd[5]=='-') pos_time -= atoi(cmd+6) * 1000; else pos_time = atoi(cmd+5) * 1000; err = xine_play (this->slave.stream, 0, pos_time); if(this->fd_control >= 0) err = CONTROL_OK; } } else if(!strncasecmp(cmd, "GETLENGTH", 9)) { int pos_stream=0, pos_time=0, length_time=0; xine_get_pos_length(stream, &pos_stream, &pos_time, &length_time); err = length_time/*/1000*/; if(this->fd_control >= 0) { printf_control(this, "RESULT %d %d\r\n", this->token, err); err = CONTROL_OK; } } else if(!strncasecmp(cmd, "GETAUTOPLAYSIZE", 15)) { if (cmd[15]==' ' && cmd[16]) { /* query from specific input plugin */ const char *cls_name = cmd + 16; this->autoplay_size = 0; if (! xine_get_browse_mrls (stream->xine, cls_name, NULL, &this->autoplay_size)) /* try older method */ xine_get_autoplay_mrls(stream->xine, cls_name, &this->autoplay_size); } if(this->autoplay_size < 0) { if (this->slave.stream && this->slave.stream->input_plugin && this->slave.stream->input_plugin->input_class) this->slave.stream->input_plugin->input_class-> get_autoplay_list(this->slave.stream->input_plugin->input_class, &this->autoplay_size); } err = this->autoplay_size; if(this->fd_control >= 0) { printf_control(this, "RESULT %d %d\r\n", this->token, err); err = CONTROL_OK; } } else if(!strncasecmp(cmd, "GETPOS", 6)) { int pos_stream=0, pos_time=0, length_time=0; xine_get_pos_length(stream, &pos_stream, &pos_time, &length_time); err = pos_time/*/1000*/; if(this->fd_control >= 0) { printf_control(this, "RESULT %d %d\r\n", this->token, err); err = CONTROL_OK; } } else if(!strncasecmp(cmd, "SUBTITLES ", 10)) { if (this->slave.stream) { int vpos = 0; if(1 == sscanf(cmd+10, "%d", &vpos)) this->class->xine->config->update_num(this->class->xine->config, "subtitles.separate.vertical_offset", vpos); else err = CONTROL_PARAM_ERROR; } } else if(!strncasecmp(cmd, "EXTSUBSIZE ", 11)) { int size = 0; if(1 == sscanf(cmd+11, "%d", &size)) /* size of separate subtitles : -1 = xine default 0...6 = { tiny small normal large very large huge } */ this->class->xine->config->update_num(this->class->xine->config, "subtitles.separate.subtitle_size", size); else err = CONTROL_PARAM_ERROR; } else if(!strncasecmp(cmd, "CONFIG END", 10)) { this->config_ok = 1; } else if(!strncasecmp(cmd, "GRAB ", 5)) { handle_control_grab(this, cmd); /* next ones need to be synchronized to data stream */ } else if(!strncasecmp(cmd, "BLANK", 5)) { put_control_buf(this->block_buffer, this->buffer_pool, CONTROL_BUF_BLANK); } else if(!strncasecmp(cmd, "CLEAR", 5)) { /* #warning should be delayed and executed in read_block */ } else { LOGMSG("vdr_plugin_parse_control(): unknown control %s", cmd); err = CONTROL_UNKNOWN; } LOGCMD("vdr_plugin_parse_control(): DONE (%d): %s", err, cmd); VDR_ENTRY_UNLOCK(); return err; } static void *vdr_control_thread(void *this_gen) { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_gen; char line[8128]; int err; int counter = 100; LOGDBG("Control thread started"); /*(void)nice(-1);*/ /* wait until state changes from open to play */ while(bSymbolsFound && counter>0 && ! this->funcs.fe_control) { xine_usec_sleep(50*1000); counter--; } if (this->osd_manager && this->osd_manager->argb_supported(this->stream)) { LOGMSG("ARGB OSD supported by video driver"); puts_vdr(this, "INFO ARGBOSD\r\n"); } write_control(this, "CONFIG\r\n"); while(this->control_running) { /* read next command */ line[0] = 0; pthread_testcancel(); if((err=readline_control(this, line, sizeof(line)-1, -1)) <= 0) { if(err < 0) break; continue; } LOGCMD("Received command %s",line); pthread_testcancel(); if(!this->control_running) break; /* parse */ switch(err = vdr_plugin_parse_control(&this->iface, line)) { case CONTROL_OK: break; case CONTROL_UNKNOWN: LOGMSG("unknown control message %s", line); break; case CONTROL_PARAM_ERROR: LOGMSG("invalid parameter in control message %s", line); break; case CONTROL_DISCONNECTED: LOGMSG("control stream read error - disconnected ?"); this->control_running = 0; break; default: LOGMSG("parse_control failed with result: %d", err); break; } } if(this->control_running) write_control(this, "CLOSE\r\n"); this->control_running = 0; if (this->slave.stream) { xine_stop(this->slave.stream); } LOGDBG("Control thread terminated"); pthread_exit(NULL); } /**************************** Control to VDR ********************************/ static void update_dvd_title_number(vdr_input_plugin_t *this) { /* DVD title number and menu domain detection */ #ifdef XINE_STREAM_INFO_DVD_TITLE_NUMBER int tn = _x_stream_info_get(this->slave.stream, XINE_STREAM_INFO_DVD_TITLE_NUMBER); int tc = _x_stream_info_get(this->slave.stream, XINE_STREAM_INFO_DVD_TITLE_COUNT); if (tn >= 0 && tc > 0) { if (tn == 0) dvd_menu_domain(this, 1); printf_vdr(this, "INFO DVDTITLE %d/%d\r\n", tn, tc); } #endif } static const char *trim_str(const char *s) { while (*s == ' ' || *s == '\r' || *s == '\n') s++; return s; } static void slave_track_maps_changed(vdr_input_plugin_t *this) { char tracks[1024], lang[128]; int i, current, n = 0; size_t cnt; /* DVD title and menu domain detection */ update_dvd_title_number(this); /* Audio tracks */ strcpy(tracks, "INFO TRACKMAP AUDIO "); cnt = strlen(tracks); current = xine_get_param(this->slave.stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL); for(i=0; i<32 && cntslave.stream, i, lang)) { cnt += snprintf(tracks+cnt, sizeof(tracks)-cnt-32, "%s%d:%s ", i==current?"*":"", i, trim_str(lang)); n++; } else if (i < this->slave.stream->audio_track_map_entries) { cnt += snprintf(tracks+cnt, sizeof(tracks)-cnt-32, "%s%d:%d ", i==current?"*":"", i, i); n++; } tracks[sizeof(tracks)-1] = 0; if(n>1) LOGDBG("%s", tracks); strcpy(tracks + cnt, "\r\n"); puts_vdr(this, tracks); /* DVD SPU tracks */ n = 0; strcpy(tracks, "INFO TRACKMAP SPU "); cnt = strlen(tracks); current = _x_get_spu_channel (this->slave.stream); if(current < 0) { /* -2 == none, -1 == auto */ cnt += snprintf(tracks+cnt, sizeof(tracks)-cnt-32, "*%d:%s ", current, current==SPU_CHANNEL_NONE ? "none" : "auto"); n++; if(current == SPU_CHANNEL_AUTO) current = this->slave.stream->spu_channel_auto; } for(i=0; i<32 && cntslave.stream, i, lang)) { cnt += snprintf(tracks+cnt, sizeof(tracks)-cnt-32, "%s%d:%s ", i==current?"*":"", i, trim_str(lang)); n++; } tracks[sizeof(tracks)-1] = 0; if(n>1) LOGDBG("%s", tracks); strcpy(tracks + cnt, "\r\n"); puts_vdr(this, tracks); } /* Map some xine input events to vdr input (remote key names) */ static const struct { const uint32_t event; const char name[12]; } vdr_keymap[] = { {XINE_EVENT_INPUT_NEXT, "Next"}, {XINE_EVENT_INPUT_PREVIOUS, "Previous"}, {XINE_EVENT_INPUT_DOWN, "Down"}, {XINE_EVENT_INPUT_UP, "Up"}, {XINE_EVENT_INPUT_LEFT, "Left"}, {XINE_EVENT_INPUT_RIGHT, "Right"}, {XINE_EVENT_INPUT_SELECT, "Ok"}, {XINE_EVENT_INPUT_MENU1, "Menu"}, {XINE_EVENT_INPUT_MENU2, "Red"}, {XINE_EVENT_INPUT_MENU3, "Green"}, {XINE_EVENT_INPUT_MENU4, "Yellow"}, {XINE_EVENT_INPUT_MENU5, "Blue"}, {XINE_EVENT_INPUT_NUMBER_0, "0"}, {XINE_EVENT_INPUT_NUMBER_1, "1"}, {XINE_EVENT_INPUT_NUMBER_2, "2"}, {XINE_EVENT_INPUT_NUMBER_3, "3"}, {XINE_EVENT_INPUT_NUMBER_4, "4"}, {XINE_EVENT_INPUT_NUMBER_5, "5"}, {XINE_EVENT_INPUT_NUMBER_6, "6"}, {XINE_EVENT_INPUT_NUMBER_7, "7"}, {XINE_EVENT_INPUT_NUMBER_8, "8"}, {XINE_EVENT_INPUT_NUMBER_9, "9"}, #if defined(XINE_EVENT_VDR_RED) {XINE_EVENT_VDR_BACK, "Back"}, {XINE_EVENT_VDR_CHANNELPLUS, "Channel+"}, {XINE_EVENT_VDR_CHANNELMINUS, "Channel-"}, {XINE_EVENT_VDR_RED, "Red"}, {XINE_EVENT_VDR_GREEN, "Green"}, {XINE_EVENT_VDR_YELLOW, "Yellow"}, {XINE_EVENT_VDR_BLUE, "Blue"}, {XINE_EVENT_VDR_PLAY, "Play"}, {XINE_EVENT_VDR_PAUSE, "Pause"}, {XINE_EVENT_VDR_STOP, "Stop"}, {XINE_EVENT_VDR_RECORD, "Record"}, {XINE_EVENT_VDR_FASTFWD, "FastFwd"}, {XINE_EVENT_VDR_FASTREW, "FastRew"}, {XINE_EVENT_VDR_POWER, "Power"}, {XINE_EVENT_VDR_SCHEDULE, "Schedule"}, {XINE_EVENT_VDR_CHANNELS, "Channels"}, {XINE_EVENT_VDR_TIMERS, "Timers"}, {XINE_EVENT_VDR_RECORDINGS, "Recordings"}, {XINE_EVENT_VDR_SETUP, "Setup"}, {XINE_EVENT_VDR_COMMANDS, "Commands"}, {XINE_EVENT_VDR_USER1, "User1"}, {XINE_EVENT_VDR_USER2, "User2"}, {XINE_EVENT_VDR_USER3, "User3"}, {XINE_EVENT_VDR_USER4, "User4"}, {XINE_EVENT_VDR_USER5, "User5"}, {XINE_EVENT_VDR_USER6, "User6"}, {XINE_EVENT_VDR_USER7, "User7"}, {XINE_EVENT_VDR_USER8, "User8"}, {XINE_EVENT_VDR_USER9, "User9"}, {XINE_EVENT_VDR_VOLPLUS, "Volume+"}, {XINE_EVENT_VDR_VOLMINUS, "Volume-"}, {XINE_EVENT_VDR_MUTE, "Mute"}, {XINE_EVENT_VDR_AUDIO, "Audio"}, #endif #if defined(XINE_EVENT_VDR_INFO) {XINE_EVENT_VDR_INFO, "Info"}, #endif #if defined(XINE_EVENT_VDR_SUBTITLES) {XINE_EVENT_VDR_SUBTITLES, "Subtitles"}, #endif }; static void vdr_event_cb (void *user_data, const xine_event_t *event) { vdr_input_plugin_t *this = (vdr_input_plugin_t *)user_data; unsigned i; for (i = 0; i < sizeof(vdr_keymap) / sizeof(vdr_keymap[0]); i++) { if ((uint32_t)event->type == vdr_keymap[i].event) { if (event->data && event->data_length == 4 && !strncmp(event->data, "VDR", 4)) { /*LOGMSG("Input event created by self, ignoring");*/ return; } LOGDBG("XINE_EVENT (input) %d --> %s", event->type, vdr_keymap[i].name); if (this->fd_control >= 0) { /* remote mode: -> input_plugin -> connection -> VDR */ printf_control(this, "KEY %s\r\n", vdr_keymap[i].name); } if (this->funcs.xine_input_event) { /* local mode: -> VDR */ this->funcs.xine_input_event(this->funcs.fe_handle, NULL, vdr_keymap[i].name); } return; } } switch (event->type) { case XINE_EVENT_UI_SET_TITLE: if (event->stream == this->slave.stream) { xine_ui_data_t *data = (xine_ui_data_t *)event->data; LOGMSG("XINE_EVENT_UI_SET_TITLE: %s", data->str); update_dvd_title_number(this); printf_vdr(this, "INFO TITLE %s\r\n", data->str); break; } case XINE_EVENT_UI_NUM_BUTTONS: if (event->stream == this->slave.stream) { xine_ui_data_t *data = (xine_ui_data_t*)event->data; dvd_menu_domain(this, data->num_buttons > 0); printf_vdr(this, "INFO DVDBUTTONS %d\r\n", data->num_buttons); break; } case XINE_EVENT_UI_CHANNELS_CHANGED: if (event->stream==this->slave.stream) { slave_track_maps_changed(this); } break; case XINE_EVENT_FRAME_FORMAT_CHANGE: { xine_format_change_data_t *frame_change = (xine_format_change_data_t *)event->data; LOGOSD("XINE_EVENT_FRAME_FORMAT_CHANGE (%dx%d, aspect=%d)", frame_change->width, frame_change->height, frame_change->aspect); if (!frame_change->aspect) /* from frontend */ this->osd_manager->video_size_changed(this->osd_manager, event->stream, frame_change->width, frame_change->height); } break; case XINE_EVENT_UI_PLAYBACK_FINISHED: if(event->stream == this->stream) { LOGDBG("XINE_EVENT_UI_PLAYBACK_FINISHED"); this->control_running = 0; #if 1 if(iSysLogLevel >= SYSLOGLEVEL_DEBUG) { /* dump whole xine log as we should not be here ... */ xine_t *xine = this->class->xine; int i, j; int logs = xine_get_log_section_count(xine); const char * const * names = xine_get_log_names(xine); for(i=0; ilock); if (event->stream == this->slave.stream) { LOGMSG("XINE_EVENT_UI_PLAYBACK_FINISHED (slave stream)"); if (this->fd_control >= 0) { write_control(this, "ENDOFSTREAM\r\n"); } else { if (this->funcs.fe_control) this->funcs.fe_control(this->funcs.fe_handle, "ENDOFSTREAM\r\n"); } } else if(event->stream == this->bg_stream.stream) { LOGMSG("XINE_EVENT_UI_PLAYBACK_FINISHED (background stream)"); xine_play(this->bg_stream.stream, 0, 0); } pthread_mutex_unlock(&this->lock); break; default: LOGCMD("Got an xine event, type 0x%08x", event->type); break; } } /**************************** Data Stream *********************************/ /* * wait_stream_sync() * * Wait until data and control streams have reached the same sync point. * - wait_stream_sync() is called only from data thread. * - data stream handling is suspended until control stream has * reached the same sync point (and xine engine has been flushed). * * Function is interrupred when * - control thread has been terminated * - demux_action_pending signal is set * * return value: * 1: Both streams have reached the same sync point * 0: timeout (errno = EAGAIN) or * interrupted (errno = EINTR) or * disconnected (errno = ENOTCONN) */ static int wait_stream_sync(vdr_input_plugin_t *this) { int counter = 100; mutex_lock_cancellable(&this->lock); if (this->discard_index < this->discard_index_ds) LOGVERBOSE("wait_stream_sync: waiting for engine_flushed condition %"PRIu64"<%"PRIu64, this->discard_index, this->discard_index_ds); while(this->control_running && this->discard_index < this->discard_index_ds && !_x_action_pending(this->stream) && --counter > 0) { struct timespec abstime; create_timeout_time(&abstime, 10); pthread_cond_timedwait(&this->engine_flushed, &this->lock, &abstime); } if (this->discard_index != this->curpos) { LOGMSG("wait_stream_sync: discard_index %"PRIu64" != curpos %"PRIu64" ! (diff %"PRId64")", this->discard_index, this->curpos, (int64_t)(this->discard_index - this->curpos)); } mutex_unlock_cancellable(&this->lock); if (this->discard_index == this->discard_index_ds) { LOGVERBOSE("wait_stream_sync: streams synced at %"PRIu64"/%"PRIu64, this->discard_index_ds, this->discard_index); return 0; } if (!this->control_running) { errno = ENOTCONN; } else if (_x_action_pending(this->stream)) { LOGVERBOSE("wait_stream_sync: demux_action_pending set"); errno = EINTR; } else if (counter <= 0) { LOGMSG("wait_stream_sync: Timed out ! diff %"PRId64, (int64_t)(this->discard_index - this->discard_index_ds)); errno = EAGAIN; } return 1; } static void data_stream_parse_control(vdr_input_plugin_t *this, char *cmd) { char *tmp; cmd[64] = 0; if(NULL != (tmp=strchr(cmd, '\r'))) *tmp = '\0'; if(NULL != (tmp=strchr(cmd, '\n'))) *tmp = '\0'; LOGVERBOSE(" %s", cmd); if(!strncasecmp(cmd, "DISCARD ", 8)) { uint64_t index; if(1 == sscanf(cmd+8, "%" PRIu64, &index)) { this->discard_index_ds = index; this->block_buffer->clear(this->block_buffer); wait_stream_sync(this); } return; } else if(!strncasecmp(cmd, "BLANK", 5)) { put_control_buf(this->block_buffer, this->buffer_pool, CONTROL_BUF_BLANK); return; } LOGMSG("Unexpected data_stream_parse_control(%s)", cmd); vdr_plugin_parse_control(&this->iface, cmd); } /* * vdr_plugin_read_block_tcp() * * - Read single transport block from socket / pipe. * * Returns NULL if read failed or data is not available. * (sets errno to EAGAIN, EINTR or ENOTCONN) * */ static buf_element_t *vdr_plugin_read_block_tcp(vdr_input_plugin_t *this) { buf_element_t *read_buffer = this->read_buffer; int todo = sizeof(stream_tcp_header_t); int warnings = 0; int result, n; if (this->discard_index < this->discard_index_ds && wait_stream_sync(this)) return NULL; if (read_buffer && read_buffer->size >= (ssize_t)sizeof(stream_tcp_header_t)) todo += ((stream_tcp_header_t *)read_buffer->content)->len; while (XIO_READY == (result = _x_io_select(this->stream, this->fd_data, XIO_READ_READY, 100))) { if (!this->control_running || this->fd_data < 0) { errno = ENOTCONN; return NULL; } if (_x_action_pending(this->stream)) { errno = EINTR; return NULL; } /* Allocate buffer */ if (!read_buffer) { this->read_buffer = read_buffer = get_buf_element_timed(this, 2048+sizeof(stream_tcp_header_t), 100); if (!read_buffer) { /* do not drop any data here ; dropping is done only at server side. */ if (!this->is_paused && !warnings) LOGDBG("TCP: fifo buffer full"); warnings++; continue; /* must call select to check fd for errors / closing */ } /* read the header first */ todo = sizeof(stream_tcp_header_t); read_buffer->size = 0; warnings = 0; } /* Read data */ errno = 0; n = read(this->fd_data, read_buffer->content + read_buffer->size, todo - read_buffer->size); if (n <= 0) { if (!n || (errno != EINTR && errno != EAGAIN)) { if (n < 0 && this->fd_data >= 0) LOGERR("TCP read error (data stream %d : %d)", this->fd_data, n); if (n == 0) LOGMSG("Data stream disconnected"); errno = ENOTCONN; } /* errno == EINTR || errno == EAGAIN || errno == ENOTCONN */ return NULL; } read_buffer->size += n; /* Header complete ? */ if (read_buffer->size == sizeof(stream_tcp_header_t)) { stream_tcp_header_t *hdr = ((stream_tcp_header_t *)read_buffer->content); hdr->len = ntohl(hdr->len); hdr->pos = ntohull(hdr->pos); todo += hdr->len; if (todo + read_buffer->size >= read_buffer->max_size) { LOGMSG("TCP: Buffer too small (%d ; incoming frame %d bytes)", read_buffer->max_size, todo + read_buffer->size); errno = ENOTCONN; return NULL; } } /* Buffer complete ? */ if (read_buffer->size >= todo) { stream_tcp_header_t *hdr = ((stream_tcp_header_t *)read_buffer->content); if (hdr->pos == (uint64_t)(-1ULL) /*0xffffffff ffffffff*/) { /* control data */ uint8_t *pkt_data = read_buffer->content + sizeof(stream_tcp_header_t); if (!DATA_IS_TS(pkt_data) && pkt_data[0]) { /* -> can't be pes or ts frame */ data_stream_parse_control(this, (char*)pkt_data); read_buffer->free_buffer(read_buffer); this->read_buffer = NULL; errno = EAGAIN; return NULL; } } /* frame ready */ read_buffer->type = BUF_NETWORK_BLOCK; this->read_buffer = NULL; return read_buffer; } } errno = (result == XIO_TIMEOUT) ? EAGAIN : (result == XIO_ABORTED) ? EINTR : ENOTCONN; return NULL; } /* * read_socket_udp() * * - Read single transport block from datagram socket * * Returns NULL if read failed or data is not available. * (sets errno to EAGAIN, EINTR or ENOTCONN) * */ static buf_element_t *read_socket_udp(vdr_input_plugin_t *this) { /* * poll for incoming data */ int result = _x_io_select(this->stream, this->fd_data, XIO_READ_READY, 100); if (!this->control_running) { errno = ENOTCONN; return NULL; } if (_x_action_pending(this->stream)) { errno = EINTR; return NULL; } if (result != XIO_READY) { if (result == XIO_ERROR) LOGERR("read_socket_udp(): select() failed"); errno = (result == XIO_TIMEOUT) ? EAGAIN : (result == XIO_ABORTED) ? EINTR : ENOTCONN; return NULL; } /* * allocate buffer */ udp_data_t *udp = this->udp_data; buf_element_t *read_buffer = get_buf_element_timed(this, 2048+sizeof(stream_rtp_header_impl_t), 100); if (!read_buffer) { /* if queue is full, skip (video) frame. Waiting longer for free buffers just makes things worse ... */ if (!this->is_paused) { LOGDBG("UDP Fifo buffer full !"); if (this->scr && !udp->scr_jump_done) { this->scr->jump (this->scr, 40*90); LOGMSG("SCR jump: +40 ms (live=%d, tuning=%d)", this->live_mode, this->scr_tuning); udp->scr_jump_done = 50; } } errno = EAGAIN; return NULL; } if (udp->scr_jump_done) udp->scr_jump_done --; /* * Receive frame from socket and check for errors */ struct sockaddr_in server_address; socklen_t address_len = sizeof(server_address); ssize_t n = recvfrom(this->fd_data, read_buffer->mem, read_buffer->max_size, MSG_TRUNC, &server_address, &address_len); if (n <= 0) { if (!n || (errno != EINTR && errno != EAGAIN)) { LOGERR("read_socket_udp(): recvfrom() failed"); errno = ENOTCONN; } read_buffer->free_buffer(read_buffer); /* errno == EAGAIN || errno == EINTR || errno == ENOTCONN */ return NULL; } /* * check source address */ if ((server_address.sin_addr.s_addr != udp->server_address.sin_addr.s_addr) || server_address.sin_port != udp->server_address.sin_port) { #ifdef LOG_UDP uint32_t tmp_ip = ntohl(server_address.sin_addr.s_addr); LOGUDP("Received data from unknown sender: %d.%d.%d.%d:%d", ((tmp_ip>>24)&0xff), ((tmp_ip>>16)&0xff), ((tmp_ip>>8)&0xff), ((tmp_ip)&0xff), server_address.sin_port); #endif read_buffer->free_buffer(read_buffer); errno = EAGAIN; return NULL; } /* * Check if frame size is valid */ if (this->rtp) { if (n < (ssize_t)sizeof(stream_rtp_header_impl_t)) { LOGMSG("received invalid RTP packet (too short)"); read_buffer->free_buffer(read_buffer); errno = EAGAIN; return NULL; } } else if (n < (ssize_t)sizeof(stream_udp_header_t)) { LOGMSG("received invalid UDP packet (too short)"); read_buffer->free_buffer(read_buffer); errno = EAGAIN; return NULL; } if (n > read_buffer->max_size) { LOGMSG("received too large UDP packet (%zd bytes, buffer is %d bytes)", n, read_buffer->max_size); read_buffer->free_buffer(read_buffer); errno = EAGAIN; return NULL; } /* * */ read_buffer->size = n; read_buffer->type = BUF_NETWORK_BLOCK; return read_buffer; } static buf_element_t *udp_parse_header(buf_element_t *read_buffer, int rtp) { if (rtp) { /* check if RTP header is valid. If not, assume UDP without RTP. */ stream_rtp_header_impl_t *rtp_pkt = (stream_rtp_header_impl_t*)read_buffer->content; if (rtp_pkt->rtp_hdr.raw[0] == (RTP_VERSION_BYTE | RTP_HDREXT_BIT) && ( rtp_pkt->rtp_hdr.raw[1] == RTP_PAYLOAD_TYPE_PES || rtp_pkt->rtp_hdr.raw[1] == RTP_PAYLOAD_TYPE_TS ) && rtp_pkt->hdr_ext.hdr.size == htons(RTP_HEADER_EXT_X_SIZE) && rtp_pkt->hdr_ext.hdr.type == htons(RTP_HEADER_EXT_X_TYPE)) { /* strip RTP header but leave UDP header (carried inside RTP header extension) */ read_buffer->content += sizeof(stream_rtp_header_impl_t) - sizeof(stream_udp_header_t); read_buffer->size -= sizeof(stream_rtp_header_impl_t) - sizeof(stream_udp_header_t); } } stream_udp_header_t *pkt = (stream_udp_header_t*)read_buffer->content; pkt->seq = ntohs(pkt->seq); pkt->pos = ntohull(pkt->pos); return read_buffer; } static buf_element_t *udp_check_packet(buf_element_t *read_buffer) { stream_udp_header_t *pkt = (stream_udp_header_t*)read_buffer->content; uint8_t *pkt_data = read_buffer->content + sizeof(stream_udp_header_t); /* Check for MPEG-TS sync byte or PES header */ if (read_buffer->size > (ssize_t)sizeof(stream_udp_header_t) && !DATA_IS_TS(pkt_data) && (pkt_data[0] || pkt_data[1] || pkt_data[2] != 1)) { LOGMSG("received invalid UDP packet (TS sync byte or PES header missing)"); read_buffer->free_buffer(read_buffer); return NULL; } /* Check if header is valid */ if (pkt->seq > UDP_SEQ_MASK) { LOGMSG("received invalid UDP packet (sequence number too big)"); read_buffer->free_buffer(read_buffer); return NULL; } return read_buffer; } static buf_element_t *udp_parse_control(vdr_input_plugin_t *this, buf_element_t *read_buffer) { stream_udp_header_t *pkt = (stream_udp_header_t*)read_buffer->content; uint8_t *pkt_data = UDP_PAYLOAD(read_buffer->content); /* Check for control messages */ if (/*pkt->seq == (uint16_t)(-1) &&*/ /*0xffff*/ pkt->pos == (uint64_t)(-1ULL) && /*0xffffffff ffffffff*/ pkt_data[0]) { /* -> can't be PES frame */ pkt_data[64] = 0; if (!strncmp((char*)pkt_data, "UDP MISSING", 11)) { /* Re-send failed */ int seq1 = 0; int seq2 = 0; uint64_t rpos = UINT64_C(0); udp_data_t *udp = this->udp_data; sscanf(((char*)pkt_data)+12, "%d-%d %" PRIu64, &seq1, &seq2, &rpos); read_buffer->size = sizeof(stream_udp_header_t); read_buffer->type = BUF_NETWORK_BLOCK; pkt->pos = rpos; LOGUDP("Got UDP MISSING %d-%d (currseq=%d)", seq1, seq2, udp->next_seq); if (seq1 == udp->next_seq) { /* this is the one we are expecting ... */ int n = ADDSEQ(seq2 + 1, -seq1); udp->missed_frames += n; seq2 &= UDP_SEQ_MASK; pkt->seq = seq2; udp->next_seq = seq2; LOGUDP(" accepted: now currseq %d", udp->next_seq); /* -> drop frame thru as empty ; it will trigger queue to continue */ } else { LOGUDP(" UDP packet rejected: not expected seq ???"); read_buffer->free_buffer(read_buffer); return NULL; } } else { data_stream_parse_control(this, (char*)pkt_data); read_buffer->free_buffer(read_buffer); return NULL; } } return read_buffer; } static buf_element_t *udp_process_queue(vdr_input_plugin_t *this) { udp_data_t *udp = this->udp_data; if (udp->queued <= 0) return NULL; /* * Stay inside receiving window: * if window exceeded, skip missing frames */ if (udp->queued > ((UDP_SEQ_MASK+1)>>2)) { #ifdef LOG_UDP int start = udp->next_seq; #endif while (!udp->queue[udp->next_seq]) { INCSEQ(udp->next_seq); udp->missed_frames++; } udp->resend_requested = 0; LOGUDP("Re-ordering window exceeded, skipped missed frames %d-%d", start, udp->next_seq-1); } /* * flush all packets when idle padding found */ if (udp->is_padding && udp->queued > 0) while (!udp->queue[udp->next_seq]) { INCSEQ(udp->next_seq); udp->missed_frames++; } /* * return next packet if available */ while (udp->queued > 0 && udp->queue[udp->next_seq]) { buf_element_t *buf = NULL; stream_udp_header_t *pkt = (stream_udp_header_t*)udp->queue[udp->next_seq]->content; udp->queue_input_pos = pkt->pos + udp->queue[udp->next_seq]->size - sizeof(stream_udp_header_t); if (udp->queue[udp->next_seq]->size > (ssize_t)sizeof(stream_udp_header_t)) buf = udp->queue[udp->next_seq]; else udp->queue[udp->next_seq]->free_buffer(udp->queue[udp->next_seq]); udp->queue[udp->next_seq] = NULL; udp->queued --; INCSEQ(udp->next_seq); if (udp->resend_requested) udp->resend_requested --; /* flush all packets when idle padding found */ if (udp->is_padding && udp->queued > 0) while (!udp->queue[udp->next_seq]) { INCSEQ(udp->next_seq); udp->missed_frames++; } if (buf) return buf; } errno = EAGAIN; return NULL; } static void udp_process_resend(vdr_input_plugin_t *this) { udp_data_t *udp = this->udp_data; /* no new resend requests until previous has been completed or failed */ if (udp->resend_requested) return; /* If frames are missing, request re-send */ if (NEXTSEQ(udp->current_seq) != udp->next_seq && udp->queued) { int max_req = 20; while (!udp->queue[udp->current_seq] && --max_req > 0) INCSEQ(udp->current_seq); printf_control(this, "UDP RESEND %d-%d %" PRIu64 "\r\n", udp->next_seq, PREVSEQ(udp->current_seq), udp->queue_input_pos); udp->resend_requested = (udp->current_seq + (UDP_SEQ_MASK+1) - udp->next_seq) & UDP_SEQ_MASK; LOGUDP("%d-%d missing, requested re-send for %d frames", udp->next_seq, PREVSEQ(udp->current_seq), udp->resend_requested); } } /* * vdr_plugin_read_block_udp() * * - Get next UDP transport block from (socket)/queue. * * Returns NULL if read failed or data is not available. * (sets errno to EAGAIN, EINTR or ENOTCONN) * */ static buf_element_t *vdr_plugin_read_block_udp(vdr_input_plugin_t *this) { udp_data_t *udp = this->udp_data; while (this->control_running && this->fd_data >= 0) { if (this->discard_index < this->discard_index_ds && wait_stream_sync(this)) return NULL; if (!this->control_running || this->fd_data < 0) { errno = ENOTCONN; return NULL; } buf_element_t *read_buffer; /* return next packet from reordering queue (if any) */ if (NULL != (read_buffer = udp_process_queue(this))) return read_buffer; if (_x_action_pending(this->stream)) { errno = EINTR; return NULL; } /* poll + read socket */ if ( ! (read_buffer = read_socket_udp(this))) return NULL; if (! (read_buffer = udp_parse_header(read_buffer, this->rtp)) || ! (read_buffer = udp_parse_control(this, read_buffer)) || ! (read_buffer = udp_check_packet(read_buffer))) { errno = EAGAIN; return NULL; } /* * handle re-ordering and retransmissios */ stream_udp_header_t *pkt = (stream_udp_header_t*)read_buffer->content; uint8_t *pkt_data = UDP_PAYLOAD(read_buffer->content); udp->current_seq = pkt->seq & UDP_SEQ_MASK; udp->is_padding = DATA_IS_PES(pkt_data) && IS_PADDING_PACKET(pkt_data); /* first received frame initializes sequence counter */ if (udp->received_frames == -1) { udp->next_seq = udp->current_seq; udp->received_frames = 0; } /* check if received sequence number is inside allowed window (half of whole range) */ if (ADDSEQ(udp->current_seq, -udp->next_seq) > ((UDP_SEQ_MASK+1) >> 1)/*0x80*/) { struct sockaddr_in sin; LOGUDP("Received SeqNo out of window (%d ; [%d..%d])", udp->current_seq, udp->next_seq, (udp->next_seq+((UDP_SEQ_MASK+1) >> 1)/*0x80*/) & UDP_SEQ_MASK); /* reset link */ LOGDBG("UDP: resetting link"); memcpy(&sin, &udp->server_address, sizeof(sin)); free_udp_data(udp); udp = this->udp_data = init_udp_data(); memcpy(&udp->server_address, &sin, sizeof(sin)); read_buffer->free_buffer(read_buffer); errno = EAGAIN; return NULL; } /* Add received frame to incoming queue */ if (udp->queue[udp->current_seq]) { /* Duplicate packet or lot of dropped packets */ LOGUDP("Got duplicate or window exceeded ? (queue slot %d in use) !", udp->current_seq); udp->queue[udp->current_seq]->free_buffer(udp->queue[udp->current_seq]); udp->queue[udp->current_seq] = NULL; if (!udp->queued) LOGERR("UDP queue corrupt !!!"); else udp->queued--; } udp->queue[udp->current_seq] = read_buffer; read_buffer = NULL; udp->queued ++; /* return next packet from queue (if any) */ if (NULL != (read_buffer = udp_process_queue(this))) return read_buffer; udp_process_resend(this); #ifdef LOG_UDP /* Link quality statistics */ udp->received_frames++; if (udp->received_frames >= 1000) { if (udp->missed_frames) LOGUDP("packet loss %d.%d%% (%4d/%4d)", udp->missed_frames*100/udp->received_frames, (udp->missed_frames*1000/udp->received_frames)%10, udp->missed_frames, udp->received_frames); udp->missed_frames = udp->received_frames = 0; } #endif } errno = ENOTCONN; return NULL; } #ifdef TEST_PIP static int write_slave_stream(vdr_input_plugin_t *this, int stream, const char *data, int len) { fifo_input_plugin_t *slave; buf_element_t *buf; TRACE("write_slave_stream (%d bytes)", len); if(!this->pip_stream) { LOGMSG("Detected new video stream 0x%X", (unsigned int)data[3]); LOGMSG(" no xine stream yet, trying to create ..."); vdr_plugin_parse_control((input_plugin_t*)this, "SUBSTREAM 0xE1 50 50 288 196"); } if(!this->pip_stream) { LOGMSG(" pip substream: no stream !"); return -1; } /*LOGMSG(" pip substream open, queuing data");*/ slave = (fifo_input_plugin_t*)this->pip_stream->input_plugin; if(!slave) { LOGMSG(" pip substream: no input plugin !"); return len; } if(slave->buffer_pool->num_free(slave->buffer_pool) < 20) { /*LOGMSG(" pip substream: fifo almost full !");*/ xine_usec_sleep(3000); return 0; } buf = slave->buffer_pool->buffer_pool_try_alloc(slave->buffer_pool); if(!buf) { LOGMSG(" pip substream: fifo full !"); return 0; } if(len > buf->max_size) { LOGMSG(" pip substream: buf too small"); buf->free_buffer(buf); return len; } buf->content = buf->mem; buf->size = len; buf->type = BUF_DEMUX_BLOCK; xine_fast_memcpy(buf->content, data, len); slave->buffer->put(slave->buffer, buf); return len; } #endif static int vdr_plugin_write(vdr_input_plugin_if_t *this_if, int stream, uint64_t pos, const char *data, int len) { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_if; buf_element_t *buf = NULL; if (this->slave.stream) return len; #ifdef TEST_PIP if (stream) return write_slave_stream(this, stream, data, len); #else if (stream) return len; #endif TRACE("vdr_plugin_write (%d bytes)", len); VDR_ENTRY_LOCK(0); buf = get_buf_element(this, len, 0); if(!buf) { /* need counter to filter non-fatal overflows (VDR is not polling for every PES packet) */ if (this->write_overflows++ > 1) LOGMSG("vdr_plugin_write: buffer overflow ! (%d bytes)", len); VDR_ENTRY_UNLOCK(); xine_usec_sleep(5*1000); errno = EAGAIN; return 0; /* EAGAIN */ } this->write_overflows = 0; if(len > buf->max_size) { LOGMSG("vdr_plugin_write: PES too long (%d bytes, max size " "%d bytes), data ignored !", len, buf->max_size); buf->free_buffer(buf); /* curr_pos will be invalid when this point is reached ! */ VDR_ENTRY_UNLOCK(); return len; } stream_local_header_t *hdr = (stream_local_header_t*)buf->content; hdr->pos = pos; buf->type = BUF_LOCAL_BLOCK; buf->size = len + sizeof(stream_local_header_t); xine_fast_memcpy(buf->content + sizeof(stream_local_header_t), data, len); this->block_buffer->put(this->block_buffer, buf); VDR_ENTRY_UNLOCK(); TRACE("vdr_plugin_write returns %d", len); return len; } /* * post_vdr_event() * * - Called by frontend * - forward (input) events to VDR * * It is safe to cancel thread while this function is being executed. */ static int post_vdr_event(vdr_input_plugin_if_t *this_if, const char *msg) { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_if; if (msg && this->fd_control >= 0) return write_control (this, msg); LOGMSG("post_vdr_event: error ! \"%s\" not delivered.", msg ?: ""); return -1; } /******************************* Plugin **********************************/ #if XINE_VERSION_CODE < 10190 static off_t vdr_plugin_read (input_plugin_t *this_gen, char *buf_gen, off_t len) #else static off_t vdr_plugin_read (input_plugin_t *this_gen, void *buf_gen, off_t len) #endif { memset(buf_gen, 0, len); return 0; } /* * update_frames() * * Update frame type counters. * Collected information is used to start replay when enough data has been buffered */ static uint8_t update_frames(vdr_input_plugin_t *this, const uint8_t *data, int len) { uint8_t type = pes_get_picture_type(data, len); if (!this->I_frames) this->P_frames = this->B_frames = 0; switch (type) { case I_FRAME: this->I_frames++; LOGSCR_VERBOSE("I"); break; case P_FRAME: this->P_frames++; LOGSCR_VERBOSE("P"); break; case B_FRAME: this->B_frames++; LOGSCR_VERBOSE("B"); break; default: break; } return type; } /* * Preprocess buffer before passing it to demux * - handle discard * - handle display blanking * - handle stream start * - strip network headers */ static buf_element_t *preprocess_buf(vdr_input_plugin_t *this, buf_element_t *buf) { /* internal control bufs */ if(buf->type == CONTROL_BUF_BLANK) { pthread_mutex_lock(&this->lock); if(!this->stream_start) { LOGMSG("BLANK in middle of stream! bufs queue %d , video_fifo %d", this->block_buffer->fifo_size, this->stream->video_fifo->fifo_size); } else { vdr_x_demux_control_newpts(this->stream, 0, BUF_FLAG_SEEK); queue_blank_yv12(this); } pthread_mutex_unlock(&this->lock); buf->free_buffer(buf); return NULL; } /* demuxed video, control messages, ... go directly to demuxer */ if (buf->type != BUF_NETWORK_BLOCK && buf->type != BUF_LOCAL_BLOCK && buf->type != BUF_DEMUX_BLOCK) return buf; pthread_mutex_lock(&this->lock); /* Update stream position and remove network headers */ strip_network_headers(this, buf); /* Handle discard */ if (this->discard_index > this->curpos && this->guard_index < this->curpos) { pthread_mutex_unlock(&this->lock); buf->free_buffer(buf); return NULL; } /* Update stream position */ this->curpos += buf->size; this->curframe ++; /* ignore UDP/RTP "idle" padding */ if (!DATA_IS_TS(buf->content) && IS_PADDING_PACKET(buf->content)) { pthread_mutex_unlock(&this->lock); return buf; } /* First packet ? */ if (this->stream_start) { this->stream_start = 0; pthread_mutex_lock (&this->stream->first_frame_lock); this->stream->first_frame_flag = 2; pthread_mutex_unlock (&this->stream->first_frame_lock); memset(&this->scr_buf, 0, sizeof(this->scr_buf)); this->scr->got_pcr(this->scr, -1); } /* live mode clock sync input */ if (this->live_mode || (this->fd_control >= 0 && !this->fixed_scr)) { int64_t pcr = -1; if (DATA_IS_TS(buf->content) && ts_get_pcr_n(buf->content, buf->size / TS_SIZE, &pcr) && pcr >= 0) { this->scr->got_pcr(this->scr, pcr); } /* PES stream has no PCR, use audio pts for vdr-1.6.0 compability */ if (IS_AUDIO_PACKET(buf->content)) { pcr = pes_get_pts(buf->content, buf->size); if (pcr > 0) { this->scr->got_pcr(this->scr, pcr); } } } pthread_mutex_unlock(&this->lock); return buf; } /* * Postprocess buffer before passing it to demuxer * - Signal new pts upstream after stream changes * - Special handling for still images * - Count video frames for SCR tuning * - Special handling for ffmpeg mpeg2 video decoder */ static void postprocess_buf(vdr_input_plugin_t *this, buf_element_t *buf, int need_pause) { if (need_pause) { pthread_mutex_lock(&this->lock); scr_tuning_set_paused(this); pthread_mutex_unlock(&this->lock); } if (buf->type != BUF_DEMUX_BLOCK || DATA_IS_TS(buf->content)) return; #if 0 /* this is done in demux */ /* generated still images start with empty video PES, PTS = 0. Reset metronom pts so images will be displayed */ if(this->still_mode && buf->size == 14) { int64_t pts = pes_get_pts(buf->content, buf->size); if(pts==0) { vdr_x_demux_control_newpts(this->stream, pts, BUF_FLAG_SEEK); /* delay frame 10ms (9000 ticks) */ /*buf->content[12] = (uint8_t)((10*90) >> 7);*/ } } #endif /* Count video frames for SCR tuning algorithm */ if(this->live_mode && this->I_frames < 4) if(IS_VIDEO_PACKET(buf->content) && buf->size > 32) update_frames(this, buf->content, buf->size); } static void handle_disconnect(vdr_input_plugin_t *this) { LOGMSG("read_block: no data source, returning NULL"); flush_all_fifos (this, 0); pthread_mutex_lock(&this->lock); reset_trick_speed(this); this->live_mode = 0; reset_scr_tuning(this); this->stream->emergency_brake = 1; this->control_running = 0; errno = ENOTCONN; pthread_mutex_unlock(&this->lock); } static int adjust_scr_speed(vdr_input_plugin_t *this) { int need_pause = 0; if(pthread_mutex_lock(&this->lock)) { LOGERR("adjust_scr_speed: pthread_mutex_lock failed"); return 0; } if( (!this->live_mode && (this->fd_control < 0 || this->fixed_scr)) || this->slave.stream) { if(this->scr_tuning) reset_scr_tuning(this); } else { if(this->stream_start) { reset_scr_tuning(this); need_pause = 1; } else { vdr_adjust_realtime_speed(this); } } pthread_mutex_unlock(&this->lock); return need_pause; } static buf_element_t *vdr_plugin_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo) { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_gen; buf_element_t *buf = NULL; int need_pause = 0; TRACE("vdr_plugin_read_block"); if (this->slave.stream || !this->config_ok) { if (!this->config_ok) { LOGDBG("read_block waiting for configuration data"); xine_usec_sleep(100*1000); } xine_usec_sleep(50*1000); errno = EAGAIN; return NULL; } do { /* check for disconnection/termination */ if (!this->funcs.push_input_write /* reading from socket */ && !this->control_running) { /* disconnected ? */ handle_disconnect(this); return NULL; } /* Return immediately if demux_action_pending flag is set */ if (_x_action_pending(this->stream)) { errno = EINTR; return NULL; } /* adjust SCR speed */ need_pause = adjust_scr_speed(this); /* get next buffer */ if (this->funcs.push_input_write /* local mode */) { buf = fifo_buffer_timed_get(this->block_buffer, 100); } else { if ( !(buf= fifo_buffer_try_get(this->block_buffer))) { if (this->udp || this->rtp) buf = vdr_plugin_read_block_udp(this); else buf = vdr_plugin_read_block_tcp(this); if (!buf && errno != EAGAIN && errno != EINTR) { handle_disconnect(this); return NULL; } } } if (!buf) { if (!this->is_paused && !this->still_mode && !this->is_trickspeed && !this->slave.stream && this->stream->video_fifo->fifo_size <= 0) { this->read_timeouts++; if (this->read_timeouts > 80) { LOGMSG("No data in 8 seconds, queuing no signal image"); queue_nosignal(this); this->read_timeouts = 0; } } else { this->read_timeouts = 0; } errno = _x_action_pending(this->stream) ? EINTR : EAGAIN; return NULL; } this->read_timeouts = 0; buf = preprocess_buf(this, buf); } while (!buf); postprocess_buf(this, buf, need_pause); TRACE("vdr_plugin_read_block: return data, pos end = %" PRIu64, this->curpos); return buf; } static off_t vdr_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) { return -1; } static off_t vdr_plugin_get_length (input_plugin_t *this_gen) { return -1; } static uint32_t vdr_plugin_get_capabilities (input_plugin_t *this_gen) { return #ifdef INPUT_CAP_NOCACHE INPUT_CAP_NOCACHE | #endif INPUT_CAP_BLOCK; } static uint32_t vdr_plugin_get_blocksize (input_plugin_t *this_gen) { return 2048; } 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->discard_index > this->curpos ? this->discard_index : this->curpos; } static void vdr_plugin_dispose (input_plugin_t *this_gen) { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_gen; int local; int fd = -1, fc = -1; if(!this_gen) return; LOGDBG("vdr_plugin_dispose"); /* stop slave stream */ if (this->slave.stream) { close_slave_stream(this); } if(this->fd_control>=0) write_control(this, "CLOSE\r\n"); this->control_running = 0; local = !!this->funcs.push_input_write; memset(&this->funcs, 0, sizeof(this->funcs)); /* shutdown sockets */ if(!local) { struct linger { int l_onoff; /* linger active */ int l_linger; /* how many seconds to linger for */ } l = {0,0}; fd = this->fd_data; fc = this->fd_control; if(fc >= 0) { LOGDBG("Shutdown control"); setsockopt(fc, SOL_SOCKET, SO_LINGER, &l, sizeof(struct linger)); shutdown(fc, SHUT_RDWR); } if(fd >= 0 && this->tcp) { LOGDBG("Shutdown data"); setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(struct linger)); shutdown(fd, SHUT_RDWR); } } /* threads */ if(!local && this->threads_initialized) { void *p; LOGDBG("Cancel control thread ..."); /*pthread_cancel(this->control_thread);*/ pthread_join (this->control_thread, &p); LOGDBG("Threads joined"); } /* event queue(s) and listener threads */ LOGDBG("Disposing event queues"); if (this->event_queue) { xine_event_dispose_queue (this->event_queue); this->event_queue = NULL; } pthread_cond_broadcast(&this->engine_flushed); while(pthread_cond_destroy(&this->engine_flushed) == EBUSY) { LOGMSG("engine_flushed signal busy !"); pthread_cond_broadcast(&this->engine_flushed); xine_usec_sleep(10); } /* destroy mutexes (keep VDR out) */ LOGDBG("Destroying mutexes"); while(pthread_mutex_destroy(&this->vdr_entry_lock) == EBUSY) { LOGMSG("vdr_entry_lock busy ..."); pthread_mutex_lock(&this->vdr_entry_lock); pthread_mutex_unlock(&this->vdr_entry_lock); } while(pthread_mutex_destroy(&this->lock) == EBUSY) { LOGMSG("lock busy ..."); pthread_mutex_lock(&this->lock); pthread_mutex_unlock(&this->lock); } while(pthread_mutex_destroy(&this->fd_control_lock) == EBUSY) { LOGMSG("fd_control_lock busy ..."); pthread_mutex_lock(&this->fd_control_lock); pthread_mutex_unlock(&this->fd_control_lock); } signal_buffer_pool_not_empty(this); signal_buffer_not_empty(this); /* close sockets */ if(!local) { LOGDBG("Closing data connection"); if(fd >= 0) if(close(fd)) LOGERR("close(fd_data) failed"); LOGDBG("Closing control connection"); if(fc >= 0) if(close(fc)) LOGERR("close(fd_control) failed"); this->fd_data = this->fd_control = -1; LOGMSG("Connections closed."); } /* OSD */ if (this->osd_manager) { this->osd_manager->dispose(this->osd_manager, this->stream); this->osd_manager = NULL; } /* restore video properties */ if(this->video_properties_saved) set_video_properties(this, -1,-1,-1,-1,-1, -1, -1); /* restore defaults */ signal_buffer_pool_not_empty(this); signal_buffer_not_empty(this); /* SCR */ if (this->scr) this->scr->dispose(this->scr); /* metronom */ if (this->metronom) this->metronom->dispose(this->metronom); free (this->mrl); if(this->udp_data) free_udp_data(this->udp_data); /* fifos */ LOGDBG("Disposing fifos"); /* need to get all buffer elements back before disposing own buffers ... */ flush_all_fifos (this, 1); if (this->block_buffer) this->block_buffer->dispose(this->block_buffer); if (this->hd_buffer) this->hd_buffer->dispose(this->hd_buffer); free (this); LOGDBG("dispose done."); } #if XINE_VERSION_CODE > 10103 static const char* vdr_plugin_get_mrl (input_plugin_t *this_gen) #else static char* vdr_plugin_get_mrl (input_plugin_t *this_gen) #endif { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_gen; return this->mrl; } static int vdr_plugin_get_optional_data (input_plugin_t *this_gen, void *data, int data_type) { #ifdef INPUT_OPTIONAL_DATA_DEMUXER if (data_type == INPUT_OPTIONAL_DATA_DEMUXER) { static const char demux_name[] = "xvdr"; *((const char **)data) = demux_name; return INPUT_OPTIONAL_SUCCESS; } #endif return INPUT_OPTIONAL_UNSUPPORTED; } static int vdr_plugin_open(input_plugin_t *this_gen) { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_gen; xine_t *xine = this->class->xine; cfg_entry_t *e; this->event_queue = xine_event_new_queue (this->stream); xine_event_create_listener_thread (this->event_queue, vdr_event_cb, this); this->buffer_pool = this->stream->video_fifo; this->reserved_buffers = this->buffer_pool->buffer_pool_capacity - RADIO_MAX_BUFFERS; /* enable resample method */ xine->config->update_num(xine->config, "audio.synchronization.av_sync_method", 1); /* register our own scr provider */ this->scr = adjustable_scr_start(this->class->xine); if (!this->scr) LOGMSG("adjustable_scr_start() FAILED !"); this->scr_live_sync = 1; this->scr_tuning = SCR_TUNING_OFF; this->curpos = 0; /* replace stream metronom */ this->metronom = xvdr_metronom_init(this->stream); /* buffer */ this->block_buffer = fifo_buffer_new(this->stream, 4, 0x10000+64); /* dummy buf to be used before first read and for big PES frames */ /* OSD */ this->osd_manager = init_osd_manager(); /* sync */ pthread_mutex_init (&this->lock, NULL); pthread_mutex_init (&this->vdr_entry_lock, NULL); pthread_mutex_init (&this->fd_control_lock, NULL); pthread_cond_init (&this->engine_flushed, NULL); LOGDBG("xine_input_xvdr: revision %s", module_revision); if (this->class->num_buffers_hd != HD_BUF_NUM_BUFS) LOGMSG("Using non-default \"media." MRL_ID ".num_buffers_hd:%d\"", this->class->num_buffers_hd); /* check stream audio fifo size and issue a warning if too small */ e = this->class->xine->config->lookup_entry(this->class->xine->config, "engine.buffers.audio_num_buffers"); if (e && e->num_value < 500) { LOGMSG("WARNING: xine-engine setting \"engine.buffers.audio_num_buffers\":%d is " "too low for HD-playback! Please use values between 500-1000!", e->num_value); } return 1; } static int vdr_plugin_open_local (input_plugin_t *this_gen) { LOGDBG("vdr_plugin_open_local"); return vdr_plugin_open(this_gen); } static void set_recv_buffer_size(int fd, unsigned max_buf) { /* try to have larger receiving buffer */ /*while(max_buf) {*/ if(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &max_buf, sizeof(int)) < 0) { LOGERR("setsockopt(SO_RCVBUF,%d) failed", max_buf); /*max_buf >>= 1;*/ } else { unsigned int tmp = 0, len = sizeof(int);; if(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &tmp, &len) < 0) { LOGERR("getsockopt(SO_RCVBUF,%d) failed", max_buf); /*max_buf >>= 1;*/ } else if(tmp != 2*max_buf) { LOGDBG("setsockopt(SO_RCVBUF): got %d bytes", tmp); /*max_buf >>= 1;*/ } } /*}*/ max_buf = 256; /* not going to send anything, so shrink send buffer ... */ setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(int)); } static int alloc_udp_data_socket(int firstport, int trycount, int *port) { int fd, one = 1; struct sockaddr_in name; name.sin_family = AF_INET; name.sin_port = htons(firstport); name.sin_addr.s_addr = htonl(INADDR_ANY); fd = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/); if (fd < 0) { return -1; } set_recv_buffer_size(fd, KILOBYTE(512)); if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) LOGERR("UDP data stream: setsockopt(SO_REUSEADDR) failed"); while(bind(fd, (struct sockaddr *)&name, sizeof(name)) < 0) { if(!--trycount) { LOGMSG("UDP Data stream: bind error, no free port found"); close(fd); return -1; } LOGERR("UDP Data stream: bind error, port %d: %s", name.sin_port, strerror(errno)); name.sin_port = htons(++firstport); } *port = ntohs(name.sin_port); return fd; } static int connect_control_stream(vdr_input_plugin_t *this, const char *host, int port, int *client_id) { char tmpbuf[256]; int fd_control; int saved_fd = this->fd_control, one = 1; /* Connect to server */ this->fd_control = fd_control = _x_io_tcp_connect(this->stream, host, port); if(fd_control < 0 || XIO_READY != _x_io_tcp_connect_finish(this->stream, this->fd_control, 3000)) { LOGERR("Can't connect to tcp://%s:%d", host, port); close(fd_control); this->fd_control = saved_fd; return -1; } set_recv_buffer_size(fd_control, KILOBYTE(128)); /* request control connection */ if(_x_io_tcp_write(this->stream, fd_control, "CONTROL\r\n", 9) < 0) { LOGERR("Control stream write error"); return -1; } /* Check server greeting */ if(readline_control(this, tmpbuf, sizeof(tmpbuf)-1, 4) <= 0) { LOGMSG("Server not replying"); close(fd_control); this->fd_control = saved_fd; return -1; } LOGMSG("Server greeting: %s", tmpbuf); if(!strncmp(tmpbuf, "Access denied", 13)) { LOGMSG("Maybe host address is missing from server-side plugins/xineliboutput/allowed_hosts.conf ?"); close(fd_control); this->fd_control = saved_fd; return -1; } if(!strstr(tmpbuf, "VDR-") || !strstr(tmpbuf, "xineliboutput-") || !strstr(tmpbuf, "READY")) { LOGMSG("Unregonized greeting !"); close(fd_control); this->fd_control = saved_fd; return -1; } /* Check server xineliboutput version */ if(!strstr(tmpbuf, "xineliboutput-" XINELIBOUTPUT_VERSION " ")) { LOGMSG("-----------------------------------------------------------------"); LOGMSG("WARNING: Client and server versions of xinelibout are different !"); LOGMSG(" Client version (xine_input_vdr.so) is " XINELIBOUTPUT_VERSION); LOGMSG("-----------------------------------------------------------------"); } /* Store our client-id */ if(readline_control(this, tmpbuf, sizeof(tmpbuf)-1, 4) > 0 && !strncmp(tmpbuf, "CLIENT-ID ", 10)) { LOGDBG("Got Client-ID: %s", tmpbuf+10); if(client_id) if(1 != sscanf(tmpbuf+10, "%d", client_id)) *client_id = -1; } else { LOGMSG("Warning: No Client-ID !"); if(*client_id) *client_id = -1; } /* set socket to non-blocking mode */ fcntl (fd_control, F_SETFL, fcntl (fd_control, F_GETFL) | O_NONBLOCK); /* set control socket to deliver data immediately instead of waiting for full TCP segments */ setsockopt(fd_control, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(int)); this->fd_control = saved_fd; return fd_control; } static int connect_rtp_data_stream(vdr_input_plugin_t *this) { char cmd[256]; unsigned int ip0, ip1, ip2, ip3, port; int fd=-1, one = 1, retries = 0; struct sockaddr_in multicastAddress; struct ip_mreq mreq; struct sockaddr_in server_address, sin; socklen_t len = sizeof(sin); stream_rtp_header_impl_t tmp_rtp; /* get server IP address */ if(getpeername(this->fd_control, (struct sockaddr *)&server_address, &len)) { LOGERR("getpeername(fd_control) failed"); /* close(fd); */ return -1; } /* request RTP data transport from server */ LOGDBG("Requesting RTP transport"); if(_x_io_tcp_write(this->stream, this->fd_control, "RTP\r\n", 5) < 0) { LOGERR("Control stream write error"); return -1; } cmd[0] = 0; if(readline_control(this, cmd, sizeof(cmd)-1, 4) < 8 || strncmp(cmd, "RTP ", 4)) { LOGMSG("Server does not support RTP ? (%s)", cmd); return -1; } LOGDBG("Got: %s", cmd); if(5 != sscanf(cmd, "RTP %u.%u.%u.%u:%u", &ip0, &ip1, &ip2, &ip3, &port) || ip0>0xff || ip1>0xff || ip2>0xff || ip3>0xff || port>0xffff) { LOGMSG("Server does not support RTP ? (%s)", cmd); return -1; } LOGMSG("Connecting (data) to rtp://@%u.%u.%u.%u:%u ...", ip0, ip1, ip2, ip3, port); multicastAddress.sin_family = AF_INET; multicastAddress.sin_port = htons(port); multicastAddress.sin_addr.s_addr = htonl((ip0<<24)|(ip1<<16)|(ip2<<8)|ip3); if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { LOGERR("socket() failed"); return -1; } set_recv_buffer_size(fd, KILOBYTE(512)); if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) { LOGERR("setsockopt(SO_REUSEADDR) failed"); close(fd); return -1; } if(bind(fd, (struct sockaddr *)&multicastAddress, sizeof(multicastAddress)) < 0) { LOGERR("bind() to multicast address failed"); close(fd); return -1; } /* Join to multicast group */ memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = multicastAddress.sin_addr.s_addr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); /*mreq.imr_ifindex = 0;*/ if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) { LOGERR("setsockopt(IP_ADD_MEMBERSHIP) failed. No multicast in kernel?"); close(fd); return -1; } retry_select: /* wait until server sends first RTP packet */ if( XIO_READY != io_select_rd(fd)) { LOGDBG("Requesting RTP transport: RTP poll timeout"); if(++retries < 10) { LOGDBG("Requesting RTP transport"); if(_x_io_tcp_write(this->stream, this->fd_control, "RTP\r\n", 5) < 0) { LOGERR("Control stream write error"); close(fd); return -1; } goto retry_select; } LOGMSG("Data stream connection timed out (RTP)"); close(fd); return -1; } retry_recvfrom: /* check sender address */ if (recvfrom(fd, &tmp_rtp, sizeof(tmp_rtp), 0, &sin, &len) < 0) { LOGERR("RTP recvrom() failed"); return -1; } if(sin.sin_addr.s_addr != server_address.sin_addr.s_addr) { uint32_t tmp_ip = ntohl(sin.sin_addr.s_addr); LOGMSG("Received UDP/RTP multicast from unknown sender: %d.%d.%d.%d:%d", ((tmp_ip>>24)&0xff), ((tmp_ip>>16)&0xff), ((tmp_ip>>8)&0xff), ((tmp_ip)&0xff), sin.sin_port); if(XIO_READY == _x_io_select(this->stream, fd, XIO_READ_READY, 0)) goto retry_recvfrom; if(++retries < 4) goto retry_select; close(fd); return -1; } /* Succeed */ this->udp_data = init_udp_data(); /* store server address */ memcpy(&this->udp_data->server_address, &sin, sizeof(sin)); this->udp_data->ssrc = tmp_rtp.rtp_hdr.ssrc; return fd; } static int connect_udp_data_stream(vdr_input_plugin_t *this) { char cmd[256]; struct sockaddr_in server_address, sin; socklen_t len = sizeof(sin); uint32_t tmp_ip; stream_udp_header_t tmp_udp; int retries = 0, port = -1, fd = -1; /* get server IP address */ if(getpeername(this->fd_control, (struct sockaddr *)&server_address, &len)) { LOGERR("getpeername(fd_control) failed"); /* close(fd); */ return -1; } tmp_ip = ntohl(server_address.sin_addr.s_addr); LOGDBG("VDR server address: %d.%d.%d.%d", ((tmp_ip>>24)&0xff), ((tmp_ip>>16)&0xff), ((tmp_ip>>8)&0xff), ((tmp_ip)&0xff)); /* allocate UDP socket */ if((fd = alloc_udp_data_socket(DEFAULT_VDR_PORT, 20, &port)) < 0) return -1; /*LOGDBG("my UDP port is: %d", port);*/ retry_request: /* request UDP data transport from server */ LOGDBG("Requesting UDP transport"); sprintf(cmd, "UDP %d\r\n", port); if(_x_io_tcp_write(this->stream, this->fd_control, cmd, strlen(cmd)) < 0) { LOGERR("Control stream write error"); close(fd); return -1; } cmd[0] = 0; if(readline_control(this, cmd, sizeof(cmd)-1, 4) < 6 || strncmp(cmd, "UDP OK", 6)) { LOGMSG("Server does not support UDP ? (%s)", cmd); close(fd); return -1; } retry_select: /* wait until server sends first UDP packet */ if( XIO_READY != io_select_rd(fd)) { LOGDBG("Requesting UDP transport: UDP poll timeout"); if(++retries < 4) goto retry_request; LOGMSG("Data stream connection timed out (UDP)"); close(fd); return -1; } retry_recvfrom: /* check sender address */ if (recvfrom(fd, &tmp_udp, sizeof(tmp_udp), 0, &sin, &len) < 0) { LOGERR("UDP recvrom() failed"); return -1; } if(sin.sin_addr.s_addr != server_address.sin_addr.s_addr) { tmp_ip = ntohl(sin.sin_addr.s_addr); LOGMSG("Received UDP packet from unknown sender: %d.%d.%d.%d:%d", ((tmp_ip>>24)&0xff), ((tmp_ip>>16)&0xff), ((tmp_ip>>8)&0xff), ((tmp_ip)&0xff), sin.sin_port); if(XIO_READY == _x_io_select(this->stream, fd, XIO_READ_READY, 0)) goto retry_recvfrom; if(++retries < 4) goto retry_select; close(fd); return -1; } /* Succeed */ this->udp_data = init_udp_data(); /* store server address */ memcpy(&this->udp_data->server_address, &sin, sizeof(sin)); return fd; } static int connect_tcp_data_stream(vdr_input_plugin_t *this, const char *host, int port) { static const char ackmsg[] = {'D','A','T','A','\r','\n'}; struct sockaddr_in sinc; socklen_t len = sizeof(sinc); uint32_t ipc; char tmpbuf[256]; int fd_data, n; /* Connect to server */ fd_data = _x_io_tcp_connect(this->stream, host, port); if(fd_data < 0 || XIO_READY != _x_io_tcp_connect_finish(this->stream, fd_data, 3000)) { LOGERR("Can't connect to tcp://%s:%d", host, port); close(fd_data); return -1; } set_recv_buffer_size(fd_data, KILOBYTE(128)); /* request data connection */ getsockname(this->fd_control, (struct sockaddr *)&sinc, &len); ipc = ntohl(sinc.sin_addr.s_addr); sprintf(tmpbuf, "DATA %d 0x%x:%u %d.%d.%d.%d\r\n", this->client_id, (unsigned int)ipc, (unsigned int)ntohs(sinc.sin_port), ((ipc>>24)&0xff), ((ipc>>16)&0xff), ((ipc>>8)&0xff), ((ipc)&0xff) ); if(_x_io_tcp_write(this->stream, fd_data, tmpbuf, strlen(tmpbuf)) < 0) { LOGERR("Data stream write error (TCP)"); } else if( XIO_READY != io_select_rd(fd_data)) { LOGERR("Data stream poll failed (TCP)"); } else if((n=read(fd_data, tmpbuf, sizeof(ackmsg))) <= 0) { LOGERR("Data stream read failed (TCP)"); } else if(nmrl, "127.0.0.1")) { struct sockaddr_in sinc; struct sockaddr_in sins; socklen_t len = sizeof(sinc); getsockname(this->fd_control, &sinc, &len); getpeername(this->fd_control, &sins, &len); if(sinc.sin_addr.s_addr != sins.sin_addr.s_addr) { LOGMSG("connect_pipe_data_stream: client ip=0x%x != server ip=0x%x !", (unsigned int)sinc.sin_addr.s_addr, (unsigned int)sins.sin_addr.s_addr); #if 0 return -1; #endif } } _x_io_tcp_write(this->stream, this->fd_control, "PIPE\r\n", 6); if(readline_control(this, tmpbuf, sizeof(tmpbuf), 4) <= 0) { LOGMSG("Pipe request failed"); } else if(strncmp(tmpbuf, "PIPE /", 6)) { LOGMSG("Server does not support pipes ? (%s)", tmpbuf); } else { int fd_data; LOGMSG("Connecting (data) to pipe://%s", tmpbuf+5); if((fd_data = open(tmpbuf+5, O_RDONLY|O_NONBLOCK)) < 0) { if(errno == ENOENT) LOGMSG("Pipe not found"); else LOGERR("Pipe opening failed"); } else { _x_io_tcp_write(this->stream, this->fd_control, "PIPE OPEN\r\n", 11); if(readline_control(this, tmpbuf, sizeof(tmpbuf)-1, 4) >6 && !strncmp(tmpbuf, "PIPE OK", 7)) { fcntl (fd_data, F_SETFL, fcntl (fd_data, F_GETFL) | O_NONBLOCK); return fd_data; } LOGMSG("Data stream connection failed (PIPE)"); close(fd_data); } } return -1; } static int vdr_plugin_open_net (input_plugin_t *this_gen) { vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_gen; char tmpbuf[256]; int err; LOGDBG("vdr_plugin_open_net %s", this->mrl); if(strchr(this->mrl, '#')) *strchr(this->mrl, '#') = 0; if((!strncasecmp(this->mrl, MRL_ID "+tcp://", MRL_ID_LEN+7) && (this->tcp=1)) || (!strncasecmp(this->mrl, MRL_ID "+udp://", MRL_ID_LEN+7) && (this->udp=1)) || (!strncasecmp(this->mrl, MRL_ID "+rtp://", MRL_ID_LEN+7) && (this->rtp=1)) || (!strncasecmp(this->mrl, MRL_ID "+pipe://", MRL_ID_LEN+8)) || (!strncasecmp(this->mrl, MRL_ID "://", MRL_ID_LEN+3))) { char *phost = strdup(strstr(this->mrl, "//") + 2); char host[256]; char *port = strchr(phost, ':'); int iport; int one = 1; if(port) *port++ = 0; iport = port ? atoi(port) : DEFAULT_VDR_PORT; strn0cpy(host, phost, 254); /*host[sizeof(host)-1] = 0;*/ free(phost); /* TODO: use multiple input plugins - tcp/udp/file */ /* connect control stream */ LOGMSG("Connecting (control) to tcp://%s:%d ...", host, iport); this->fd_control = connect_control_stream(this, host, iport, &this->client_id); if (this->fd_control < 0) { LOGERR("Can't connect to tcp://%s:%d", host, iport); return 0; } setsockopt(this->fd_control, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(int)); LOGMSG("Connected (control) to tcp://%s:%d", host, iport); /* connect data stream */ /* try pipe ? */ if(!this->tcp && !this->udp && !this->rtp) { if((this->fd_data = connect_pipe_data_stream(this)) < 0) { LOGMSG("Data stream connection failed (PIPE)"); } else { this->tcp = this->udp = this->rtp = 0; LOGMSG("Data stream connected (PIPE)"); } } /* try RTP ? */ if(this->fd_data < 0 && this->rtp) { /* flush control buffer (if PIPE was tried first) */ while(0 < read(this->fd_control, tmpbuf, 255)) ; if((this->fd_data = connect_rtp_data_stream(this)) < 0) { LOGMSG("Data stream connection failed (RTP)"); this->rtp = 0; } else { this->rtp = 1; this->tcp = this->udp = 0; LOGMSG("Data stream connected (RTP)"); } } /* try UDP ? */ if(this->fd_data < 0 && !this->tcp) { LOGMSG("Connecting (data) to udp://%s ...", host); /* flush control buffer (if RTP was tried first) */ while(0 < read(this->fd_control, tmpbuf, 255)) ; if((this->fd_data = connect_udp_data_stream(this)) < 0) { LOGMSG("Data stream connection failed (UDP)"); this->udp = 0; } else { this->udp = 1; this->tcp = this->rtp = 0; LOGMSG("Data stream connected (UDP)"); } } /* fall back to TCP ? */ if(this->fd_data < 0) { LOGMSG("Connecting (data) to tcp://%s:%d ...", host, iport); this->tcp = 0; if((this->fd_data = connect_tcp_data_stream(this, host, iport)) < 0) { LOGMSG("Data stream connection failed (TCP)"); this->tcp = 0; } else { this->tcp = 1; } if(this->tcp) { /* succeed */ this->rtp = this->udp = 0; LOGMSG("Data stream connected (TCP)"); } else { /* failed */ if (this->fd_data >= 0) close(this->fd_data); if (this->fd_control >= 0) close(this->fd_control); this->fd_control = this->fd_data = -1; return 0; } } } else { LOGMSG("Unknown mrl (%s)", this->mrl); return 0; } if(!vdr_plugin_open(this_gen)) return 0; queue_nosignal(this); this->control_running = 1; if ((err = pthread_create (&this->control_thread, NULL, vdr_control_thread, (void*)this)) != 0) { LOGERR("Can't create new thread"); return 0; } this->class->xine->port_ticket->acquire(this->class->xine->port_ticket, 1); if(!(this->stream->video_out->get_capabilities(this->stream->video_out) & VO_CAP_UNSCALED_OVERLAY)) LOGMSG("WARNING: Video output driver reports it does not support unscaled overlays !"); this->class->xine->port_ticket->release(this->class->xine->port_ticket, 1); this->threads_initialized = 1; return 1; } /**************************** Plugin class *******************************/ /* Callback on default mrl change */ static void vdr_class_default_mrl_change_cb(void *data, xine_cfg_entry_t *cfg) { vdr_input_class_t *class = (vdr_input_class_t *) data; class->mrls[0] = cfg->str_value; } /* callback on scr tuning step change */ static void vdr_class_scr_tuning_step_cb(void *data, xine_cfg_entry_t *cfg) { vdr_input_class_t *class = (vdr_input_class_t *) data; class->scr_tuning_step = cfg->num_value / 1000000.0; } /* callback on scr tuning mode change */ static void vdr_class_smooth_scr_tuning_cb(void *data, xine_cfg_entry_t *cfg) { vdr_input_class_t *class = (vdr_input_class_t *) data; class->smooth_scr_tuning = cfg->num_value; } /* callback on OSD scaling mode change */ static void vdr_class_fast_osd_scaling_cb(void *data, xine_cfg_entry_t *cfg) { vdr_input_class_t *class = (vdr_input_class_t *) data; class->fast_osd_scaling = cfg->num_value; } static input_plugin_t *vdr_class_get_instance (input_class_t *class_gen, xine_stream_t *stream, const char *data) { vdr_input_class_t *class = (vdr_input_class_t *) class_gen; vdr_input_plugin_t *this; char *mrl = (char *) data; int local_mode; LOGDBG("vdr_class_get_instance"); if (strncasecmp (mrl, MRL_ID ":", MRL_ID_LEN+1) && strncasecmp (mrl, MRL_ID "+", MRL_ID_LEN+1)) return NULL; if(!strncasecmp(mrl, MRL_ID "+slave://0x", MRL_ID_LEN+11)) { LOGMSG("vdr_class_get_instance: slave stream requested"); return fifo_class_get_instance(class_gen, stream, data); } this = calloc(1, sizeof(vdr_input_plugin_t)); this->stream = stream; this->mrl = strdup(mrl); this->class = class; this->fd_data = -1; this->fd_control = -1; this->stream_start = 1; this->autoplay_size = -1; local_mode = ( (!strncasecmp(mrl, MRL_ID "://", MRL_ID_LEN+3)) && (strlen(mrl)==7)) || (!strncasecmp(mrl, MRL_ID ":///", MRL_ID_LEN+4)); if(!bSymbolsFound) { /* not running under VDR or vdr-sxfe/vdr-fbfe */ if(local_mode) { LOGDBG("vdr or vdr-??fe not detected, forcing remote mode"); local_mode = 0; } if(!strcasecmp(mrl, MRL_ID ":") || !strcasecmp(mrl, MRL_ID ":/") || !strcasecmp(mrl, MRL_ID "://") || !strcasecmp(mrl, MRL_ID ":///")) { /* default to local host */ free(this->mrl); this->mrl = strdup(MRL_ID "://127.0.0.1"); LOGMSG("Changed mrl from %s to %s", mrl, this->mrl); } } this->input_plugin.open = local_mode ? vdr_plugin_open_local : vdr_plugin_open_net; this->input_plugin.get_mrl = vdr_plugin_get_mrl; this->input_plugin.dispose = vdr_plugin_dispose; this->input_plugin.input_class = class_gen; 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_optional_data = vdr_plugin_get_optional_data; if(local_mode) { this->funcs.push_input_write = vdr_plugin_write; this->funcs.push_input_control= vdr_plugin_parse_control; this->funcs.push_input_osd = vdr_plugin_exec_osd_command; /*this->funcs.xine_input_event= NULL; -- frontend sets this */ } else { this->funcs.post_vdr_event = post_vdr_event; } LOGDBG("vdr_class_get_instance done."); return &this->input_plugin; } /* * vdr input plugin class stuff */ #if INPUT_PLUGIN_IFACE_VERSION < 18 #if XINE_VERSION_CODE > 10103 static const char *vdr_class_get_description (input_class_t *this_gen) #else static char *vdr_class_get_description (input_class_t *this_gen) #endif { return _("VDR (Video Disk Recorder) input plugin"); } static const char *vdr_class_get_identifier (input_class_t *this_gen) { return MRL_ID; } #endif #if XINE_VERSION_CODE >= 10200 static const char * const *vdr_plugin_get_autoplay_list(input_class_t *this_gen, int *num_files) { vdr_input_class_t *this = (vdr_input_class_t *)this_gen; *num_files = 1; return (const char * const *)this->mrls; } #else static char **vdr_plugin_get_autoplay_list(input_class_t *this_gen, int *num_files) { vdr_input_class_t *this = (vdr_input_class_t *)this_gen; *num_files = 1; return this->mrls; } #endif static void vdr_class_dispose (input_class_t *this_gen) { vdr_input_class_t *this = (vdr_input_class_t *) this_gen; config_values_t *config = this->xine->config; config->unregister_callback(config, "media." MRL_ID ".default_mrl"); config->unregister_callback(config, "media." MRL_ID ".osd.fast_scaling"); config->unregister_callback(config, "media." MRL_ID ".scr_tuning_step"); config->unregister_callback(config, "media." MRL_ID ".smooth_scr_tuning"); free (this); } static void *input_xvdr_init_class (xine_t *xine, void *data) { vdr_input_class_t *this; config_values_t *config = xine->config; SetupLogLevel(); if(!bSymbolsFound) { if(xine->verbosity > 0) { iSysLogLevel = xine->verbosity + 1; LOGMSG("detected verbose logging xine->verbosity=%d, setting log level to %d:%s", xine->verbosity, iSysLogLevel, (iSysLogLevel < 1) ? "NONE" : (iSysLogLevel < 2) ? "ERRORS" : (iSysLogLevel < 3) ? "INFO" : (iSysLogLevel < 4) ? "DEBUG" : "VERBOSE DEBUG"); } } this = calloc(1, sizeof (vdr_input_class_t)); this->xine = xine; this->mrls[ 0 ] = config->register_string(config, "media." MRL_ID ".default_mrl", MRL_ID "://127.0.0.1#nocache;demux:mpeg_block", _("default VDR host"), _("The default VDR host"), 10, vdr_class_default_mrl_change_cb, (void *)this); this->mrls[ 1 ] = 0; this->fast_osd_scaling = config->register_bool(config, "media." MRL_ID ".fast_osd_scaling", 0, _("Fast (low-quality) OSD scaling"), _("Enable fast (lower quality) OSD scaling.\n" "Default is to use (slow) linear interpolation " "to calculate pixels and full palette re-allocation " "to optimize color palette.\n" "Fast method only duplicates/removes rows and columns " "and does not modify palette."), 10, vdr_class_fast_osd_scaling_cb, (void *)this); this->scr_tuning_step = config->register_num(config, "media." MRL_ID ".scr_tuning_step", 5000, _("SRC tuning step"), _("SCR tuning step width unit %1000000."), 10, vdr_class_scr_tuning_step_cb, (void *)this) / 1000000.0; this->smooth_scr_tuning = config->register_bool(config, "media." MRL_ID ".smooth_scr_tuning", 0, _("Smoother SRC tuning"), _("Smoother SCR tuning"), 10, vdr_class_smooth_scr_tuning_cb, (void *)this); this->num_buffers_hd = config->register_num(config, "media." MRL_ID ".num_buffers_hd", HD_BUF_NUM_BUFS, _("number of buffers for HD content"), _("number of buffers for HD content"), 10, NULL, NULL); this->scr_treshold_sd = config->register_num(config, "media." MRL_ID ".scr_treshold_sd", 50, _("SCR-Treshold for SD-Playback (%)"), _("SCR-Treshold for starting SD-Playback (%)"), 10, NULL, NULL); this->scr_treshold_hd = config->register_num(config, "media." MRL_ID ".scr_treshold_hd", 40, _("SCR-Treshold for HD-Playback (%)"), _("SCR-Treshold for starting HD-Playback (%)"), 10, NULL, NULL); this->input_class.get_instance = vdr_class_get_instance; #if INPUT_PLUGIN_IFACE_VERSION < 18 this->input_class.get_identifier = vdr_class_get_identifier; this->input_class.get_description = vdr_class_get_description; #else this->input_class.identifier = MRL_ID; this->input_class.description = N_("VDR (Video Disk Recorder) input plugin"); #endif this->input_class.get_autoplay_list = vdr_plugin_get_autoplay_list; this->input_class.dispose = vdr_class_dispose; LOGDBG("init class succeeded"); return this; } /* * demuxer (xine/demux_xvdr.c) */ void *demux_xvdr_init_class (xine_t *xine, void *data); static const demuxer_info_t demux_info_xvdr = { 100 /* priority */ }; /* * exported plugin catalog entry */ const plugin_info_t xine_plugin_info[] __attribute__((visibility("default"))) = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_INPUT, INPUT_PLUGIN_IFACE_VERSION, MRL_ID, XINE_VERSION_CODE, NULL, input_xvdr_init_class }, { PLUGIN_DEMUX, DEMUXER_PLUGIN_IFACE_VERSION, MRL_ID, XINE_VERSION_CODE, &demux_info_xvdr, demux_xvdr_init_class }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } }; const plugin_info_t *xine_plugin_info_xvdr = xine_plugin_info; xineliboutput-2.0.0/xine_frontend_main.c0000644000175000017500000005606713061253352016254 0ustar phph/* * Simple main() routine for stand-alone frontends. * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "features.h" #ifndef _GNU_SOURCE # define _GNU_SOURCE // asprintf #endif #include #include #include #include #include #include #include #include #include /* xine_get_version */ #define LOG_MODULENAME "[vdr-fe] " #include "logdefs.h" #include "xine_input_vdr_mrl.h" #include "xine_frontend.h" #include "tools/vdrdiscovery.h" #include "xine_frontend_cec.h" #include "xine_frontend_lirc.h" #include "xine_frontend_kbd.h" /* static data */ /* next symbol is dynamically linked from input plugin */ int SysLogLevel __attribute__((visibility("default"))) = SYSLOGLEVEL_INFO; /* errors and info, no debug */ volatile int last_signal = 0; int gui_hotkeys = 0; /* * SignalHandler() */ static void SignalHandler(int signum) { LOGMSG("caught signal %d", signum); switch (signum) { case SIGHUP: last_signal = signum; case SIGPIPE: break; default: if (last_signal) { LOGMSG("SignalHandler: exit(-1)"); exit(-1); } last_signal = signum; break; } signal(signum, SignalHandler); } /* * strcatrealloc() */ static char *strcatrealloc(char *dest, const char *src) { size_t l; if (!src || !*src) return dest; l = (dest ? strlen(dest) : 0) + strlen(src) + 1; if(dest) { dest = (char *)realloc(dest, l); strcat(dest, src); } else { dest = (char*)malloc(l); strcpy(dest, src); } return dest; } /* * static data */ static const char help_str[] = "When server address is not given, server is searched from local network.\n" "If server is not found, localhost (127.0.0.1) is used as default.\n\n" " -H, --help Show (this) help message\n" " -A, --audio=audiodriver[:device]\n" " Select audio driver and optional port\n" " -V, --video=videodriver[:device]\n" " Select video driver and optional port\n" #ifndef IS_FBFE " -d, --display=displayaddress X11 display address\n" " -W, --wid=id Use existing X11 window\n" " Special ID for root window: --wid=root\n" # ifdef HAVE_XRANDR " -m, --modeswitch Enable video mode switching\n" # endif #endif " -a, --aspect=[auto|4:3|16:9|16:10|default]\n" " Display aspect ratio\n" " Use script to control HW aspect ratio:\n" " --aspect=auto:path_to_script\n" #ifndef IS_FBFE " -f, --fullscreen Fullscreen mode\n" # if defined(HAVE_XRENDER) || defined(HAVE_OPENGL) " -D, --hud[=flag[,flag]] Head Up Display OSD\n" " Optional flags:\n" # endif # ifdef HAVE_XRENDER # ifdef HAVE_XCOMPOSITE " xrender Use XRender instead of compositing\n" " (no compositing manager required)\n" # endif # ifdef HAVE_XSHAPE " xshape Use XShape instead of compositing\n" " (no compositing manager required)\n" # endif # endif // HAVE_XRENDER # ifdef HAVE_OPENGL " opengl Use OpenGL instead of compositing\n" " (no compositing manager required)\n" # endif # ifdef HAVE_OPENGL " -O, --opengl Use OpenGL for video and Head Up Display OSD\n" # endif " -w, --width=x Video window width\n" " -h, --height=x Video window height\n" " -g, --geometry=WxH[+X+Y] Set output window geometry (X style)\n" #endif // !IS_FBFE " -B, --buffers=x Number of PES buffers\n" " -n, --noscaling Disable all video scaling\n" " -P, --post=name[:arg=val[,arg=val]]\n" " Load and use xine post plugin(s)\n" " Examples:\n" " --post=upmix\n" " --post=upmix;tvtime:enabled=1,cheap_mode=1\n" " -L, --lirc[=devicename] Use lirc input device\n" " Optional lirc socket name can be given\n" #ifdef HAVE_LIBCEC " -E, --nocec Disable HDMI-CEC input device\n" " -e, --cec[=port[,type]] Use HDMI-CEC input device\n" " port: HDMI port number\n" " type: 0 for TV, 5 for AVR\n" #endif " -C, --config=file Use config file (default: ~/.xine/config_xineliboutput).\n" " -v, --verbose Verbose debug output\n" " -s, --silent Silent mode (report only errors)\n" " -l, --syslog Write all output to system log\n" " -k, --nokbd Disable console keyboard input\n" #ifndef IS_FBFE " -x, --noxkbd Disable X11 keyboard input\n" #endif " -o, --hotkeys Enable frontend GUI hotkeys\n" " -U, --touch Enable touch screen remote controller\n" " -p, --shutdown=MIN[:CMD] Shutdown after MIN minutes of inactivity\n" " Use CMD to perform shutdown (default: /sbin/shutdown)\n" " -T, --terminal=dev Controlling TTY\n" " -b, --daemon Run as daemon (disable keyboard,\n" " log to syslog and fork to background)\n" " -S, --slave Enable slave mode (read commands from stdin)\n" " -R, --reconnect Automatically reconnect when connection has been lost\n" " -t, --tcp Use TCP transport\n" " -u, --udp Use UDP transport\n" " -r, --rtp Use RTP transport\n\n" " If no transport options are given, transports\n" " are tried in following order:\n" " local pipe, rtp, udp, tcp\n\n"; static const char short_options[] = "HA:V:d:W:a:fg:Dw:h:B:nP:L:C:T:p:vsxlkoOeEbSRtuUr"; static const struct option long_options[] = { { "help", no_argument, NULL, 'H' }, { "audio", required_argument, NULL, 'A' }, { "video", required_argument, NULL, 'V' }, { "aspect", required_argument, NULL, 'a' }, #ifndef IS_FBFE { "display", required_argument, NULL, 'd' }, { "wid", required_argument, NULL, 'W' }, # ifdef HAVE_XRANDR { "modeswitch", no_argument, NULL, 'm' }, # endif { "fullscreen", no_argument, NULL, 'f' }, { "geometry", required_argument, NULL, 'g' }, # ifdef HAVE_XRENDER { "hud", optional_argument, NULL, 'D' }, # ifdef HAVE_OPENGL { "opengl", no_argument, NULL, 'O' }, # endif # endif { "width", required_argument, NULL, 'w' }, { "height", required_argument, NULL, 'h' }, #endif { "buffers", required_argument, NULL, 'B' }, { "noscaling", no_argument, NULL, 'n' }, { "post", required_argument, NULL, 'P' }, { "lirc", optional_argument, NULL, 'L' }, #ifdef HAVE_LIBCEC { "nocec", optional_argument, NULL, 'E' }, { "cec", optional_argument, NULL, 'e' }, #endif { "config", required_argument, NULL, 'C' }, { "terminal", required_argument, NULL, 'T' }, { "shutdown", required_argument, NULL, 'p' }, { "verbose", no_argument, NULL, 'v' }, { "silent", no_argument, NULL, 's' }, { "syslog", no_argument, NULL, 'l' }, { "nokbd", no_argument, NULL, 'k' }, { "noxkbd", no_argument, NULL, 'x' }, { "hotkeys", no_argument, NULL, 'o' }, { "touch", no_argument, NULL, 'U' }, { "daemon", no_argument, NULL, 'b' }, { "slave", no_argument, NULL, 'S' }, { "reconnect", no_argument, NULL, 'R' }, { "tcp", no_argument, NULL, 't' }, { "udp", no_argument, NULL, 'u' }, { "rtp", no_argument, NULL, 'r' }, { NULL, no_argument, NULL, 0 } }; #define PRINTF(x...) do { if(SysLogLevel>1) printf(x); } while(0) #define EXIT(x...) do { fprintf(stderr, x); exit(-1); } while(0) int main(int argc, char *argv[]) { int ftcp = 0, fudp = 0, frtp = 0, reconnect = 0, firsttry = 1; int fullscreen = 0, hud = 0, opengl = 0, xpos = 0, ypos = 0, width = 720, height = 576; int pes_buffers = 250; int scale_video = 1, aspect = 1, modeswitch = 0; int daemon_mode = 0, nokbd = 0, noxkbd = 0, slave_mode = 0; int repeat_emu = 0; int touchscreen = 0; int window_id = WINDOW_ID_NONE; int xmajor, xminor, xsub; int c; int xine_finished = FE_XINE_ERROR; int inactivity_timer = 0; char *mrl = NULL; char *video_driver = NULL; char *audio_driver = NULL; char *static_post_plugins = NULL; char *lirc_dev = NULL; int cec_hdmi_port = 0, cec_dev_type = 0; char *p; const char *audio_device = NULL; const char *video_port = NULL; const char *aspect_controller = NULL; const char *tty = NULL; const char *exec_name = argv[0]; const char *config_file = NULL; const char *power_off_cmd = NULL; extern const fe_creator_f fe_creator; frontend_t *fe = NULL; LogToSysLog = 0; if (strrchr(argv[0],'/')) exec_name = strrchr(argv[0],'/')+1; xine_get_version(&xmajor, &xminor, &xsub); printf("%s %s (build with xine-lib %d.%d.%d, using xine-lib %d.%d.%d)\n\n", exec_name, FE_VERSION_STR, XINE_MAJOR_VERSION, XINE_MINOR_VERSION, XINE_SUB_VERSION, xmajor, xminor, xsub); /* Parse arguments */ while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { switch (c) { default: case 'H': printf("\nUsage: %s [options] [" MRL_ID "[+udp|+tcp|+rtp]:[//host[:port]]] \n" "\nAvailable options:\n", exec_name); printf("%s", help_str); list_xine_plugins(NULL, SysLogLevel>2); exit(0); case 'A': audio_driver = strdup(optarg); if (NULL != (p = strchr(audio_driver, ':'))) { *p = 0; audio_device = p + 1; } PRINTF("Audio driver: %s\n", audio_driver); if (audio_device) PRINTF("Audio device: %s\n", audio_device); break; case 'V': video_driver = strdup(optarg); if (NULL != (p = strchr(video_driver, ':'))) { *p = 0; video_port = p + 1; } PRINTF("Video driver: %s\n", video_driver); if (video_port) PRINTF("Video port: %s\n", video_port); break; #ifndef IS_FBFE case 'W': if (!strcmp(optarg, "root")) window_id = WINDOW_ID_ROOT; else window_id = atoi(optarg); break; case 'm': modeswitch = 1; #ifdef HAVE_XRANDR PRINTF("Video mode switching enabled\n"); #else PRINTF("Video mode switching not supported\n"); #endif break; case 'd': video_port = optarg; break; case 'x': noxkbd = 1; break; #endif case 'a': if (!strncmp(optarg, "auto", 4)) aspect = 0; if (!strncmp(optarg, "4:3", 3)) aspect = 2; if (!strncmp(optarg, "16:9", 4)) aspect = 3; if (!strncmp(optarg, "16:10", 5)) aspect = 4; if (aspect == 0 && optarg[4] == ':') aspect_controller = optarg + 5; PRINTF("Aspect ratio: %s\n", aspect==0?"Auto":aspect==2?"4:3":aspect==3?"16:9": aspect==4?"16:10":"Default"); if (aspect_controller) PRINTF("Using %s to switch aspect ratio\n", aspect_controller); break; #ifndef IS_FBFE case 'f': fullscreen=1; PRINTF("Fullscreen mode\n"); break; case 'D': # ifdef HAVE_XRENDER hud = HUD_COMPOSITE; PRINTF("HUD OSD mode\n"); # else EXIT("HUD OSD not supported\n"); # endif if (optarg && strstr(optarg, "opengl")) { # ifdef HAVE_OPENGL hud |= HUD_OPENGL; PRINTF("OpenGL HUD OSD mode\n"); break; # else EXIT("OpenGL HUD OSD not supported\n"); # endif } if (optarg && strstr(optarg, "xrender")) { # ifdef HAVE_XCOMPOSITE hud |= HUD_XRENDER; PRINTF("XRender HUD OSD mode\n"); break; # else EXIT("XRender HUD OSD not supported\n"); # endif } if (optarg && strstr(optarg, "xshape")) { # ifdef HAVE_XSHAPE hud |= HUD_XSHAPE; PRINTF("XShape HUD OSD mode\n"); # else EXIT("XShape HUD OSD not supported\n"); # endif } break; case 'O': # ifdef HAVE_OPENGL opengl = 1; PRINTF("Using OpenGL to draw video and OSD\n"); # else EXIT("OpenGL not supported\n"); # endif break; case 'w': width = atoi(optarg); PRINTF("Width: %d\n", width); break; case 'g': sscanf (optarg, "%dx%d+%d+%d", &width, &height, &xpos, &ypos); PRINTF("Geometry: %dx%d+%d+%d\n", width, height, xpos, ypos); break; case 'h': height = atoi(optarg); PRINTF("Height: %d\n", height); break; #endif // !IS_FBFE case 'B': pes_buffers = atoi(optarg); PRINTF("Buffers: %d\n", pes_buffers); break; case 'T': tty = optarg; if (access(tty, R_OK | W_OK) < 0) { EXIT("Can't access terminal: %s\n", tty); } PRINTF("Terminal: %s\n", tty); break; case 'n': scale_video = 0; PRINTF("Video scaling disabled\n"); break; case 'p': inactivity_timer = atoi(optarg); power_off_cmd = strchr(optarg, ':'); power_off_cmd = power_off_cmd ? power_off_cmd+1 : "/sbin/shutdown"; PRINTF("Shutdown after %d minutes of inactivity using %s\n", inactivity_timer, power_off_cmd); break; case 'P': if (static_post_plugins) strcatrealloc(static_post_plugins, ";"); static_post_plugins = strcatrealloc(static_post_plugins, optarg); PRINTF("Post plugins: %s\n", static_post_plugins); break; case 'C': config_file = optarg; PRINTF("Config file: %s\n", config_file); break; case 'L': lirc_dev = strdup(optarg ? : "/dev/lircd"); if (strstr(lirc_dev, ",repeatemu")) { *strstr(lirc_dev, ",repeatemu") = 0; repeat_emu = 1; } PRINTF("LIRC device: %s%s\n", lirc_dev, repeat_emu?", emulating key repeat":""); break; case 'E': cec_hdmi_port = -1; break; case 'e': cec_hdmi_port = 0; #ifdef HAVE_LIBCEC if (optarg) sscanf(optarg, "%d,%d", &cec_hdmi_port, &cec_dev_type); PRINTF("HDMI-CEC enabled. Connected to HDMI port %d (type %d)\n", cec_hdmi_port, cec_dev_type); #else EXIT("HDMI-CEC support not compiled in\n"); #endif break; case 'v': SysLogLevel = (SysLogLevel fullscreen toggle\n" " keyboard d,D -> deinterlace toggle\n" " keyboard p,P -> power off\n" " LIRC Deinterlace -> deinterlace toggle\n" " LIRC Fullscreen -> fullscreen toggle\n" " LIRC PowerOff -> power off\n" " LIRC Quit -> exit\n"); break; case 'U': touchscreen = 1; PRINTF("Touchscreen input enabled\n"); PRINTF("Display is divided to 4x3 buttons:\n"); PRINTF(" Menu Up Back Ok \n"); PRINTF(" Left Down Right \n"); PRINTF(" Red Green Yellow Blue\n"); break; case 'b': nokbd = daemon_mode = 1; PRINTF("Keyboard input disabled\n"); break; case 'R': reconnect = 1; PRINTF("Automatic reconnection enabled\n"); break; case 't': ftcp = 1; PRINTF("Protocol: TCP\n"); break; case 'u': fudp = 1; PRINTF("Protocol: UDP\n"); break; case 'r': frtp = 1; PRINTF("Protocol: RTP\n"); break; case 1: printf("arg 1 (%s)\n", long_options[optind].name); exit(0); } } if (optind < argc) { mrl = strdup(argv[optind]); PRINTF("VDR Server: %s\n", mrl); while (++optind < argc) printf("Unknown argument: %s\n", argv[optind]); } PRINTF("\n"); if (tty) { /* claim new controlling terminal */ stdin = freopen(tty, "r", stdin); stdout = freopen(tty, "w", stdout); stderr = freopen(tty, "w", stderr); } #if 1 /* backward compability */ if (mrl && ( !strncmp(mrl, MRL_ID ":tcp:", MRL_ID_LEN+5) || !strncmp(mrl, MRL_ID ":udp:", MRL_ID_LEN+5) || !strncmp(mrl, MRL_ID ":rtp:", MRL_ID_LEN+5) || !strncmp(mrl, MRL_ID ":pipe:", MRL_ID_LEN+6))) mrl[4] = '+'; #endif /* If server address not given, try to find server automatically */ if (!mrl || !strcmp(mrl, MRL_ID ":") || !strcmp(mrl, MRL_ID "+tcp:") || !strcmp(mrl, MRL_ID "+udp:") || !strcmp(mrl, MRL_ID "+rtp:") || !strcmp(mrl, MRL_ID "+pipe:")) { char address[1024] = ""; int port = -1; PRINTF("VDR server not given, searching ...\n"); if (udp_discovery_find_server(&port, &address[0])) { PRINTF("Found VDR server: host %s, port %d\n", address, port); if (mrl) { char *tmp = mrl; mrl = NULL; if (asprintf(&mrl, "%s//%s:%d", tmp, address, port) < 0) return -1; free(tmp); } else if (asprintf(&mrl, MRL_ID "://%s:%d", address, port) < 0) return -1; } else { PRINTF("---------------------------------------------------------------\n" "WARNING: MRL not given and server not found from local network.\n" " Trying to connect to default port on local host.\n" "---------------------------------------------------------------\n"); mrl = strdup(MRL_ID "://127.0.0.1"); } } if (mrl && strncmp(mrl, MRL_ID ":", MRL_ID_LEN+1) && strncmp(mrl, MRL_ID "+", MRL_ID_LEN+1)) { char *mrl2 = mrl; PRINTF("WARNING: MRL does not start with \'" MRL_ID ":\' (%s)\n", mrl); if (asprintf(&mrl, MRL_ID "://%s", mrl) < 0) return -1; free(mrl2); } { char *tmp = NULL, *mrl2 = mrl; if (frtp && !strstr(mrl, "rtp:")) tmp = strdup(MRL_ID "+rtp:"); else if (fudp && !strstr(mrl, "udp:")) tmp = strdup(MRL_ID "+udp:"); else if (ftcp && !strstr(mrl, "tcp:")) tmp = strdup(MRL_ID "+tcp:"); if (tmp) { mrl = strcatrealloc(tmp, strchr(mrl, '/')); free(mrl2); } } if (daemon_mode) { PRINTF("Entering daemon mode\n\n"); if (daemon(1, 0) == -1) { fprintf(stderr, "%s: %m\n", exec_name); LOGERR("daemon() failed"); return -2; } } /* Create front-end */ fe = (*fe_creator)(); if (!fe) { fprintf(stderr, "Error initializing frontend\n"); return -3; } /* Initialize display */ if (!fe->fe_display_open(fe, xpos, ypos, width, height, fullscreen, hud, opengl, modeswitch, "", aspect, noxkbd, gui_hotkeys, touchscreen, video_port, scale_video, aspect_controller, window_id)) { fprintf(stderr, "Error opening display\n"); fe->fe_free(fe); return -4; } /* Initialize xine */ if (!fe->xine_init(fe, audio_driver, audio_device, video_driver, pes_buffers, static_post_plugins, config_file)) { fprintf(stderr, "Error initializing xine\n"); list_xine_plugins(fe, SysLogLevel>2); fe->fe_free(fe); return -5; } if (power_off_cmd) { fe->shutdown_init(fe, power_off_cmd, inactivity_timer * 60); } if (SysLogLevel > 2) list_xine_plugins(fe, SysLogLevel>2); /* signal handlers */ if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN); if (signal(SIGINT, SignalHandler) == SIG_IGN) signal(SIGINT, SIG_IGN); if (signal(SIGTERM, SignalHandler) == SIG_IGN) signal(SIGTERM, SIG_IGN); if (signal(SIGPIPE, SignalHandler) == SIG_IGN) signal(SIGPIPE, SIG_IGN); do { if (!firsttry) { PRINTF("Connection to server lost. Reconnecting after two seconds...\n"); sleep(2); PRINTF("Reconnecting...\n"); } /* Connect to VDR xineliboutput server */ if (!fe->xine_open(fe, mrl)) { /*print_xine_log(((fe_t *)fe)->xine);*/ if (!firsttry) { PRINTF("Error opening %s\n", mrl); continue; } fprintf(stderr, "Error opening %s\n", mrl); fe->fe_free(fe); return -6; } if (!fe->xine_play(fe)) { if (!firsttry) { PRINTF("Error playing %s\n", argv[1]); continue; } fprintf(stderr, "Error playing %s\n", argv[1]); fe->fe_free(fe); return -7; } if (firsttry) { /* Start LIRC forwarding */ lirc_start(fe, lirc_dev, repeat_emu); cec_start(fe, cec_hdmi_port, cec_dev_type); /* Start keyboard listener thread */ if (!nokbd) { PRINTF("\n\nPress Esc to exit\n\n"); kbd_start(fe, slave_mode); } } /* Main loop */ fflush(stdout); fflush(stderr); while (!last_signal && fe->fe_run(fe)) ; xine_finished = fe->xine_is_finished(fe,0); fe->xine_close(fe); firsttry = 0; /* HUP reconnects */ if (last_signal == SIGHUP) last_signal = 0; } while (!last_signal && xine_finished != FE_XINE_EXIT && reconnect); /* Clean up */ PRINTF("Terminating...\n"); fe->send_event(fe, "QUIT"); /* stop input threads */ lirc_stop(); cec_stop(); if (!nokbd) kbd_stop(); fe->fe_free(fe); free(static_post_plugins); free(mrl); free(audio_driver); free(video_driver); free(lirc_dev); return xine_finished==FE_XINE_EXIT ? 0 : 1; } xineliboutput-2.0.0/xine_frontend_lirc.h0000644000175000017500000000056013061253352016251 0ustar phph/* * xine_frontend_lirc.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINE_FRONTEND_LIRC_H #define XINE_FRONTEND_LIRC_H struct frontend_s; void lirc_start(struct frontend_s *fe, const char *lirc_dev, int repeat_emu); void lirc_stop(void); #endif /* XINE_FRONTEND_LIRC_H */ xineliboutput-2.0.0/xine_frontend_lirc.c0000644000175000017500000001521113061253352016243 0ustar phph/* * xine_frontend_lirc.c: Forward (local) lirc keys to VDR (server) * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ /* * * Almost directly copied from vdr-1.4.3-2 (lirc.c : cLircRemote) * */ /* * lirc.c: LIRC remote control * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * * LIRC support added by Carsten Koch 2000-06-16. * */ #include #include #ifdef __FreeBSD__ #include #endif #include #include #include #include #include #include #include "tools/time_ms.h" #define LOG_MODULENAME "[lirc] " #include "logdefs.h" #include "xine_frontend.h" #include "xine_frontend_lirc.h" #define REPEATDELAY 350 /* ms */ #define REPEATFREQ 100 /* ms */ #define REPEATTIMEOUT 500 /* ms */ #define RECONNECTDELAY 3000 /* ms */ #define LIRC_KEY_BUF 30 #define LIRC_BUFFER_SIZE 128 #define MIN_LIRCD_CMD_LEN 5 /* static data */ static pthread_t lirc_thread; static volatile const char *lirc_device_name = NULL; static volatile int fd_lirc = -1; static int lirc_repeat_emu = 0; /* xine_frontend_main.c: */ extern int gui_hotkeys; static void lircd_connect(void) { struct sockaddr_un addr; if (fd_lirc >= 0) { close(fd_lirc); fd_lirc = -1; } if (!lirc_device_name) { LOGDBG("no lirc device given"); return; } addr.sun_family = AF_UNIX; strncpy(addr.sun_path, (char*)lirc_device_name, sizeof(addr.sun_path)); addr.sun_path[sizeof(addr.sun_path)-1] = 0; if ((fd_lirc = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { LOGERR("lirc error: socket() < 0"); return; } if (connect(fd_lirc, (struct sockaddr *)&addr, sizeof(addr))) { LOGERR("lirc error: connect(%s) < 0", lirc_device_name); close(fd_lirc); fd_lirc = -1; return; } } static void *lirc_receiver_thread(void *fe_gen) { frontend_t *fe = (frontend_t*)fe_gen; const int priority = -1; int timeout = -1; int repeat = 0; uint64_t FirstTime = time_ms(); uint64_t LastTime = time_ms(); char buf[LIRC_BUFFER_SIZE]; char LastKeyName[LIRC_KEY_BUF] = ""; LOGMSG("lirc forwarding started"); errno = 0; if ((nice(priority) == -1) && errno) LOGDBG("LIRC: Can't nice to value: %d", priority); lircd_connect(); while (lirc_device_name && fd_lirc >= 0) { fd_set set; int ready, ret = -1; FD_ZERO(&set); FD_SET(fd_lirc, &set); pthread_testcancel(); if (timeout >= 0) { struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; ready = select(FD_SETSIZE, &set, NULL, NULL, &tv) > 0 && FD_ISSET(fd_lirc, &set); } else { ready = select(FD_SETSIZE, &set, NULL, NULL, NULL) > 0 && FD_ISSET(fd_lirc, &set); } pthread_testcancel(); if (ready < 0) { LOGMSG("LIRC connection lost ?"); break; } if (ready) { do { errno = 0; ret = read(fd_lirc, buf, sizeof(buf)); pthread_testcancel(); } while (ret < 0 && errno == EINTR); if (ret <= 0 ) { /* try reconnecting */ LOGERR("LIRC connection lost"); lircd_connect(); while (lirc_device_name && fd_lirc < 0) { pthread_testcancel(); sleep(RECONNECTDELAY/1000); lircd_connect(); } if (fd_lirc >= 0) LOGMSG("LIRC reconnected"); continue; } if (ret >= MIN_LIRCD_CMD_LEN) { unsigned int count; char KeyName[LIRC_KEY_BUF]; LOGDBG("LIRC: %s", buf); if (sscanf(buf, "%*x %x %29s", &count, KeyName) != 2) { /* '29' in '%29s' is LIRC_KEY_BUF-1! */ LOGMSG("unparseable lirc command: %s", buf); continue; } if (lirc_repeat_emu) if (strcmp(KeyName, LastKeyName) == 0 && elapsed(LastTime) < REPEATDELAY) count = repeat + 1; if (count == 0) { if (strcmp(KeyName, LastKeyName) == 0 && elapsed(FirstTime) < REPEATDELAY) continue; /* skip keys coming in too fast */ if (repeat) { alarm(3); fe->send_input_event(fe, "LIRC", LastKeyName, 0, 1); alarm(0); } strcpy(LastKeyName, KeyName); repeat = 0; FirstTime = time_ms(); timeout = -1; } else { if (elapsed(LastTime) < REPEATFREQ) continue; /* repeat function kicks in after a short delay */ if (elapsed(FirstTime) < REPEATDELAY) { if (lirc_repeat_emu) LastTime = time_ms(); continue; /* skip keys coming in too fast */ } repeat = 1; timeout = REPEATDELAY; } LastTime = time_ms(); if (gui_hotkeys) { if (!strcmp(KeyName, "Quit")) { fe->send_event(fe, "QUIT"); break; } if (!strcmp(KeyName, "PowerOff")) { fe->send_event(fe, "POWER_OFF"); break; } if (!strcmp(KeyName, "Fullscreen")) { if (!repeat) fe->send_event(fe, "TOGGLE_FULLSCREEN"); continue; } if (!strcmp(KeyName, "Deinterlace")) { if (!repeat) fe->send_event(fe, "TOGGLE_DEINTERLACE"); continue; } } alarm(3); fe->send_input_event(fe, "LIRC", KeyName, repeat, 0); alarm(0); } } if (repeat && (!ready || ret < MIN_LIRCD_CMD_LEN)) { /* the last one was a repeat, so let's generate a release */ if (elapsed(LastTime) >= REPEATTIMEOUT) { alarm(3); fe->send_input_event(fe, "LIRC", LastKeyName, 0, 1); alarm(0); repeat = 0; *LastKeyName = 0; timeout = -1; } } } if (fd_lirc >= 0) close(fd_lirc); fd_lirc = -1; pthread_exit(NULL); return NULL; /* never reached */ } void lirc_start(struct frontend_s *fe, const char *lirc_dev, int repeat_emu) { if (lirc_dev) { int err; lirc_device_name = lirc_dev; lirc_repeat_emu = repeat_emu; if ((err = pthread_create (&lirc_thread, NULL, lirc_receiver_thread, (void*)fe)) != 0) { fprintf(stderr, "can't create new thread for lirc (%s)\n", strerror(err)); } } } void lirc_stop(void) { if (lirc_device_name) { void *p; /*free(lirc_device_name);*/ lirc_device_name = NULL; if (fd_lirc >= 0) close(fd_lirc); fd_lirc = -1; pthread_cancel (lirc_thread); pthread_join (lirc_thread, &p); } } xineliboutput-2.0.0/xine_frontend_kbd.h0000644000175000017500000000052413061253352016060 0ustar phph/* * xine_frontend_kbd.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINE_FRONTEND_KBD_H #define XINE_FRONTEND_KBD_H struct frontend_s; void kbd_start(struct frontend_s *fe, int slave_mode); void kbd_stop(void); #endif /* XINE_FRONTEND_KBD_H */ xineliboutput-2.0.0/xine_frontend_kbd.c0000644000175000017500000001651513061253352016062 0ustar phph/* * xine_frontend_kbd.c: Forward (local) console key presses to VDR (server) * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "features.h" #include #include #include #include #include #include #include #include #include #define LOG_MODULENAME "[console] " #include "logdefs.h" #include "xine_frontend.h" #include "xine_frontend_kbd.h" /* * stdin (keyboard/slave mode) reading */ /* static data */ static pthread_t kbd_thread; static struct termios tm, saved_tm; /* xine_frontend_main.c: */ extern int gui_hotkeys; /* * read_key() * * Try to read single char from stdin. * * Returns: >=0 char readed * -1 nothing to read * -2 fatal error */ #define READ_KEY_ERROR -2 #define READ_KEY_EAGAIN -1 static int read_key(void) { unsigned char ch; int err; struct pollfd pfd; pfd.fd = STDIN_FILENO; pfd.events = POLLIN; errno = 0; pthread_testcancel(); if (1 == (err = poll(&pfd, 1, 50))) { pthread_testcancel(); if (1 == (err = read(STDIN_FILENO, &ch, 1))) return (int)ch; if (err < 0) LOGERR("read_key: read(stdin) failed"); else LOGERR("read_key: read(stdin) failed: no stdin"); return READ_KEY_ERROR; } else if (err < 0 && errno != EINTR) { LOGERR("read_key: poll(stdin) failed"); return READ_KEY_ERROR; } pthread_testcancel(); return READ_KEY_EAGAIN; } /* * read_key_seq() * * Read a key sequence from stdin. * Key sequence is either normal key or escape sequence. * * Returns the key or escape sequence as uint64_t. * * Originally copied from vdr: * remote.c: General Remote Control handling * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger */ #define READ_KEY_SEQ_ERROR 0xffffffff static uint64_t read_key_seq(void) { /* from vdr, remote.c */ uint64_t k = 0; int key1; if ((key1 = read_key()) >= 0) { k = key1; if (key1 == 0x1B) { /* Start of escape sequence */ if ((key1 = read_key()) >= 0) { k <<= 8; k |= key1 & 0xFF; switch (key1) { case 0x4F: /* 3-byte sequence */ if ((key1 = read_key()) >= 0) { k <<= 8; k |= key1 & 0xFF; } break; case 0x5B: /* 3- or more-byte sequence */ if ((key1 = read_key()) >= 0) { k <<= 8; k |= key1 & 0xFF; switch (key1) { case 0x31 ... 0x3F: /* more-byte sequence */ case 0x5B: /* strange, may apparently occur */ do { if ((key1 = read_key()) < 0) break; /* Sequence ends here */ k <<= 8; k |= key1 & 0xFF; } while (key1 != 0x7E); break; default:; } } break; default:; } } } } if (key1 == READ_KEY_ERROR) return READ_KEY_SEQ_ERROR; return k; } /* * kbd_receiver_thread() * * Read key(sequence)s from stdin and pass those to frontend. */ static void kbd_receiver_thread_cleanup(void *arg) { if (isatty(STDIN_FILENO)) { tcsetattr(STDIN_FILENO, TCSANOW, &saved_tm); } if (isatty(STDOUT_FILENO)) { int status; status = system("setterm -cursor on"); if (status < 0) LOGMSG("system(\"setterm -cursor on\") failed\n"); } LOGMSG("Keyboard thread terminated"); } static void *kbd_receiver_thread(void *fe_gen) { frontend_t *fe = (frontend_t*)fe_gen; uint64_t code = 0; char str[64]; if (isatty(STDOUT_FILENO)) { int status; status = system("setterm -cursor off"); if (status < 0) LOGMSG("system(\"setterm -cursor off\") failed\n"); status = system("setterm -blank off"); if (status < 0) LOGMSG("system(\"setterm -blank off\") failed\n"); } if (isatty(STDIN_FILENO)) { /* Set stdin to deliver keypresses without buffering whole lines */ tcgetattr(STDIN_FILENO, &saved_tm); if (tcgetattr(STDIN_FILENO, &tm) == 0) { tm.c_iflag = 0; tm.c_lflag &= ~(ICANON | ECHO); tm.c_cc[VMIN] = 0; tm.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSANOW, &tm); } } pthread_cleanup_push(kbd_receiver_thread_cleanup, NULL); do { alarm(0); errno = 0; code = read_key_seq(); alarm(3); /* watchdog */ if (code == 0) continue; if (code == READ_KEY_SEQ_ERROR) break; if (code == 27) { fe->send_event(fe, "QUIT"); break; } if (gui_hotkeys) { if (code == 'f' || code == 'F') { fe->send_event(fe, "TOGGLE_FULLSCREEN"); continue; } if (code == 'p' || code == 'P') { fe->send_event(fe, "POWER_OFF"); continue; } if (code == 'd' || code == 'D') { fe->send_event(fe, "TOGGLE_DEINTERLACE"); continue; } } snprintf(str, sizeof(str), "%016" PRIX64, code); fe->send_input_event(fe, "KBD", str, 0, 0); } while (fe->xine_is_finished(fe, 0) != FE_XINE_EXIT); alarm(0); LOGDBG("Keyboard thread terminating"); pthread_cleanup_pop(1); pthread_exit(NULL); return NULL; /* never reached */ } /* * slave_receiver_thread() * * Read slave mode commands from stdin * Interpret and execute valid commands */ static void slave_receiver_thread_cleanup(void *arg) { /* restore terminal settings */ tcsetattr(STDIN_FILENO, TCSANOW, &saved_tm); LOGDBG("Slave mode receiver terminated"); } static void *slave_receiver_thread(void *fe_gen) { frontend_t *fe = (frontend_t*)fe_gen; char str[128], *pt; tcgetattr(STDIN_FILENO, &saved_tm); pthread_cleanup_push(slave_receiver_thread_cleanup, NULL); do { errno = 0; str[0] = 0; pthread_testcancel(); if (!fgets(str, sizeof(str), stdin)) break; pthread_testcancel(); if (NULL != (pt = strchr(str, '\r'))) *pt = 0; if (NULL != (pt = strchr(str, '\n'))) *pt = 0; if (!strncasecmp(str, "QUIT", 4)) { fe->send_event(fe, "QUIT"); break; } if (!strncasecmp(str, "FULLSCREEN", 10)) { if (strpbrk(str + 10, "01")) fe->send_event(fe, str); else fe->send_event(fe, "TOGGLE_FULLSCREEN"); continue; } if (!strncasecmp(str, "DEINTERLACE ", 12)) { fe->send_event(fe, str); continue; } if (!strncasecmp(str, "HITK ", 5)) { fe->send_input_event(fe, NULL, str+5, 0, 0); continue; } LOGMSG("Unknown slave mode command: %s", str); } while (fe->xine_is_finished(fe, 0) != FE_XINE_EXIT); LOGDBG("Slave mode receiver terminating"); pthread_cleanup_pop(1); pthread_exit(NULL); return NULL; /* never reached */ } /* * kbd_start() * * Start keyboard/slave mode reader thread */ void kbd_start(frontend_t *fe, int slave_mode) { int err; if ((err = pthread_create (&kbd_thread, NULL, slave_mode ? slave_receiver_thread : kbd_receiver_thread, (void*)fe)) != 0) { fprintf(stderr, "Can't create new thread for keyboard (%s)\n", strerror(err)); } } /* * kbd_stop() * * Stop keyboard/slave mode reader thread */ void kbd_stop(void) { void *p; pthread_cancel(kbd_thread); pthread_join(kbd_thread, &p); } xineliboutput-2.0.0/xine_frontend_internal.h0000644000175000017500000000564513061253352017145 0ustar phph/* * xine_frontend_internal.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINE_FRONTEND_INTERNAL_H #define XINE_FRONTEND_INTERNAL_H #include #include #include "xine_frontend.h" #include "xine_input_vdr.h" #include "xine/post.h" typedef struct fe_s { /* base class */ frontend_t fe; /* from xine_frontend.c */ double (*dest_pixel_aspect) (const struct fe_s *, double video_pixel_aspect, int video_width, int video_height); void (*frame_output_handler)(void *data, int video_width, int video_height, double video_pixel_aspect, int *dest_x, int *dest_y, int *dest_width, int *dest_height, double *dest_pixel_aspect, int *win_x, int *win_y); /* called from xine_frontend.c */ void (*update_display_size_cb) (struct fe_s *); void (*toggle_fullscreen_cb) (struct fe_s *, int); /* if set before xine_init(), will be called by video driver wrapper for each frame */ void (*frame_draw_cb)(void *, vo_frame_t *); /* xine stuff */ xine_t *xine; xine_stream_t *stream; xine_stream_t *slave_stream; vdr_input_plugin_if_t *input_plugin; xine_video_port_t *video_port; xine_video_port_t *video_port_none; xine_audio_port_t *audio_port; xine_audio_port_t *audio_port_none; xine_event_queue_t *event_queue; post_plugins_t *postplugins; char *video_port_name; /* frame buffer device */ char *aspect_controller; /* path to external HW aspect ratio controller */ char *configfile; /* path of our config file */ int xine_visual_type; union { void *vis; fb_visual_t vis_fb; x11_visual_t vis_x11; raw_visual_t vis_raw; }; /* frontend */ double video_aspect; /* aspect ratio of video frame */ double display_ratio; /* aspect ratio of video window */ uint8_t terminate_key_pressed; uint16_t xpos, ypos; /* position of video window */ uint16_t width; /* size of video window */ uint16_t height; /* */ uint16_t video_width; /* size of video frame */ uint16_t video_height; /* */ uint16_t pes_buffers; /* max. number of PES packets in video fifo */ uint8_t aspect; /* aspect ratio of video window (user setting) */ uint8_t overscan; /* overscan in % (crop video borders) */ /*uint8_t cropping : 1;*/ uint8_t scale_video : 1; /* enable/disable all video scaling */ uint8_t playback_finished : 1; uint8_t slave_playback_finished : 1; char *shutdown_cmd; int shutdown_timeout; time_t shutdown_time; } fe_t; /* setup function pointers */ void init_fe(fe_t *fe); char *strn0cpy(char *dest, const char *src, int n); #endif /* XINE_FRONTEND_INTERNAL_H */ xineliboutput-2.0.0/xine_frontend_cec.h0000644000175000017500000000054113061253352016051 0ustar phph/* * xine_frontend_cec.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINE_FRONTEND_CEC_H #define XINE_FRONTEND_CEC_H struct frontend_s; void cec_start(struct frontend_s *fe, int hdmi_port, int dev_type); void cec_stop(void); #endif /* XINE_FRONTEND_CEC_H */ xineliboutput-2.0.0/xine_frontend_cec.c0000644000175000017500000004376713061253352016065 0ustar phph/* * xine_frontend_cec.c: Forward (local) HDMI-CEC keys to VDR (server) * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "features.h" #include #include #include #ifdef __FreeBSD__ #include #endif #include #include #ifdef HAVE_LIBCEC #include #endif #define LOG_MODULENAME "[hdmi-cec] " #include "logdefs.h" #include "xine_frontend.h" #include "xine_frontend_cec.h" #ifdef HAVE_LIBCEC #if defined(CEC_LIB_VERSION_MAJOR) && CEC_LIB_VERSION_MAJOR >= 3 # define HAVE_LIBCEC_3 # if CEC_LIB_VERSION_MAJOR >= 4 # define HAVE_LIBCEC_4 # endif #else typedef void * libcec_connection_t; # define libcec_initialise(c) ((void*)cec_initialise(c)) # define libcec_init_video_standalone(c) cec_init_video_standalone() # define libcec_find_adapters(a,b,c,d) cec_find_adapters(b,c,d) # define libcec_ping_adapters(c) cec_ping_adapters() # define libcec_open(c,d,e) cec_open(d,e) # define libcec_close(c) cec_close() # define libcec_destroy(c) cec_destroy() #endif /* static data */ static volatile int exit_req = 0; static pthread_t cec_thread; static int cec_hdmi_port = 0; static int cec_dev_type = 0; /* 0 - TV, 5 - AVR */ static int cec_not_found = 0; static const struct keymap_item { const uint8_t map; const char key[12]; } keymap[] = { [CEC_USER_CONTROL_CODE_SELECT] = {0, "Ok"}, [CEC_USER_CONTROL_CODE_UP] = {0, "Up"}, [CEC_USER_CONTROL_CODE_DOWN] = {0, "Down"}, [CEC_USER_CONTROL_CODE_LEFT] = {0, "Left"}, [CEC_USER_CONTROL_CODE_RIGHT] = {0, "Right"}, [CEC_USER_CONTROL_CODE_RIGHT_UP] = {1, "RIGHT_UP"}, [CEC_USER_CONTROL_CODE_RIGHT_DOWN] = {1, "RIGHT_DOWN"}, [CEC_USER_CONTROL_CODE_LEFT_UP] = {1, "LEFT_UP"}, [CEC_USER_CONTROL_CODE_LEFT_DOWN] = {1, "LEFT_DOWN"}, [CEC_USER_CONTROL_CODE_ROOT_MENU] = {0, "Menu"}, [CEC_USER_CONTROL_CODE_SETUP_MENU] = {0, "Menu"}, [CEC_USER_CONTROL_CODE_CONTENTS_MENU] = {0, "Recordings"}, [CEC_USER_CONTROL_CODE_FAVORITE_MENU] = {1, "FAVORITE"}, [CEC_USER_CONTROL_CODE_EXIT] = {0, "Back"}, [CEC_USER_CONTROL_CODE_NUMBER0] = {0, "0"}, [CEC_USER_CONTROL_CODE_NUMBER1] = {0, "1"}, [CEC_USER_CONTROL_CODE_NUMBER2] = {0, "2"}, [CEC_USER_CONTROL_CODE_NUMBER3] = {0, "3"}, [CEC_USER_CONTROL_CODE_NUMBER4] = {0, "4"}, [CEC_USER_CONTROL_CODE_NUMBER5] = {0, "5"}, [CEC_USER_CONTROL_CODE_NUMBER6] = {0, "6"}, [CEC_USER_CONTROL_CODE_NUMBER7] = {0, "7"}, [CEC_USER_CONTROL_CODE_NUMBER8] = {0, "8"}, [CEC_USER_CONTROL_CODE_NUMBER9] = {0, "9"}, [CEC_USER_CONTROL_CODE_DOT] = {1, "DOT"}, [CEC_USER_CONTROL_CODE_ENTER] = {0, "Ok"}, [CEC_USER_CONTROL_CODE_CLEAR] = {1, "CLEAR"}, [CEC_USER_CONTROL_CODE_NEXT_FAVORITE] = {1, "FAVORITE+"}, [CEC_USER_CONTROL_CODE_CHANNEL_UP] = {0, "Channel+"}, [CEC_USER_CONTROL_CODE_CHANNEL_DOWN] = {0, "Channel-"}, [CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL] = {0, "Previous"}, [CEC_USER_CONTROL_CODE_SOUND_SELECT] = {0, "Audio"}, [CEC_USER_CONTROL_CODE_INPUT_SELECT] = {1, "INP_SELECT"}, [CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION] = {0, "Info"}, [CEC_USER_CONTROL_CODE_HELP] = {1, "HELP"}, [CEC_USER_CONTROL_CODE_PAGE_UP] = {1, "PAGE_UP"}, [CEC_USER_CONTROL_CODE_PAGE_DOWN] = {1, "PAGE_DOWN"}, [CEC_USER_CONTROL_CODE_POWER] = {0, "Power"}, [CEC_USER_CONTROL_CODE_VOLUME_UP] = {0, "Volume+"}, [CEC_USER_CONTROL_CODE_VOLUME_DOWN] = {0, "Volume-"}, [CEC_USER_CONTROL_CODE_MUTE] = {0, "Mute"}, [CEC_USER_CONTROL_CODE_PLAY] = {0, "Play"}, [CEC_USER_CONTROL_CODE_STOP] = {0, "Stop"}, [CEC_USER_CONTROL_CODE_PAUSE] = {0, "Pause"}, [CEC_USER_CONTROL_CODE_RECORD] = {0, "Record"}, [CEC_USER_CONTROL_CODE_REWIND] = {0, "FastRew"}, [CEC_USER_CONTROL_CODE_FAST_FORWARD] = {0, "FastFwd"}, [CEC_USER_CONTROL_CODE_EJECT] = {1, "EJECT"}, [CEC_USER_CONTROL_CODE_FORWARD] = {0, "Next"}, [CEC_USER_CONTROL_CODE_BACKWARD] = {0, "Previous"}, //[CEC_USER_CONTROL_CODE_STOP_RECORD] = {0, ""}, //0x4D, //[CEC_USER_CONTROL_CODE_PAUSE_RECORD] = {0, ""}, //0x4E, [CEC_USER_CONTROL_CODE_ANGLE] = {1, "ANGLE"}, [CEC_USER_CONTROL_CODE_SUB_PICTURE] = {0, "Subtitles"}, //[CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND] = {0, ""}, //0x52, [CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE] = {0, "Schedule"}, [CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING] = {0, "Timers"}, //[CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION] = {0, ""}, //[CEC_USER_CONTROL_CODE_PLAY_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_RECORD_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_STOP_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_MUTE_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_TUNE_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION] = {0, ""}, //[CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION] = {0, ""}, [CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION] = {0, "Audio"}, [CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION] = {0, "Power"}, [CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION] = {0, "Power"}, //[CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION] = {0, ""}, [CEC_USER_CONTROL_CODE_F1_BLUE] = {0, "Blue"}, [CEC_USER_CONTROL_CODE_F2_RED] = {0, "Red"}, [CEC_USER_CONTROL_CODE_F3_GREEN] = {0, "Green"}, [CEC_USER_CONTROL_CODE_F4_YELLOW] = {0, "Yellow"}, [CEC_USER_CONTROL_CODE_F5] = {0, "User1"}, [CEC_USER_CONTROL_CODE_DATA] = {1, "DATA"}, //[CEC_USER_CONTROL_CODE_AN_RETURN] = {0, ""}, [CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST] = {0, "Channels"}, [0xff] = {0, ""}, }; /* * libcec callbacks */ static void _cec4_config_changed_cb(void *this_gen, const libcec_configuration *config) { LOGDBG("cec_config_changed"); } #ifndef HAVE_LIBCEC_4 static int _cec_config_changed_cb(void *this_gen, const libcec_configuration config) { _cec4_config_changed_cb(this_gen, &config); return 1; } #endif static int _cec4_menu_state_changed_cb(void *this_gen, const cec_menu_state state) { LOGDBG("cec_menu_state_changed"); return 1; } #ifndef HAVE_LIBCEC_4 static int _cec_menu_state_changed_cb(void *this_gen, const cec_menu_state state) { return _cec4_menu_state_changed_cb(this_gen, state); } #endif static void _cec_source_activated_cb(void *this_gen, const cec_logical_address address, const uint8_t param) { LOGMSG("cec_source_activated: address %d param %d", address, param); } static void _cec4_log_cb(void *this_gen, const cec_log_message *message) { if (message->level <= CEC_LOG_ERROR) { errno = 0; LOGERR("%s", message->message); } else if (message->level <= CEC_LOG_NOTICE) { LOGMSG("%s", message->message); } else if (message->level <= CEC_LOG_DEBUG) { LOGDBG("%s", message->message); } else { LOGVERBOSE("%s", message->message); } } #ifndef HAVE_LIBCEC_4 static int _cec_log_cb(void *this_gen, const cec_log_message message) { _cec4_log_cb(this_gen, &message); return 1; } #endif static void _cec4_keypress_cb(void *this_gen, const cec_keypress *keypress) { frontend_t *fe = (frontend_t*)this_gen; static int last_key = -1; LOGVERBOSE("keypress 0x%x duration %d", keypress->keycode, keypress->duration); if (keypress->keycode == last_key && keypress->duration > 0) return; else if (keypress->duration > 0) last_key = -1; else last_key = keypress->keycode; if (keypress->keycode >= sizeof(keymap) / sizeof(keymap[0]) || !keymap[keypress->keycode].key[0]) { LOGMSG("unknown keycode 0x%x", keypress->keycode); return; } LOGDBG("sending key %s%s", keymap[keypress->keycode].map ? "CEC." : "", keymap[keypress->keycode].key); alarm(3); fe->send_input_event(fe, keymap[keypress->keycode].map ? "CEC" : NULL, keymap[keypress->keycode].key, 0, 1); alarm(0); return; } #ifndef HAVE_LIBCEC_4 static int _cec_keypress_cb(void *this_gen, const cec_keypress keypress) { _cec4_keypress_cb(this_gen, &keypress); return 1; } #endif static void _cec4_alert_cb(void *this_gen, const libcec_alert type, const libcec_parameter param) { switch (type) { case CEC_ALERT_CONNECTION_LOST: LOGMSG("ALERT: CEC connection lost"); break; case CEC_ALERT_PERMISSION_ERROR: LOGMSG("ALERT: Permission error"); break; case CEC_ALERT_PORT_BUSY: LOGMSG("ALERT: Port busy"); break; case CEC_ALERT_PHYSICAL_ADDRESS_ERROR: LOGMSG("ALERT: Physical address error"); break; case CEC_ALERT_TV_POLL_FAILED: LOGMSG("ALERT: TV poll failed"); break; case CEC_ALERT_SERVICE_DEVICE: default: break; } } #ifndef HAVE_LIBCEC_4 static int _cec_alert_cb(void *this_gen, const libcec_alert type, const libcec_parameter param) { _cec4_alert_cb(this_gen, type, param); return 0; } #endif static void _cec4_command_cb(void *this_gen, const cec_command *command) { LOGMSG("Received command 0x%x from 0x%x", command->opcode, command->initiator); switch (command->opcode) { case CEC_OPCODE_STANDBY: case CEC_OPCODE_SET_MENU_LANGUAGE: case CEC_OPCODE_DECK_CONTROL: case CEC_OPCODE_PLAY: default: break; } } #ifndef HAVE_LIBCEC_4 static int _cec_command_cb(void *this_gen, const cec_command command) { _cec4_command_cb(this_gen, &command); return 1; } #endif ICECCallbacks callbacks = { #ifdef HAVE_LIBCEC_4 .logMessage = _cec4_log_cb, .keyPress = _cec4_keypress_cb, .commandReceived = _cec4_command_cb, .configurationChanged = _cec4_config_changed_cb, .alert = _cec4_alert_cb, .menuStateChanged = _cec4_menu_state_changed_cb, .sourceActivated = _cec_source_activated_cb, #else .CBCecKeyPress = _cec_keypress_cb, .CBCecCommand = _cec_command_cb, .CBCecLogMessage = _cec_log_cb, .CBCecAlert = _cec_alert_cb, .CBCecConfigurationChanged = _cec_config_changed_cb, .CBCecSourceActivated = _cec_source_activated_cb, .CBCecMenuStateChanged = _cec_menu_state_changed_cb, #endif }; /* * configuration */ static void _libcec_config_clear(libcec_configuration *p) { memset(p, 0, sizeof(*p)); p->iPhysicalAddress = CEC_PHYSICAL_ADDRESS_TV; p->baseDevice = CEC_DEFAULT_BASE_DEVICE; p->iHDMIPort = CEC_DEFAULT_HDMI_PORT; p->tvVendor = CEC_VENDOR_UNKNOWN; #ifdef HAVE_LIBCEC_3 p->clientVersion = LIBCEC_VERSION_CURRENT; p->serverVersion = LIBCEC_VERSION_CURRENT; #else p->clientVersion = CEC_CLIENT_VERSION_CURRENT; p->serverVersion = CEC_SERVER_VERSION_CURRENT; #endif p->bAutodetectAddress = CEC_DEFAULT_SETTING_AUTODETECT_ADDRESS; p->bGetSettingsFromROM = CEC_DEFAULT_SETTING_GET_SETTINGS_FROM_ROM; #ifndef HAVE_LIBCEC_4 p->bUseTVMenuLanguage = CEC_DEFAULT_SETTING_USE_TV_MENU_LANGUAGE; #endif p->bActivateSource = CEC_DEFAULT_SETTING_ACTIVATE_SOURCE; #ifndef HAVE_LIBCEC_4 p->bPowerOffScreensaver = CEC_DEFAULT_SETTING_POWER_OFF_SCREENSAVER; p->bPowerOnScreensaver = CEC_DEFAULT_SETTING_POWER_ON_SCREENSAVER; #endif p->bPowerOffOnStandby = CEC_DEFAULT_SETTING_POWER_OFF_ON_STANDBY; #ifndef HAVE_LIBCEC_4 p->bShutdownOnStandby = CEC_DEFAULT_SETTING_SHUTDOWN_ON_STANDBY; p->bSendInactiveSource = CEC_DEFAULT_SETTING_SEND_INACTIVE_SOURCE; #endif p->iFirmwareVersion = CEC_FW_VERSION_UNKNOWN; #ifndef HAVE_LIBCEC_4 p->bPowerOffDevicesOnStandby = CEC_DEFAULT_SETTING_POWER_OFF_DEVICES_STANDBY; #endif memcpy(p->strDeviceLanguage, CEC_DEFAULT_DEVICE_LANGUAGE, 3); p->iFirmwareBuildDate = CEC_FW_BUILD_UNKNOWN; p->bMonitorOnly = 0; p->cecVersion = CEC_DEFAULT_SETTING_CEC_VERSION; p->adapterType = ADAPTERTYPE_UNKNOWN; #ifdef CEC_DOUBLE_TAP_TIMEOUT_50_MS p->iDoubleTapTimeout50Ms = CEC_DOUBLE_TAP_TIMEOUT_50_MS; #else p->iDoubleTapTimeoutMs = CEC_DOUBLE_TAP_TIMEOUT_MS; #endif p->comboKey = CEC_USER_CONTROL_CODE_STOP; p->iComboKeyTimeoutMs = CEC_DEFAULT_COMBO_TIMEOUT_MS; memset(p->strDeviceName, 0, sizeof(p->strDeviceName)); //deviceTypes.Clear(); int i; for (i = 0; i < sizeof(p->deviceTypes.types) / sizeof(p->deviceTypes.types[0]); i++) p->deviceTypes.types[i] = CEC_DEVICE_TYPE_RESERVED; //logicalAddresses.Clear(); p->logicalAddresses.primary = CECDEVICE_UNREGISTERED; memset(p->logicalAddresses.addresses, 0, sizeof(p->logicalAddresses.addresses)); //wakeDevices.Clear(); p->wakeDevices.primary = CECDEVICE_UNREGISTERED; memset(p->wakeDevices.addresses, 0, sizeof(p->wakeDevices.addresses)); //powerOffDevices.Clear(); p->powerOffDevices.primary = CECDEVICE_UNREGISTERED; memset(p->powerOffDevices.addresses, 0, sizeof(p->powerOffDevices.addresses)); #if CEC_DEFAULT_SETTING_POWER_OFF_SHUTDOWN == 1 p->powerOffDevices.primary = CECDEVICE_BROADCAST; #endif #if CEC_DEFAULT_SETTING_ACTIVATE_SOURCE == 1 p->wakeDevices.primary = CECDEVICE_TV; #endif p->callbackParam = NULL; p->callbacks = NULL; } static int _cec_parse_edid(uint8_t *edid, int size) { /* get cec physical address from edid vendor-specific block */ int i; for (i = 0; i < size; i++) { if (edid[i] == 0x03 && edid[i + 1] == 0x0c && edid[i + 2] == 0x00) { /* hdmi marker found */ LOGMSG("Got CEC physical address from edid: %d.%d.%d.%d", edid[i + 3] >> 4 & 0xf, edid[i + 3] & 0xf, edid[i + 4] >> 4 & 0xf, edid[i + 4] & 0xf); return (edid[i + 3] << 8) | edid[i + 4]; } } return -1; } static int _detect_hdmi_address(frontend_t *fe_gen) { if (cec_hdmi_port <= 0) { frontend_t *fe = (frontend_t*)fe_gen; if (fe->fe_display_edid) { int cec_hdmi_address; int size = 0; uint8_t *edid; edid = fe->fe_display_edid(fe, &size); if (edid) { cec_hdmi_address = _cec_parse_edid(edid, size); free(edid); if (cec_hdmi_address > 0) { return cec_hdmi_address; } } } LOGMSG("WARNING: CEC HDMI port not given and edid reading/parsing failed"); } return 0; } static libcec_connection_t _libcec_init(void *fe_gen) { libcec_configuration config; libcec_connection_t conn; _libcec_config_clear(&config); strncpy(config.strDeviceName, "VDR", sizeof(config.strDeviceName)); config.iPhysicalAddress = _detect_hdmi_address(fe_gen); config.iHDMIPort = cec_hdmi_port; config.baseDevice = cec_dev_type; config.bActivateSource = 0; config.callbackParam = fe_gen; config.callbacks = &callbacks; config.deviceTypes.types[0] = CEC_DEVICE_TYPE_PLAYBACK_DEVICE; config.deviceTypes.types[1] = CEC_DEVICE_TYPE_RECORDING_DEVICE; config.deviceTypes.types[2] = CEC_DEVICE_TYPE_TUNER; //config.deviceTypes.types[3] = CEC_DEVICE_TYPE_AUDIO_SYSTEM; if (!(conn = libcec_initialise(&config))) { LOGMSG("libcec_initialize() failed"); return NULL; } libcec_init_video_standalone(conn); return conn; } /* * */ static int _libcec_open(libcec_connection_t conn) { cec_adapter devices[10]; int count = libcec_find_adapters(conn, devices, 10, NULL); if (count < 1) { if (!cec_not_found) { LOGMSG("No HDMI-CEC adapters found"); cec_not_found = 1; } return 0; } LOGMSG("%d adapters found. Opening %s", count, devices[0].comm); if (!libcec_open(conn, devices[0].comm, 3000)) { LOGMSG("error opening CEC adapter"); return 0; } LOGMSG("opened adapter %s", devices[0].comm); return 1; } static int _libcec_check_device(libcec_connection_t conn) { if (!libcec_ping_adapters(conn)) { LOGMSG("libcec_ping_adapters() failed"); return 0; } return 1; } static void _cleanup(void *p) { #ifdef HAVE_LIBCEC_3 libcec_connection_t conn = *(libcec_connection_t *)p; #endif libcec_close(conn); libcec_destroy(conn); } static void *_cec_receiver_thread(void *fe_gen) { libcec_connection_t conn; LOGDBG("started"); pthread_cleanup_push(_cleanup, &conn); enum { INIT, WAIT_DEVICE, RUNNING } state = INIT; while (!exit_req) { pthread_testcancel(); switch (state) { case INIT: if (!(conn = _libcec_init(fe_gen))) { return NULL; } state = WAIT_DEVICE; break; case WAIT_DEVICE: if (_libcec_open(conn)) { state = RUNNING; } usleep(5000*1000); break; case RUNNING: if (!_libcec_check_device(conn)) { state = WAIT_DEVICE; } usleep(1000*1000); break; } } pthread_cleanup_pop(1); pthread_exit(NULL); return NULL; /* never reached */ } #endif /* HAVE_LIBCEC */ /* * interface */ void cec_start(struct frontend_s *fe, int hdmi_port, int dev_type) { #ifdef HAVE_LIBCEC if (hdmi_port >= 0) { int err; exit_req = 0; cec_hdmi_port = hdmi_port; cec_dev_type = dev_type; if ((err = pthread_create (&cec_thread, NULL, _cec_receiver_thread, (void*)fe)) != 0) { fprintf(stderr, "can't create new thread for HDMI-CEC (%s)\n", strerror(err)); } } #endif /* HAVE_LIBCEC */ } void cec_stop(void) { #ifdef HAVE_LIBCEC if (!exit_req) { void *p; exit_req = 1; pthread_cancel (cec_thread); pthread_join (cec_thread, &p); } #endif /* HAVE_LIBCEC */ } xineliboutput-2.0.0/xine_frontend.h0000644000175000017500000001024513061253352015241 0ustar phph/* * xine_frontend.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINE_FRONTEND_H #define _XINE_FRONTEND_H #ifdef __cplusplus extern "C" { #endif #define FE_VERSION_STR XINELIBOUTPUT_VERSION /*"1.0.0pre1"*/ typedef struct frontend_config_s frontend_config_t; typedef struct frontend_s frontend_t; struct osd_command_s; #if 0 struct frontend_config_s { /* Display */ int width; int height; int fullscreen; int modeswitch; char *modeline; int aspect; char *video_port; int scale_video; fe_keypress_f keypresshandler; /* Xine engine */ char *audio_driver; char *audio_port; char *video_driver; int pes_buffers; int priority; }; #endif /* xine_is_finished return values */ #define FE_XINE_RUNNING 0 #define FE_XINE_ERROR -1 #define FE_XINE_EXIT 1 /* return values */ #define FE_OK 1 #define FE_ERROR 0 /* HUD OSD bitmask values */ #define HUD_COMPOSITE 0x1 /* hud in transparent window */ #define HUD_XSHAPE 0x2 /* use XShape */ #define HUD_OPENGL 0x4 /* draw OSD and video using OpenGL */ #define HUD_XRENDER 0x8 /* draw OSD and video using Xrender */ /* Special window_id's */ #define WINDOW_ID_NONE -1 #define WINDOW_ID_ROOT -2 /* Video mode switching flags */ #define MODESWITCH_SIZE 0x1 #define MODESWITCH_RATE 0x2 #define MODESWITCH_INTERLACE 0x4 struct frontend_s { /* Display */ int (*fe_display_open)(frontend_t*, int xpos, int ypos, int winwidth, int winheight, int fullscreen, int hud, int opengl, int modeswitch, const char *modeline, int aspect, int no_x_kbd, int gui_hotkeys, int touchscreen, const char *video_port, int scale_video, const char *aspect_controller, int window_id); int (*fe_display_config)(frontend_t *, int xpos, int ypos, int width, int height, int fullscreen, int modeswitch, const char *modeline, int aspect, int scale_video); unsigned char * (*fe_display_edid)(frontend_t *, int *size); void (*fe_display_close)(frontend_t*); /* Xine engine */ int (*xine_init)(frontend_t*, const char *audio_driver, const char *audio_port, const char *video_driver, int pes_buffers, const char *static_post, const char *config_file); int (*xine_open)(frontend_t*, const char *mrl); int (*xine_play)(frontend_t*); int (*xine_stop)(frontend_t*); void (*xine_close)(frontend_t*); void (*xine_exit)(frontend_t*); void (*shutdown_init)(frontend_t*, const char *cmd, int timeout); /* Execution control */ int (*fe_run)(frontend_t*); void (*fe_interrupt)(frontend_t*); void (*fe_free)(frontend_t*); int (*xine_is_finished)(frontend_t*, int slave_stream); /* Data transfer VDR -> frontend/xine */ int (*xine_osd_command)(frontend_t*, struct osd_command_s *cmd); int (*xine_control)(frontend_t*, const char *cmd); int (*xine_queue_pes_packet)(frontend_t*, int stream, uint64_t pos, const char *data, int len); char *(*grab)(frontend_t*, int *size, int jpeg, int quality, int width, int height); /* frontend services: events from frontend -> xine/vdr */ int (*send_event)(frontend_t *fe, const char *data); int (*send_input_event)(frontend_t *fe, const char *map, const char *key, int repeat, int release); /* Control messages frontend -> VDR (local mode) * frontend should fill following pointers */ void *fe_message_h; void (*fe_message_cb)(void *, const char *keymap, const char *name); #if 0 frontend_config_t config; #endif }; typedef frontend_t *(*fe_creator_f)(void); void list_xine_plugins(frontend_t *fe, int verbose); #ifdef __cplusplus } /* extern "C" { */ #endif #endif /* _XINE_FRONTEND_H */ xineliboutput-2.0.0/xine_frontend.c0000644000175000017500000016442313061253352015244 0ustar phph/* * xine_frontend.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "features.h" #ifndef _GNU_SOURCE # define _GNU_SOURCE // asprintf #endif #include #include #include #include #include #include #ifdef HAVE_LIBJPEG # ifdef boolean # define HAVE_BOOLEAN # endif # include # undef boolean #endif #define XINE_ENGINE_INTERNAL #include #include #define LOG_MODULENAME "[vdr-fe] " #include "logdefs.h" #include "xine_frontend_internal.h" #include "xine/post.h" #include "xine/vo_post.h" #include "xine/vo_osdscaler.h" #include "xine/vo_osdreorder.h" #include "xine/vo_lastpts.h" #include "xine/vo_frameoutput.h" #undef MIN #define MIN(a,b) ( (a) < (b) ? (a) : (b)) #undef MAX #define MAX(a,b) ( (a) > (b) ? (a) : (b)) static void intercept_video_driver(xine_video_port_t *video_port) { vo_driver_t *osdscaler = osdscaler_init(); if (! wire_video_driver(video_port, osdscaler)) { LOGMSG("wire_video_driver() for osdscaler failed"); osdscaler->dispose(osdscaler); } vo_driver_t *osdreorder = osd_reorder_init(); if (! wire_video_driver(video_port, osdreorder)) { LOGMSG("wire_video_driver() for osdreorder failed"); osdreorder->dispose(osdreorder); } vo_driver_t *lastpts = vo_lastpts_init(); if (! wire_video_driver(video_port, lastpts)) { LOGMSG("wire_video_driver() for vo_lastpts failed"); lastpts->dispose(lastpts); } } char *strn0cpy(char *dest, const char *src, int n) { char *s = dest; for ( ; --n && (*dest = *src) != 0; dest++, src++) ; *dest = 0; return s; } static int guess_cpu_count(void) { static int cores = -1; FILE *f; if(cores >= 0) return cores; cores = 0; if(NULL != (f = fopen("/proc/cpuinfo", "r"))) { char buf[256]; while (NULL != fgets(buf, 255, f)) sscanf(buf, "processor : %d", &cores); fclose(f); } cores++; if(cores > 1) LOGMSG("Detected %d CPUs", cores); else LOGDBG("Detected single CPU. Multithreaded decoding and post processing disabled."); return cores; } static void shutdown_system(char *cmd, int user_requested) { const char *reason = user_requested ? "User requested system shutdown" : "Inactivity timer elapsed"; if (cmd) { LOGMSG("%s. Executing '%s'", reason, cmd); if (-1 == system(cmd)) LOGERR("Can't execute %s", cmd); } else { LOGMSG("%s, power off comand undefined!", reason); } } static void make_dirs(const char *file) { struct stat st; char *s = strdup(file); char *p = s; if (*p == '/') { p++; while ((p = strchr(p, '/')) != NULL) { *p = 0; if (stat(s, &st) != 0 || !S_ISDIR(st.st_mode)) { if (mkdir(s, ACCESSPERMS) == -1) { LOGERR("Can't create %s", s); break; } LOGDBG("Created directory %s", s); } *p++ = '/'; } } free(s); } /* * list available plugins */ static void list_plugins_type(xine_t *xine, const char *msg, typeof (xine_list_audio_output_plugins) list_func) { const char *const *list = list_func(xine); printf("%s", msg); while(list && *list) printf(" %s", *list++); printf("\n"); } void list_xine_plugins(frontend_t *fe, int verbose) { fe_t *this = (fe_t *)fe; xine_t *tmp_xine = NULL; xine_t *xine = this ? this->xine : NULL; if (!xine) xine_init(xine = tmp_xine = xine_new()); list_plugins_type (xine, "Available video drivers:", xine_list_video_output_plugins); list_plugins_type (xine, "Available audio drivers:", xine_list_audio_output_plugins); if (verbose) { list_plugins_type (xine, "Available post plugins: ", xine_list_post_plugins); list_plugins_type (xine, "Available input plugins:", xine_list_input_plugins); list_plugins_type (xine, "Available demux plugins:", xine_list_demuxer_plugins); list_plugins_type (xine, "Available audio decoder plugins:", xine_list_audio_decoder_plugins); list_plugins_type (xine, "Available video decoder plugins:", xine_list_video_decoder_plugins); list_plugins_type (xine, "Available SPU decoder plugins: ", xine_list_spu_plugins); } if (tmp_xine) xine_exit(tmp_xine); } /* * detect input plugin */ static int find_input_plugin(fe_t *this) { if(!this->input_plugin) { if(!this->stream || !this->stream->input_plugin || !this->stream->input_plugin->input_class || this->playback_finished) { LOGMSG("find_input_plugin: stream not initialized or playback finished !"); usleep(100*1000); return 0; } #if XINE_VERSION_CODE < 10190 if(strcmp(this->stream->input_plugin->input_class->get_identifier( this->stream->input_plugin->input_class), MRL_ID)) { #else if(strcmp(this->stream->input_plugin->input_class->identifier, MRL_ID)) { #endif LOGMSG("find_input_plugin: current xine input plugin is not " MRL_ID " !"); return 0; } this->input_plugin = (vdr_input_plugin_if_t*)this->stream->input_plugin; } return 1; } static void *fe_control(frontend_t *this_gen, const char *cmd); /* * xine callbacks */ static double fe_dest_pixel_aspect(const fe_t *this, double video_pixel_aspect, int video_width, int video_height) { /*int new_cropping = 0;*/ double result = 1.0; if(!this->scale_video) { /*#warning what to do if scaling disabled ???*/ /*return video_pixel_aspect;*/ } switch(this->aspect) { /* Auto */ default: { double correction = ((double)video_width/(double)video_height) / ((double)this->width/(double)this->height); result = video_pixel_aspect * correction; if(result > (16.9/9.0 * (double)this->height/ (double)this->width)) result = (16.0/9.0 * (double)this->height/ (double)this->width); break; } /* Default */ case 1: result = this->display_ratio; break; /* 4:3 */ case 2: result = (4.0/3.0 * (double)this->height/(double)this->width); break; /* 16:9 */ case 3: result = (16.0/9.0 * (double)this->height/(double)this->width); break; /* 16:10 */ case 4: result = (16.0/10.0 * (double)this->height/(double)this->width); break; /* Pan&Scan */ case 5: { double aspect_diff /*= video_pixel_aspect - 1.0*/; /* TODO */ /* does not work (?) */ aspect_diff=(video_pixel_aspect*(double)video_width/(double)video_height) - 4.0 / 3.0; if ((aspect_diff < 0.05) && (aspect_diff > -0.05)) { result = (4.0/3.0 * (double)this->height/(double)this->width); /*LOGDBG("diff: %f", aspect_diff);*/ /*new_cropping = 1;*/ } else { result = (16.0/9.0 * (double)this->height/(double)this->width); } /*result = (4.0/3.0 * (double)this->height/(double)this->width);*/ break; } /* center cut-out */ case 6: { /*#warning center cut-out mode not implemented*/ break; } } #if 0 if(this->cropping && !new_cropping) { LOGDBG("pan&scan CROP OFF"); xine_set_param(this->stream, XINE_PARAM_VO_CROP_LEFT, 0); xine_set_param(this->stream, XINE_PARAM_VO_CROP_TOP, 72); xine_set_param(this->stream, XINE_PARAM_VO_CROP_RIGHT, 0); xine_set_param(this->stream, XINE_PARAM_VO_CROP_BOTTOM, 72); this->cropping = 0; } if(!this->cropping && new_cropping) { LOGDBG("pan&scan CROP ON"); /*** Should set unscaled osd (or top & bottom will be cropped off) */ xine_set_param(this->stream, XINE_PARAM_VO_CROP_LEFT, 0); xine_set_param(this->stream, XINE_PARAM_VO_CROP_TOP, 0); xine_set_param(this->stream, XINE_PARAM_VO_CROP_RIGHT, 0); xine_set_param(this->stream, XINE_PARAM_VO_CROP_BOTTOM, 0); this->cropping = 1; } #endif return result; } static void fe_frame_output_cb (void *data, int video_width, int video_height, double video_pixel_aspect, int *dest_x, int *dest_y, int *dest_width, int *dest_height, double *dest_pixel_aspect, int *win_x, int *win_y) { fe_t *this = (fe_t *)data; if (!this) return; *dest_width = this->width; *dest_height = this->height; *dest_x = 0; *dest_y = 0; #if 1 if(!this->scale_video) { if(video_height < this->height) { *dest_height = video_height; *dest_y = (this->height - video_height) / 2; } if(video_width < this->width) { *dest_width = video_width; *dest_x = (this->width - video_width) / 2; } } #endif *win_x = this->xpos; *win_y = this->ypos; *dest_pixel_aspect = this->dest_pixel_aspect(this, video_pixel_aspect, video_width, video_height); #if 0 if(this->cropping) { *dest_pixel_aspect = *dest_pixel_aspect * (16.0/9.0)/(4.0/3.0); *dest_y = *dest_y - 72; *dest_height = *dest_height + 144; } #endif #if 0 /* video_out cropping works better */ if(this->overscan) { int crop_x = this->overscan * this->width / 100 / 2; int crop_y = this->overscan * this->height / 100 / 2; *dest_x -= crop_x; *dest_y -= crop_y; *dest_width += crop_x; *dest_height += crop_y; } #endif if(!this->stream) return; _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_RATIO, (int)(10000.0*video_pixel_aspect * ((double)video_width)/((double)video_height))); if(this->video_width != video_width || this->video_height != video_height) { xine_format_change_data_t framedata = { .width = video_width, .height = video_height, .aspect = 0, /* TODO */ .pan_scan = 0, }; const xine_event_t event = { .type = XINE_EVENT_FRAME_FORMAT_CHANGE, .stream = this->stream, .data = &framedata, .data_length = sizeof(framedata), }; xine_event_send(this->stream, &event); this->video_width = video_width; this->video_height = video_height; #if 0 /* trigger forced redraw to make cropping changes effective */ if(this->cropping) xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_X, 100); #endif } if(this->aspect_controller) { double video_aspect = (video_pixel_aspect * (double)video_width / (double)video_height); double aspect_diff = video_aspect - this->video_aspect; if ((aspect_diff > 0.05) || (aspect_diff < -0.05)) { char cmd[4096]; if(snprintf(cmd, sizeof(cmd), "%s %d", this->aspect_controller, (int)(video_aspect * 10000.0)) < (int)sizeof(cmd)) { LOGDBG("Aspect ratio changed, executing %s", cmd); if(system(cmd) == -1) LOGERR("Executing /bin/sh -c %s failed", cmd); this->video_aspect = video_aspect; } } } } static void xine_event_cb (void *user_data, const xine_event_t *event) { fe_t *this = (fe_t *)user_data; switch (event->type) { /* in local mode: vdr stream / slave stream ; in remote mode: vdr stream only */ case XINE_EVENT_UI_PLAYBACK_FINISHED: LOGDBG("XINE_EVENT_UI_PLAYBACK_FINISHED"); if(this) { if(event->stream == this->stream) this->playback_finished = 1; } else { LOGMSG("xine_event_cb: NO USER DATA !"); } break; default: break; } } /* * fe_xine_init * * initialize xine engine, initialize audio and video ports, setup stream */ #define MONO 0 #define STEREO 1 #define HEADPHONES 2 #define SURROUND21 3 #define SURROUND3 4 #define SURROUND4 5 #define SURROUND41 6 #define SURROUND5 7 #define SURROUND51 8 #define SURROUND6 9 #define SURROUND61 10 #define SURROUND71 11 #define A52_PASSTHRU 12 #define x_reg_num(x...) xine_config_register_num(this->xine, x) #define x_reg_str(x...) xine_config_register_string(this->xine, x) #define x_reg_enum(x...) xine_config_register_enum(this->xine, x) #define x_reg_bool(x...) xine_config_register_bool(this->xine, x) #define x_upd_num(x...) this->xine->config->update_num(this->xine->config, x) #define x_upd_str(x...) this->xine->config->update_string(this->xine->config, x) static void configure_audio_out(const fe_t *this, const char *audio_driver, const char *audio_port) { /* * alsa */ if(audio_driver && audio_port && !strcmp("alsa", audio_driver) && strlen(audio_port) > 0) { /* define possible speaker arrangements */ /* From xine audio_alsa_out.c ; must be synchronized ! */ static char *speaker_arrangement[] = {"Mono 1.0", "Stereo 2.0", "Headphones 2.0", "Stereo 2.1", "Surround 3.0", "Surround 4.0", "Surround 4.1", "Surround 5.0", "Surround 5.1", "Surround 6.0", "Surround 6.1", "Surround 7.1", "Pass Through", NULL}; x_reg_enum("audio.output.speaker_arrangement", STEREO, speaker_arrangement, _("speaker arrangement"), _("Select how your speakers are arranged, " "this determines which speakers xine uses for sound output. " "The individual values are:\n\n" "Mono 1.0: You have only one speaker.\n" "Stereo 2.0: You have two speakers for left and right channel.\n" "Headphones 2.0: You use headphones.\n" "Stereo 2.1: You have two speakers for left and right channel, and one " "subwoofer for the low frequencies.\n" "Surround 3.0: You have three speakers for left, right and rear channel.\n" "Surround 4.0: You have four speakers for front left and right and rear " "left and right channels.\n" "Surround 4.1: You have four speakers for front left and right and rear " "left and right channels, and one subwoofer for the low frequencies.\n" "Surround 5.0: You have five speakers for front left, center and right and " "rear left and right channels.\n" "Surround 5.1: You have five speakers for front left, center and right and " "rear left and right channels, and one subwoofer for the low frequencies.\n" "Surround 6.0: You have six speakers for front left, center and right and " "rear left, center and right channels.\n" "Surround 6.1: You have six speakers for front left, center and right and " "rear left, center and right channels, and one subwoofer for the low frequencies.\n" "Surround 7.1: You have seven speakers for front left, center and right, " "left and right and rear left and right channels, and one subwoofer for the " "low frequencies.\n" "Pass Through: Your sound system will receive undecoded digital sound from xine. " "You need to connect a digital surround decoder capable of decoding the " "formats you want to play to your sound card's digital output."), 10, NULL, NULL); x_reg_str("audio.device.alsa_default_device", "default", _("device used for mono output"), _("xine will use this alsa device " "to output mono sound.\n" "See the alsa documentation " "for information on alsa devices."), 10, NULL, NULL); x_reg_str("audio.device.alsa_front_device", "plug:front:default", _("device used for stereo output"), _("xine will use this alsa device " "to output stereo sound.\n" "See the alsa documentation " "for information on alsa devices."), 10, NULL, NULL); x_reg_str("audio.device.alsa_surround51_device", "plug:surround51:0", _("device used for 5.1-channel output"), _("xine will use this alsa device to output " "5 channel plus LFE (5.1) surround sound.\n" "See the alsa documentation for information " "on alsa devices."), 10, NULL, NULL); x_reg_str("audio.device.alsa_passthrough_device", "iec958:AES0=0x6,AES1=0x82,AES2=0x0,AES3=0x2", _("device used for 5.1-channel output"), _("xine will use this alsa device to output " "undecoded digital surround sound. This can " "be used be external surround decoders.\nSee the " "alsa documentation for information on alsa " "devices."), 10, NULL, NULL); x_upd_str("audio.device.alsa_front_device", audio_port); x_upd_str("audio.device.alsa_default_device", audio_port); x_upd_str("audio.device.alsa_surround51_device", audio_port); if(strstr(audio_port, "iec") || strstr(audio_port, "spdif")) { x_upd_str("audio.device.alsa_passthrough_device", audio_port); x_upd_num("audio.output.speaker_arrangement", A52_PASSTHRU); } else { #if 0 x_upd_num("audio.output.speaker_arrangement", strstr(audio_port, "surround") ? SURROUND51 : STEREO); #endif } } /* * OSS */ if(audio_driver && !strcmp("oss", audio_driver) && audio_port) { int devnum = -2; if(!strcmp("default", audio_port)) devnum = -1; if(!strncmp("/dev/dsp", audio_port, 8) && sscanf(audio_port+8, "%d", &devnum) < 1) devnum = -1; if(!strncmp("/dev/sound/dsp", audio_port, 14) && sscanf(audio_port+14, "%d", &devnum) < 1) devnum = -1; if(devnum > -2) { x_reg_num("audio.device.oss_device_number", -1, _("OSS audio device number, -1 for none"), _("The full audio device name is created by concatenating the " "OSS device name and the audio device number.\n" "If you do not need a number because you are happy with " "your system's default audio device, set this to -1.\n" "The range of this value is -1 or 0-15. This setting is " "ignored, when the OSS audio device name is set to \"auto\"."), 10, NULL, NULL); x_upd_num("audio.device.oss_device_num", devnum); } } } static int fe_xine_init(frontend_t *this_gen, const char *audio_driver, const char *audio_port, const char *video_driver, int pes_buffers, const char *static_post_plugins, const char *config_file) { fe_t *this = (fe_t*)this_gen; post_plugins_t *posts = NULL; if(!this) return 0; /* check xine-lib version */ if(!xine_check_version(1, 1, 0)) { LOGERR("xine-lib is too old, require at least xine library version 1.1.0\n"); return FE_ERROR; } /* * init xine engine */ if(this->xine) this->fe.xine_exit(this_gen); this->stream = NULL; this->video_port = NULL; this->audio_port = NULL; this->input_plugin = NULL; /* create a new xine and load config file */ this->xine = xine_new(); if(!this->xine) return 0; /* Set xine engine logging verbosity */ this->xine->verbosity = (SysLogLevel>2); /*xine_register_log_cb(this->xine, xine_log_cb, this);*/ free(this->configfile); this->configfile = NULL; if (config_file) this->configfile = strdup(config_file); else if(asprintf(&this->configfile, "%s%s", xine_get_homedir(), "/.xine/config_xineliboutput") < 0) return 0; LOGDBG("Using xine-lib config file %s", this->configfile); make_dirs(this->configfile); xine_config_load (this->xine, this->configfile); x_reg_num ("engine.buffers.video_num_buffers", 500, _("number of video buffers"), _("The number of video buffers " "(each is 8k in size) " "xine uses in its internal queue. " "Higher values " "mean smoother playback for unreliable " "inputs, but " "also increased latency and memory " "consumption."), 20, NULL, NULL); x_reg_bool("gui.osd_use_unscaled", 0, _("Use unscaled OSD"), _("Use unscaled (full screen resolution) OSD if possible"), 10, NULL, NULL); xine_init (this->xine); x_upd_num("video.device.xv_double_buffer", 1); x_upd_num("engine.buffers.video_num_buffers", pes_buffers); if(this->video_port_name) { if(video_driver && !strcmp(video_driver, "fb")) x_upd_str("video.device.fb_device", this->video_port_name); } this->playback_finished = 0; /* create video port */ if(video_driver && !strcmp(video_driver, "none")) this->video_port = xine_open_video_driver(this->xine, video_driver, XINE_VISUAL_TYPE_NONE, NULL); else if(video_driver && !strcmp(video_driver, "dxr3")) this->video_port = xine_open_video_driver(this->xine, video_driver, XINE_VISUAL_TYPE_X11, NULL); else if(video_driver && !strcmp(video_driver, "aadxr3")) this->video_port = xine_open_video_driver(this->xine, video_driver, XINE_VISUAL_TYPE_AA, NULL); else this->video_port = xine_open_video_driver(this->xine, video_driver, this->xine_visual_type, (void *) &(this->vis)); if(!this->video_port) { LOGMSG("fe_xine_init: xine_open_video_driver(\"%s\") failed", video_driver?video_driver:"(NULL)"); this->fe.xine_exit(this_gen); return 0; } intercept_video_driver(this->video_port); if (this->frame_draw_cb) { vo_driver_t *frameoutput = vo_frameoutput_init(this, this->frame_draw_cb); if (! wire_video_driver(this->video_port, frameoutput)) { LOGMSG("wire_video_driver() for frame output handler failed"); frameoutput->dispose(frameoutput); } } this->video_port_none = NULL; /* re-configure display size (DirectFB driver changes display mode in init) */ if(this->update_display_size_cb) this->update_display_size_cb(this); /* create audio port */ if(audio_driver && audio_port) configure_audio_out(this, audio_driver, audio_port); if(audio_driver && !strcmp(audio_driver, "auto")) { this->audio_port = xine_open_audio_driver (this->xine, NULL, NULL); #if XINE_VERSION_CODE < 10190 } else if(audio_driver && !strcmp(audio_driver, "none")) { this->audio_port = _x_ao_new_port (this->xine, NULL, 1); this->audio_port->set_property(this->audio_port, AO_PROP_DISCARD_BUFFERS, 1); #endif } else { this->audio_port = xine_open_audio_driver (this->xine, audio_driver, NULL); } if(!this->audio_port && (audio_driver && !!strcmp(audio_driver, "none"))) { LOGMSG("fe_xine_init: xine_open_audio_driver(\"%s%s%s\") failed", audio_driver?audio_driver:"(NULL)", audio_port?":":"", audio_port?audio_port:""); } this->audio_port_none = NULL; /* create stream */ this->stream = xine_stream_new(this->xine, this->audio_port, this->video_port); this->slave_stream = NULL; if(!this->stream) { LOGMSG("fe_xine_init: xine_stream_new failed"); this->fe.xine_exit(this_gen); return 0; } /* event handling */ this->event_queue = xine_event_new_queue (this->stream); xine_event_create_listener_thread (this->event_queue, xine_event_cb, this); if(!this->event_queue) LOGMSG("fe_xine_init: xine_event_new_queue failed"); /* misc. config */ this->pes_buffers = pes_buffers; posts = this->postplugins = calloc(1, sizeof(post_plugins_t)); posts->xine = this->xine; posts->audio_port = this->audio_port; posts->video_port = this->video_port; posts->video_source = posts->audio_source = this->stream; /* multithreaded decoding / post processing */ if(guess_cpu_count() > 1) { if(!xine_check_version(1,1,9)) LOGMSG("FFmpeg multithreaded video decoding is not supported in xine-lib < 1.1.9"); else LOGMSG("Enabling FFmpeg multithreaded video decoding"); /* try to enable anyway, maybe someone is using patched 1.1.8 ... */ x_upd_num("video.processing.ffmpeg_thread_count", guess_cpu_count()); #if 0 LOGMSG("Enabling multithreaded post processing"); vpplugin_parse_and_store_post(posts, "thread"); #endif } /* static post plugins from command-line */ if(static_post_plugins && *static_post_plugins) { int i; LOGDBG("static post plugins (from command line): %s", static_post_plugins); posts->static_post_plugins = strdup(static_post_plugins); vpplugin_parse_and_store_post(posts, posts->static_post_plugins); applugin_parse_and_store_post(posts, posts->static_post_plugins); for(i=0; ipost_audio_elements_num; i++) if(posts->post_audio_elements[i]) posts->post_audio_elements[i]->enable = 2; for(i=0; ipost_video_elements_num; i++) if(posts->post_video_elements[i]) posts->post_video_elements[i]->enable = 2; posts->post_video_enable = 1; posts->post_audio_enable = 1; } this->video_width = this->video_height = 0; return 1; } static void fe_shutdown_init(frontend_t *this_gen, const char *cmd, int timeout) { fe_t *this = (fe_t*)this_gen; free(this->shutdown_cmd); this->shutdown_cmd = cmd ? strdup(cmd) : NULL; this->shutdown_timeout = timeout; this->shutdown_time = (timeout <= 0) ? (time_t)-1 : time(NULL) + timeout; } /* * fe_xine_open * * open xine stream */ static int fe_xine_open(frontend_t *this_gen, const char *mrl) { fe_t *this = (fe_t*)this_gen; int result = 0; char *url = NULL; if(!this) return 0; this->input_plugin = NULL; this->playback_finished = 1; this->terminate_key_pressed = 0; if (asprintf(&url, "%s#nocache", mrl ? : MRL_ID "://") < 0) return 0; result = xine_open(this->stream, url); if(!result) { LOGMSG("fe_xine_open: xine_open(\"%s\") failed", url); free(url); return 0; } free(url); #if 0 this->xine->config->update_num(this->xine->config, "video.output.xv_double_buffer", 1); #endif x_upd_num("engine.buffers.video_num_buffers", this->pes_buffers); return result; } /* * post plugin handling * */ #define POST_AUDIO_VIS 0 #define POST_AUDIO 1 #define POST_VIDEO 2 #define POST_VIDEO_PIP 3 static void init_dummy_ports(fe_t *this, int on) { if(!on) { if(this->slave_stream) LOGMSG("ERROR: init_dummy_ports(false) called while port is still in use !"); if(this->audio_port_none) xine_close_audio_driver(this->xine, this->audio_port_none); this->audio_port_none = NULL; if(this->video_port_none) xine_close_video_driver(this->xine, this->video_port_none); this->video_port_none = NULL; } else { if(! this->audio_port_none) #if XINE_VERSION_CODE < 10190 this->audio_port_none = _x_ao_new_port (this->xine, NULL, 1); #else this->audio_port_none = NULL;/*xine_new_framegrab_audio_port(this->xine);*/ #endif if(this->audio_port_none) this->audio_port_none->set_property(this->audio_port_none, AO_PROP_DISCARD_BUFFERS, 1); /*LOGMSG("initialized dummy audio port %x", this->audio_port_none);*/ #if 0 if(! this->video_port_none) this->video_port_none = _x_vo_new_port(this->xine, _x_load_video_output_plugin(this->xine, "none", XINE_VISUAL_TYPE_NONE, NULL), 1); this->video_port_none->set_property(this->video_port_none, VO_PROP_DISCARD_FRAMES, 1); #endif } } static void fe_post_unwire(fe_t *this) { if (!this || !this->stream) return; xine_post_out_t *vo_source = xine_get_video_source(this->stream); xine_post_out_t *ao_source = xine_get_audio_source(this->stream); if(this->slave_stream && this->slave_stream == this->postplugins->audio_source) { LOGDBG("unwiring slave stream audio post plugins"); init_dummy_ports(this, 1); if(ao_source && this->audio_port_none) (void) xine_post_wire_audio_port(ao_source, this->audio_port_none); ao_source = xine_get_audio_source(this->slave_stream); if(ao_source && this->audio_port) (void) xine_post_wire_audio_port(ao_source, this->audio_port); } else { LOGDBG("unwiring audio post plugins"); init_dummy_ports(this, 0); if(ao_source && this->audio_port) (void) xine_post_wire_audio_port(ao_source, this->audio_port); } if(this->slave_stream && this->slave_stream == this->postplugins->video_source) { LOGDBG("unwiring slave stream video post plugins"); /*init_dummy_ports(this, 1);*/ /*(void) xine_post_wire_video_port(vo_source, this->video_port_none);*/ vo_source = xine_get_video_source(this->slave_stream); if(vo_source && this->video_port) (void) xine_post_wire_video_port(vo_source, this->video_port); } else { LOGDBG("unwiring video post plugins"); /*init_dummy_ports(this, 0);*/ if(vo_source && this->video_port) (void) xine_post_wire_video_port(vo_source, this->video_port); } } static void fe_post_rewire(const fe_t *this) { LOGDBG("re-wiring post plugins"); vpplugin_rewire_posts(this->postplugins); applugin_rewire_posts(this->postplugins); } static void fe_post_unload(const fe_t *this) { if (this->postplugins) { LOGDBG("unloading post plugins"); vpplugin_unload_post(this->postplugins, NULL); applugin_unload_post(this->postplugins, NULL); } } static void fe_post_close(const fe_t *this, const char *name, int which) { if(!this) return; post_plugins_t *posts = this->postplugins; if(name && !strcmp(name, "AudioVisualization")) { name = NULL; which = POST_AUDIO_VIS; } if(name && !strcmp(name, "Pip")) { name = NULL; which = POST_VIDEO_PIP; } /* by name */ if(name) { LOGDBG("closing post plugin: %s", name); if(applugin_unload_post(posts, name)) { /*LOGDBG(" * rewiring audio");*/ applugin_rewire_posts(posts); return; } if(vpplugin_unload_post(posts, name)) { /*LOGDBG(" * rewiring video");*/ vpplugin_rewire_posts(posts); return; } return; } /* by type */ if(which == POST_AUDIO_VIS || which < 0) { /* audio visualization */ if(posts->post_vis_elements_num && posts->post_vis_elements && posts->post_vis_elements[0]) { LOGDBG("Closing audio visualization post plugins"); if(applugin_unload_post(posts, posts->post_vis_elements[0]->name)) { /*LOGDBG(" * rewiring audio");*/ applugin_rewire_posts(posts); } } } if(which == POST_AUDIO || which < 0) { /* audio effect(s) */ LOGDBG("Closing audio post plugins"); if(applugin_disable_post(posts, NULL)) { /*LOGDBG(" * rewiring audio");*/ applugin_rewire_posts(posts); } } if(which == POST_VIDEO || which < 0) { /* video effect(s) */ LOGDBG("Closing video post plugins"); if(vpplugin_unload_post(posts, NULL)) { /*LOGDBG(" * rewiring video");*/ vpplugin_rewire_posts(posts); } } if(which == POST_VIDEO_PIP || which < 0) { /* Picture-In-Picture */ if(posts->post_pip_elements_num && posts->post_pip_elements && posts->post_pip_elements[0]) { LOGDBG("Closing PIP (mosaico) post plugins"); if(vpplugin_unload_post(posts, "mosaico")) { /*LOGDBG(" * rewiring video");*/ vpplugin_rewire_posts(posts); } } } } static int get_opt_val(const char *s, const char *opt) { int val = -1; const char *pt = strstr(s, opt); if(pt) if(1 == sscanf(pt+strlen(opt)+1, "%d", &val)) return val; return -1; } static void fe_post_open(const fe_t *this, const char *name, const char *args) { if(!this || !this->xine || !this->stream || !name) return; post_plugins_t *posts = this->postplugins; char initstr[1024]; int found = 0; /* pip */ if(!strcmp(name, "Pip")) { posts->post_pip_enable = 1; name = "mosaico"; if(!posts->post_pip_elements || !posts->post_vis_elements[0] || !posts->post_vis_elements[0]->enable) LOGMSG("enabling picture-in-picture (\"%s:%s\") post plugin", name, args); } if(args) { snprintf(initstr, sizeof(initstr), "%s:%s", name, args); initstr[sizeof(initstr)-1] = 0; } else strn0cpy(initstr, name, sizeof(initstr)); /* swscale aspect ratio */ if (!strcmp(name, "swscale")) { char *pt = strstr(initstr, "output_aspect=auto"); if (pt) { char tmp[16]; double r = 0.0; pt += 14; switch(this->aspect) { case 0: case 1: /* */ r = this->display_ratio * (double)this->width / (double)this->height; break; case 2: /* 4:3 */ r = 4.0/3.0; break; case 3: /* 16:9 */ r = 16.0/9.0; break; case 4: /* 16:10 */ r = 16.0/10.0; break; default: LOGDBG("%s(%d): unknown aspect: %d", __FUNCTION__, __LINE__, this->aspect); } /* in finnish locale decimal separator is "," - same as post plugin parameter separator :( */ sprintf(tmp, "%04d", (int)(r*1000.0)); strncpy(pt, tmp, 4); } } LOGDBG("opening post plugin: %s", initstr); /* close old audio visualization plugin */ if(!strcmp(name,"goom") || !strcmp(name,"oscope") || !strcmp(name,"fftscope") || !strcmp(name,"fftgraph") || !strcmp(name,"fooviz")) { /* close if changed */ if(posts->post_vis_elements_num && posts->post_vis_elements && posts->post_vis_elements[0] && strcmp(name, posts->post_vis_elements[0]->name)) { fe_post_close(this, NULL, POST_AUDIO_VIS); } if(!found && applugin_enable_post(posts, initstr, &found)) { posts->post_vis_enable = 1; applugin_rewire_posts(posts); // goom wants options thru config ... if(args && !strcmp(name,"goom")) { int val; if((val = get_opt_val(initstr, "fps")) > 0) x_upd_num ("effects.goom.fps", val ); if((val = get_opt_val(initstr, "width")) > 0) x_upd_num ("effects.goom.width", val); if((val = get_opt_val(initstr, "height")) > 0) x_upd_num ("effects.goom.height", val); //if((val = get_opt_val(initstr, "csc_method"))>0) // this->xine->config->update_enum (this->xine->config, "effects.goom.csc_method", val); } } } else { /* video filters */ if(strcmp(name, "audiochannel") && strcmp(name, "volnorm") && strcmp(name, "stretch") && strcmp(name, "upmix_mono") && strcmp(name, "upmix") && vpplugin_enable_post(posts, initstr, &found)) { posts->post_video_enable = 1; vpplugin_rewire_posts(posts); } /* audio filters */ if(!found && applugin_enable_post(posts, initstr, &found)) { posts->post_audio_enable = 1; applugin_rewire_posts(posts); } } if(!found) LOGMSG("Can't load post plugin %s", name); else LOGDBG("Post plugin %s loaded and wired", name); } static void input_event_cb(frontend_t *this_gen, const char *keymap, const char *key) { fe_t *this = (fe_t*)this_gen; if (this->fe.fe_message_cb) { this->fe.fe_message_cb(this->fe.fe_message_h, keymap, key); } } static int fe_xine_play(frontend_t *this_gen) { fe_t *this = (fe_t*)this_gen; if(!this) return 0; fe_post_rewire(this); this->input_plugin = NULL; this->playback_finished = xine_play(this->stream, 0, 0) ? 0 : 1; if(!find_input_plugin(this)) return -1; this->input_plugin->f.xine_input_event = this->fe.fe_message_cb ? input_event_cb : NULL; this->input_plugin->f.fe_control = fe_control; this->input_plugin->f.fe_handle = this_gen; if(this->playback_finished) LOGMSG("Error playing " MRL_ID ":// !"); char str[128]; snprintf(str, sizeof(str), "INFO WINDOW %dx%d", this->width, this->height); this->fe.send_event(&this->fe, str); return !this->playback_finished; } static int fe_xine_stop(frontend_t *this_gen) { fe_t *this = (fe_t*)this_gen; if(!this) return 0; this->input_plugin = NULL; this->playback_finished = 1; if (!this->stream) return 0; xine_stop(this->stream); fe_post_unwire(this); return 1; } static void fe_xine_close(frontend_t *this_gen) { fe_t *this = (fe_t*)this_gen; if(!this) return; if (this && this->xine) { if(this->input_plugin) { this->input_plugin->f.xine_input_event = NULL; this->input_plugin->f.fe_control = NULL; } if (!this->stream) return; fe_xine_stop(this_gen); xine_close(this->stream); if(this->postplugins->pip_stream) xine_close(this->postplugins->pip_stream); } } static void fe_xine_exit(frontend_t *this_gen) { fe_t *this = (fe_t*)this_gen; if (this && this->xine) { if(this->input_plugin || !this->playback_finished) fe_xine_close(this_gen); fe_post_unload(this); if(this->configfile) { xine_config_save (this->xine, this->configfile); free(this->configfile); this->configfile = NULL; } if(this->event_queue) xine_event_dispose_queue(this->event_queue); this->event_queue = NULL; if(this->stream) xine_dispose(this->stream); this->stream = NULL; if (this->postplugins) { if (this->postplugins->pip_stream) xine_dispose(this->postplugins->pip_stream); this->postplugins->pip_stream = NULL; free(this->postplugins->static_post_plugins); } free(this->postplugins); this->postplugins = NULL; if(this->slave_stream) xine_dispose(this->slave_stream); this->slave_stream = NULL; if(this->audio_port) xine_close_audio_driver(this->xine, this->audio_port); this->audio_port = NULL; init_dummy_ports(this, 0); if(this->video_port) xine_close_video_driver(this->xine, this->video_port); this->video_port = NULL; xine_exit(this->xine); this->xine = NULL; } } static void fe_free(frontend_t *this_gen) { if (this_gen) { fe_t *this = (fe_t*)this_gen; this->fe.fe_display_close(this_gen); free(this->configfile); free(this->shutdown_cmd); free(this); } } static int fe_is_finished(frontend_t *this_gen, int slave_stream) { fe_t *this = (fe_t*)this_gen; if (!this) return FE_XINE_ERROR; if (this->terminate_key_pressed) return FE_XINE_EXIT; if (this->playback_finished) return FE_XINE_ERROR; if (slave_stream) { if (!this->slave_stream || this->slave_playback_finished) return FE_XINE_EXIT; } /* check inactivity timer */ if (this->shutdown_timeout > 0) { if (this->shutdown_time < time(NULL)) { shutdown_system(this->shutdown_cmd, 0); return FE_XINE_EXIT; } } return FE_XINE_RUNNING; } /************************** hooks to input plugin ****************************/ /* * data/control from VDR */ static int xine_control(frontend_t *this_gen, const char *cmd) { fe_t *this = (fe_t*)this_gen; if(!find_input_plugin(this)) return -1; return this->input_plugin->f.push_input_control(this->input_plugin, cmd); } static int xine_osd_command(frontend_t *this_gen, struct osd_command_s *cmd) { fe_t *this = (fe_t*)this_gen; if(!find_input_plugin(this)) return -1; return this->input_plugin->f.push_input_osd(this->input_plugin, cmd); } static int xine_queue_pes_packet(frontend_t *this_gen, int stream, uint64_t pos, const char *data, int len) { fe_t *this = (fe_t*)this_gen; if(!find_input_plugin(this)) return 0/*-1*/; return this->input_plugin->f.push_input_write(this->input_plugin, stream, pos, data, len); } /* * control from frontend to xine/vdr */ static int fe_send_input_event(frontend_t *this_gen, const char *map, const char *key, int repeat, int release) { fe_t *this = (fe_t*)this_gen; LOGDBG("Keypress: %s %s %s %s", map, key, repeat?"Repeat":"", release?"Release":""); /* reset inactivity timer */ if (this->shutdown_timeout > 0) this->shutdown_time = time(NULL) + this->shutdown_timeout; /* local mode: --> vdr callback */ if (this->fe.fe_message_cb) { this->fe.fe_message_cb(this->fe.fe_message_h, map, key); return FE_OK; } /* remote mode: --> input plugin --> vdr */ if (find_input_plugin(this)) { if (this->input_plugin->f.post_vdr_event) { char *msg = NULL; if (map) { if (asprintf(&msg, "KEY %s %s %s %s\r\n", map, key, repeat?"Repeat":"", release?"Release":"") < 0) msg = NULL; } else { if (asprintf(&msg, "KEY %s\r\n", key) < 0) msg = NULL; } if (msg) { int r = this->input_plugin->f.post_vdr_event(this->input_plugin, msg); free(msg); if (r > 0) return FE_OK; } LOGMSG("fe_send_input_event: message KEY %s lost", key); return FE_ERROR; } } LOGMSG("fe_send_input_event: handler not set, event lost !"); return FE_ERROR; } static int fe_send_event(frontend_t *this_gen, const char *data) { fe_t *this = (fe_t*)this_gen; if (!data) return FE_ERROR; if (!strcmp(data, "TOGGLE_FULLSCREEN")) { if(this->toggle_fullscreen_cb) this->toggle_fullscreen_cb(this, -1); } else if (!strncasecmp(data, "FULLSCREEN ", 11)) { if(this->toggle_fullscreen_cb) this->toggle_fullscreen_cb(this, atoi(data+11) ? 1: 0); } else if (!strcmp(data, "QUIT")) { this->terminate_key_pressed = 1; } else if(!strcmp(data, "TOGGLE_DEINTERLACE")) { xine_set_param(this->stream, XINE_PARAM_VO_DEINTERLACE, xine_get_param(this->stream, XINE_PARAM_VO_DEINTERLACE) ? 0 : 1); } else if(!strncasecmp(data, "DEINTERLACE ", 12)) { xine_set_param(this->stream, XINE_PARAM_VO_DEINTERLACE, atoi(data+12) ? 1 : 0); } else if (!strcmp(data, "POWER_OFF")) { shutdown_system(this->shutdown_cmd, 1); } else { LOGDBG("Event: %s", data); /* local mode: --> vdr callback */ if (this->fe.fe_message_cb) { this->fe.fe_message_cb(this->fe.fe_message_h, data, NULL); return FE_OK; } /* remote mode: --> input plugin --> vdr */ if (find_input_plugin(this)) { if (this->input_plugin->f.post_vdr_event) { char *msg = NULL; if (asprintf(&msg, "%s\r\n", data) < 1) msg = NULL; if (msg) { int r = this->input_plugin->f.post_vdr_event(this->input_plugin, msg); free(msg); return (r > 0) ? FE_OK : FE_ERROR; } return FE_ERROR; } } } return FE_OK; } /* * Control messages from input plugin */ static void *fe_control(frontend_t *this_gen, const char *cmd) { fe_t *this = (fe_t*)this_gen; post_plugins_t *posts; /*LOGDBG("fe_control(\"%s\")", cmd);*/ if(!cmd || !this) { LOGMSG("fe_control(0x%lx,0x%lx) : invalid argument", (unsigned long int)this_gen, (unsigned long int)cmd); return NULL; } posts = this->postplugins; if(!posts) { LOGMSG("fe_control : this->posts == NULL"); return NULL; } if(!strncmp(cmd, "SLAVE CLOSED", 16)) { /*LOGMSG("fe_control : slave closed");*/ if(this->slave_stream) fe_control(this_gen, "SLAVE 0x0\r\n"); init_dummy_ports(this, 0); this->video_width = this->video_height = 0; } else if(!strncmp(cmd, "SLAVE 0x", 8)) { unsigned long pt; if(1 == sscanf(cmd+8, "%lx", &pt)) { xine_stream_t *slave_stream = (xine_stream_t*)pt; if(this->slave_stream != slave_stream) { fe_post_unwire(this); if(this->slave_stream) { /*xine_post_out_t *vo_source = xine_get_video_source(posts->slave_stream);*/ if(this->slave_stream == this->postplugins->audio_source) { xine_post_out_t *ao_source = xine_get_audio_source(this->slave_stream); LOGMSG("unwiring slave stream from output"); /*(void) xine_post_wire_video_port(vo_source, this->video_port_none);*/ (void) xine_post_wire_audio_port(ao_source, this->audio_port_none); } } this->slave_stream = slave_stream; this->postplugins->video_source = this->postplugins->audio_source = this->slave_stream ?: this->stream; if(strstr(cmd, "Video")) /* video only, audio from VDR */ this->postplugins->audio_source = this->stream; if(strstr(cmd, "Audio")) /* audio only, video from VDR */ this->postplugins->video_source = this->stream; if(this->slave_stream) fe_post_unwire(this); fe_post_rewire(this); } this->slave_playback_finished = !slave_stream; this->video_width = this->video_height = 0; } } else if(!strncmp(cmd, "ENDOFSTREAM", 11)) { if(this->slave_stream) this->slave_playback_finished = 1; } else if(!strncmp(cmd, "SUBSTREAM ", 10)) { unsigned int pid; int x, y, w, h; if(5 == sscanf(cmd+10, "0x%x %d %d %d %d", &pid, &x, &y, &w, &h)) { char mrl[256]; if(!posts->pip_stream) posts->pip_stream = xine_stream_new(this->xine, this->audio_port, this->video_port); LOGMSG(" PIP %d: %dx%d @ (%d,%d)", pid & 0x0f, w, h, x, y); LOGMSG("create pip stream done"); sprintf(mrl, MRL_ID "+slave://0x%lx#nocache", (unsigned long int)this); if(!xine_open(posts->pip_stream, mrl) || !xine_play(posts->pip_stream, 0, 0)) { LOGERR(" pip stream open/play failed"); } else { char params[64]; sprintf(params, "pip_num=1,x=%d,y=%d,w=%d,h=%d", x,y,w,h); fe_post_open(this, "Pip", params); return posts->pip_stream; } } fe_post_close(this, NULL, POST_VIDEO_PIP); if(posts->pip_stream) { xine_close(posts->pip_stream); xine_dispose(posts->pip_stream); posts->pip_stream = NULL; } return NULL; } else if(!strncmp(cmd, "POST ", 5)) { char *name = strdup(cmd+5), *args = name, *pt; if(NULL != (pt=strchr(name, '\r'))) *pt = 0; if(NULL != (pt=strchr(name, '\n'))) *pt = 0; while(*args && *args != ' ') /* skip name */ args++; if(*args /*== ' '*/) *args++ = 0; while(*args && *args == ' ') /* skip whitespace between name and args */ args++; /*this->stream->xine->port_ticket->acquire(this->stream->xine->port_ticket, 0);*/ /* - locks local frontend at startup */ if(!strncmp(args, "On", 2)) { args += 2; while(*args == ' ') args++; /*LOGDBG(" POST: %s On \"%s\"", name, args);*/ fe_post_open(this, name, args); } else if(!strncmp(args, "Off", 3)) { /*LOGDBG(" POST: %s Off (name len=%d), name => int = %d", name, strlen(name), atoi(name));*/ if(strlen(name) == 1) fe_post_close(this, NULL, atoi(name)); else fe_post_close(this, name, -1); } else { LOGMSG("fe_control: POST: unknown command %s", cmd); } /*this->stream->xine->port_ticket->release(this->stream->xine->port_ticket, 0);*/ /* - locks local frontend at startup */ free(name); return NULL; } else if(!strncmp(cmd, "GRAB ", 5)) { int quality, width, height, jpeg, size=0; jpeg = !strncmp(cmd+5,"JPEG",4); if(3 == sscanf(cmd+5+4, "%d %d %d", &quality, &width, &height)) { grab_data_t *result = (grab_data_t*)malloc(sizeof(grab_data_t)); result->data = this->fe.grab((frontend_t*)this, &size, jpeg, quality, width, height); if(result->data && (result->size=size)>0) return result; free(result->data); free(result); } } else if(!strncmp(cmd, "OVERSCAN ", 9)) { int overscan; if(1 == sscanf(cmd+9, "%d", &overscan)) { int crop_x = overscan * this->width / 100 / 2; int crop_y = overscan * this->height / 100 / 2; this->overscan = overscan; xine_set_param(this->stream, XINE_PARAM_VO_CROP_LEFT, crop_x); xine_set_param(this->stream, XINE_PARAM_VO_CROP_TOP, crop_y); xine_set_param(this->stream, XINE_PARAM_VO_CROP_RIGHT, crop_x); xine_set_param(this->stream, XINE_PARAM_VO_CROP_BOTTOM, crop_y); /* trigger forced redraw to make changes effective */ xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_X, 100); } } return NULL; } /* * --- RgbToJpeg ------------------------------------------------------------- * * source: vdr-1.3.42, tools.c * modified to accept YUV data * * - move to xine_input_vdr ? */ #ifdef HAVE_LIBJPEG #define JPEGCOMPRESSMEM 500000 typedef struct tJpegCompressData_s { int size; unsigned char *mem; } tJpegCompressData; static void JpegCompressInitDestination(const j_compress_ptr cinfo) { tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; if (jcd) { cinfo->dest->free_in_buffer = jcd->size = JPEGCOMPRESSMEM; cinfo->dest->next_output_byte = jcd->mem = (unsigned char *)malloc(jcd->size); } } static boolean JpegCompressEmptyOutputBuffer(const j_compress_ptr cinfo) { tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; if (jcd) { int Used = jcd->size; jcd->size += JPEGCOMPRESSMEM; jcd->mem = (unsigned char *)realloc(jcd->mem, jcd->size); if (jcd->mem) { cinfo->dest->next_output_byte = jcd->mem + Used; cinfo->dest->free_in_buffer = jcd->size - Used; return TRUE; } } return FALSE; } static void JpegCompressTermDestination(const j_compress_ptr cinfo) { tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; if (jcd) { int Used = cinfo->dest->next_output_byte - jcd->mem; if (Used < jcd->size) { jcd->size = Used; jcd->mem = (unsigned char *)realloc(jcd->mem, jcd->size); } } } #ifndef HAVE_XINE_GRAB_VIDEO_FRAME static vo_frame_t *yuy2_to_yv12_frame(xine_stream_t *stream, vo_frame_t *frame) { /* convert yuy12 frames to yv12 */ if (frame && frame->format == XINE_IMGFMT_YUY2) { stream->xine->port_ticket->acquire(stream->xine->port_ticket, 0); vo_frame_t *img = stream->video_out->get_frame (stream->video_out, frame->width, frame->height, frame->ratio, XINE_IMGFMT_YV12, VO_BOTH_FIELDS); stream->xine->port_ticket->release(stream->xine->port_ticket, 0); if (!img) { LOGMSG("yuy2_to_yv12_frame: get_frame failed"); frame->free(frame); return NULL; } init_yuv_conversion(); yuy2_to_yv12(frame->base[0], frame->pitches[0], img->base[0], img->pitches[0], img->base[1], img->pitches[1], img->base[2], img->pitches[2], frame->width, frame->height); frame->free(frame); return img; } return frame; } static char *frame_compress_jpeg(fe_t *this, int *size, int quality, vo_frame_t *frame) { struct jpeg_destination_mgr jdm; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; tJpegCompressData jcd; /* convert yuy2 frames to yv12 */ frame = yuy2_to_yv12_frame(this->stream, frame); /* Compress JPEG */ jdm.init_destination = JpegCompressInitDestination; jdm.empty_output_buffer = JpegCompressEmptyOutputBuffer; jdm.term_destination = JpegCompressTermDestination; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); cinfo.dest = &jdm; cinfo.client_data = &jcd; cinfo.image_width = frame->width; cinfo.image_height = frame->height; cinfo.input_components = 3; cinfo.in_color_space = JCS_YCbCr; switch (frame->format) { case XINE_IMGFMT_YV12: { JSAMPARRAY pp[3]; JSAMPROW *rpY = (JSAMPROW*)malloc(sizeof(JSAMPROW) * frame->height); JSAMPROW *rpU = (JSAMPROW*)malloc(sizeof(JSAMPROW) * frame->height); JSAMPROW *rpV = (JSAMPROW*)malloc(sizeof(JSAMPROW) * frame->height); int k; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); cinfo.raw_data_in = TRUE; #if JPEG_LIB_VERSION >= 70 cinfo.do_fancy_downsampling = FALSE; #endif jpeg_set_colorspace(&cinfo, JCS_YCbCr); cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = cinfo.comp_info[1].v_samp_factor = cinfo.comp_info[2].h_samp_factor = cinfo.comp_info[2].v_samp_factor = 1; jpeg_start_compress(&cinfo, TRUE); for (k = 0; k < frame->height; k+=2) { rpY[k] = frame->base[0] + k*frame->pitches[0]; rpY[k+1] = frame->base[0] + (k+1)*frame->pitches[0]; rpU[k/2] = frame->base[1] + (k/2)*frame->pitches[1]; rpV[k/2] = frame->base[2] + (k/2)*frame->pitches[2]; } for (k = 0; k < frame->height; k+=2*DCTSIZE) { pp[0] = &rpY[k]; pp[1] = &rpU[k/2]; pp[2] = &rpV[k/2]; jpeg_write_raw_data(&cinfo, pp, 2*DCTSIZE); } free(rpY); free(rpU); free(rpV); break; } #if 0 case XINE_IMGFMT_RGB: { JSAMPROW rp[height]; int rs, k; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); rs = frame->pitches[0]; for (k = 0; k < height; k++) rp[k] = frame->base[0] + k*rs; jpeg_write_scanlines(&cinfo, rp, height); break; } #endif default: LOGMSG("fe_grab: grabbing failed (unsupported image format %d)", frame->format); break; } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); frame->free(frame); *size = jcd.size; return (char*) jcd.mem; } #endif /* HAVE_XINE_GRAB_VIDEO_FRAME */ #endif /* HAVE_LIBJPEG */ #ifndef HAVE_XINE_GRAB_VIDEO_FRAME static vo_frame_t *yv12_to_yuy2_frame(xine_stream_t *stream, vo_frame_t *frame) { /* convert yv12 frames to yuy2 */ if (frame && frame->format == XINE_IMGFMT_YV12) { stream->xine->port_ticket->acquire(stream->xine->port_ticket, 0); vo_frame_t *img = stream->video_out->get_frame (stream->video_out, frame->width, frame->height, frame->ratio, XINE_IMGFMT_YUY2, VO_BOTH_FIELDS); stream->xine->port_ticket->release(stream->xine->port_ticket, 0); if (!img) { LOGMSG("yv12_to_yuy2_frame: get_frame failed"); frame->free(frame); return NULL; } init_yuv_conversion(); yv12_to_yuy2(frame->base[0], frame->pitches[0], frame->base[1], frame->pitches[1], frame->base[2], frame->pitches[2], img->base[0], img->pitches[0], frame->width, frame->height, frame->progressive_frame); frame->free(frame); return img; } return frame; } #define YCBCR_TO_RGB( y, cb, cr, r, g, b ) \ do { \ int _y, _cb, _cr, _r, _g, _b; \ _y = ((y) - 16) * 76309; \ _cb = (cb) - 128; \ _cr = (cr) - 128; \ _r = (_y + _cr * 104597 + 0x8000) >> 16; \ _g = (_y - _cb * 25675 - _cr * 53279 + 0x8000) >> 16; \ _b = (_y + _cb * 132201 + 0x8000) >> 16; \ (r) = (_r < 0) ? 0 : ((_r > 255) ? 255 : _r); \ (g) = (_g < 0) ? 0 : ((_g > 255) ? 255 : _g); \ (b) = (_b < 0) ? 0 : ((_b > 255) ? 255 : _b); \ } while (0) static char *frame_compress_pnm(fe_t *this, int *size, vo_frame_t *frame) { /* ensure yuy2 */ if (!(frame = yv12_to_yuy2_frame(this->stream, frame))) return NULL; /* convert to PNM */ /* allocate memory for result */ size_t bytes = frame->width * frame->height * 3; uint8_t *pnm = malloc(bytes + 64); if (!pnm) { LOGMSG("fe_grab: malloc failed"); return NULL; } /* PNM header */ sprintf((char*)pnm, "P6\n%d\n%d\n255\n", frame->width, frame->height); int hdrlen = strlen((char*)pnm); uint8_t *p = pnm + hdrlen; uint8_t *s = frame->base[0]; int x, y; *size = bytes + hdrlen; /* convert to RGB */ for (y=0; y < frame->height; y++) { for (x=0; x < frame->width; x+=2, s+=4, p+=6) { YCBCR_TO_RGB(s[0], s[1], s[3], p[0], p[1], p[2]); YCBCR_TO_RGB(s[2], s[1], s[3], p[3], p[4], p[5]); } s = frame->base[0] + y*frame->pitches[0]; } return (char*)pnm; } #endif /* HAVE_XINE_GRAB_VIDEO_FRAME */ #ifdef HAVE_XINE_GRAB_VIDEO_FRAME static char *fe_compress_grab_frame(fe_t *this, int *size, int jpeg, int quality, int width, int height, xine_grab_video_frame_t *frame) { #ifdef HAVE_LIBJPEG if (jpeg) { /* Compress JPEG */ struct jpeg_destination_mgr jdm; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; tJpegCompressData jcd; jdm.init_destination = JpegCompressInitDestination; jdm.empty_output_buffer = JpegCompressEmptyOutputBuffer; jdm.term_destination = JpegCompressTermDestination; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); cinfo.dest = &jdm; cinfo.client_data = &jcd; cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); JSAMPROW rp[height]; int rs = width * 3; int k; for (k = 0; k < height; k++) rp[k] = frame->img + k * rs; jpeg_write_scanlines(&cinfo, rp, height); jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); *size = jcd.size; return (char*) jcd.mem; } #endif /* convert to PNM */ /* allocate memory for result */ size_t bytes = width * height * 3; uint8_t *pnm = malloc(bytes + 64); if (!pnm) { LOGMSG("fe_grab: malloc failed"); return NULL; } /* PNM header */ sprintf((char*)pnm, "P6\n%d\n%d\n255\n", width, height); int hdrlen = strlen((char*)pnm); /* copy image */ xine_fast_memcpy(pnm + hdrlen, frame->img, bytes); *size = bytes + hdrlen; return (char*)pnm; } #endif /* HAVE_XINE_GRAB_VIDEO_FRAME */ static char *fe_grab(frontend_t *this_gen, int *size, int jpeg, int quality, int width, int height) { fe_t *this = (fe_t*)this_gen; #ifndef HAVE_LIBJPEG if(jpeg) { LOGMSG("fe_grab: JPEG grab support was not compiled in"); return 0; } #endif /* HAVE_LIBJPEG */ if(!find_input_plugin(this)) return 0; LOGDBG("fe_grab: grabbing %s %d %dx%d", jpeg ? "JPEG" : "PNM", quality, width, height); /* validate parameters */ if ((quality < 0) || (quality > 100)) quality = 100; width = (MAX(16, MIN(width, 1920)) + 1) & ~1; /* 16...1920, even */ height = (MAX(16, MIN(height, 1200)) + 1) & ~1; /* 16...1200, even */ /* get last frame */ this->stream->xine->port_ticket->acquire(this->stream->xine->port_ticket, 0); #ifdef HAVE_XINE_GRAB_VIDEO_FRAME char *img = NULL; xine_grab_video_frame_t *grab_frame = xine_new_grab_video_frame(this->stream); if (grab_frame) { grab_frame->width = width; grab_frame->height = height; if (!grab_frame->grab(grab_frame)) img = fe_compress_grab_frame(this, size, jpeg, quality, width, height, grab_frame); grab_frame->dispose(grab_frame); } this->stream->xine->port_ticket->release(this->stream->xine->port_ticket, 0); return img; #else vo_frame_t *frame = this->stream->video_out->get_last_frame (this->stream->video_out); #if XINE_VERSION_CODE < 10190 if(frame) frame->lock(frame); #endif this->stream->xine->port_ticket->release(this->stream->xine->port_ticket, 0); if(!frame) { LOGMSG("fe_grab: get_last_frame() failed"); return NULL; } /* Scale image */ if (frame->width != width || frame->height != height) { /* #warning TODO - scaling here */ LOGMSG("fe_grab: scaling not implemented"); } if (!jpeg) return frame_compress_pnm(this, size, frame); #ifdef HAVE_LIBJPEG return frame_compress_jpeg(this, size, quality, frame); #else /* HAVE_LIBJPEG */ return NULL; #endif #endif /* HAVE_XINE_GRAB_VIDEO_FRAME */ } /* * init_fe() * * initialize function pointers */ void init_fe(fe_t *fe) { fe->fe.xine_init = fe_xine_init; fe->fe.xine_open = fe_xine_open; fe->fe.xine_play = fe_xine_play; fe->fe.xine_stop = fe_xine_stop; fe->fe.xine_close = fe_xine_close; fe->fe.xine_exit = fe_xine_exit; fe->fe.shutdown_init = fe_shutdown_init; fe->fe.fe_free = fe_free; fe->fe.xine_is_finished = fe_is_finished; fe->fe.xine_osd_command = xine_osd_command; fe->fe.xine_control = xine_control; fe->fe.xine_queue_pes_packet = xine_queue_pes_packet; fe->fe.grab = fe_grab; fe->fe.send_event = fe_send_event; fe->fe.send_input_event = fe_send_input_event; fe->dest_pixel_aspect = fe_dest_pixel_aspect; fe->frame_output_handler = fe_frame_output_cb; } xineliboutput-2.0.0/xine_fbfe_frontend.c0000644000175000017500000002002613061253352016214 0ustar phph/* * xine_fbfe_frontend.c: Simple front-end, framebuffer functions * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include #include #if defined(__linux__) # include #endif #define LOG_MODULENAME "[vdr-fbfe] " #include "logdefs.h" #include "xine_frontend_internal.h" /* * data */ typedef struct fbfe_s { /* function pointers / base class */ union { frontend_t fe; /* generic frontend */ fe_t x; /* xine frontend */ }; /* stored original handlers */ int (*fe_xine_init)(frontend_t *this_gen, const char *audio_driver, const char *audio_port, const char *video_driver, int pes_buffers, const char *static_post_plugins, const char *config_file); /* display */ /*char *modeline;*/ int fd_tty; /* frontend */ uint8_t fullscreen : 1; /*uint8_t vmode_switch : 1;*/ } fbfe_t; static void fbfe_update_display_size(fe_t *this_gen) { fbfe_t *this = (fbfe_t*)this_gen; if(this->fullscreen && this->x.video_port) { this->x.width = this->x.video_port->get_property(this->x.video_port, VO_PROP_WINDOW_WIDTH); this->x.height = this->x.video_port->get_property(this->x.video_port, VO_PROP_WINDOW_HEIGHT); LOGDBG("Framebuffer size after initialization: %dx%d", this->x.width, this->x.height); } } /* * update_DFBARGS * * (optionally) add fbdev option to DFBARGS environment variable */ static void update_DFBARGS(const char *fb_dev) { const char *env_old = getenv("DFBARGS"); char *env_new = NULL; if (env_old) { char *env_tmp = strdup(env_old); char *head = strstr(env_tmp, "fbdev="); if (head) { char *tail = strchr(head, ','); if(head == env_tmp) head = NULL; else *head = 0; if(asprintf(&env_new, "%sfbdev=%s%s", head ? env_tmp : "", fb_dev, tail ? tail : "") < 0) { free(env_tmp); return; } } else { if(asprintf(&env_new, "fbdev=%s%s%s", fb_dev, env_tmp ? "," : "", env_tmp ?: "") < 0) { free(env_tmp); return; } } free(env_tmp); LOGMSG("replacing environment variable DFBARGS with %s (original was %s)", env_new, env_old); } else { if(asprintf(&env_new, "fbdev=%s", fb_dev) < 0) return; LOGMSG("setting environment variable DFBARGS to %s", env_new); } setenv("DFBARGS", env_new, 1); free(env_new); } /* * fbfe_display_open */ static int fbfe_display_open(frontend_t *this_gen, int xpos, int ypos, int width, int height, int fullscreen, int hud, int opengl, int modeswitch, const char *modeline, int aspect, int no_x_kbd, int gui_hotkeys, int touchscreen, const char *video_port, int scale_video, const char *aspect_controller, int window_id) { fbfe_t *this = (fbfe_t*)this_gen; if(!this) return 0; if(this->fd_tty >= 0) this->fe.fe_display_close(this_gen); if(this->fe.fe_message_cb) { this->fe.fe_message_cb(this->fe.fe_message_h, "KBD", ""); } LOGDBG("fbfe_display_open(width=%d, height=%d, fullscreen=%d, display=%s)", width, height, fullscreen, video_port); this->x.xpos = xpos; this->x.ypos = ypos; this->x.width = width; this->x.height = height; this->x.aspect = aspect; /*this->x.cropping = 0;*/ this->x.scale_video = scale_video; this->x.overscan = 0; this->x.display_ratio = 1.0; this->x.aspect_controller = aspect_controller ? strdup(aspect_controller) : NULL; this->fullscreen = fullscreen; /*this->vmode_switch = modeswitch;*/ /*this->modeline = strdup(modeline ?: "");*/ /* setup xine FB visual */ this->x.xine_visual_type = XINE_VISUAL_TYPE_FB; this->x.vis_fb.frame_output_cb = this->x.frame_output_handler; this->x.vis_fb.user_data = this; /* select framebuffer device ? */ if(video_port && !strncmp(video_port, "/dev/", 5)) this->x.video_port_name = strdup(video_port); else this->x.video_port_name = NULL; /* set console to graphics mode */ #if defined(KDSETMODE) && defined(KD_GRAPHICS) if (isatty(STDIN_FILENO)) this->fd_tty = dup(STDIN_FILENO); else this->fd_tty = open("/dev/tty", O_RDWR); if(this->fd_tty < 0) LOGERR("fbfe_display_open: error opening /dev/tty"); else if (ioctl(this->fd_tty, KDSETMODE, KD_GRAPHICS) == -1) LOGERR("fbfe_display_open: failed to set /dev/tty to graphics mode"); #else # warning No support for console graphics mode this->fd_tty = -1; #endif return 1; } /* * fbfe_display_config * * configure windows */ static int fbfe_display_config(frontend_t *this_gen, int xpos, int ypos, int width, int height, int fullscreen, int modeswitch, const char *modeline, int aspect, int scale_video) { fbfe_t *this = (fbfe_t*)this_gen; if(!this) return 0; this->x.xpos = xpos >= 0 ? xpos : this->x.xpos; this->x.ypos = ypos >= 0 ? ypos : this->x.ypos; this->x.width = width >= 0 ? width : this->x.width; this->x.height = height >= 0 ? height : this->x.height; this->x.aspect = aspect; this->x.scale_video = scale_video; this->fullscreen = fullscreen; /*this->vmode_switch = modeswitch;*/ #if 0 if(!modeswitch && strcmp(modeline, this->modeline)) { free(this->modeline); this->modeline = strdup(modeline ?: ""); /* #warning XXX TODO - switch vmode */ } #endif return 1; } static void fbfe_interrupt(frontend_t *this_gen) { /* stop fbfe_run() */ } static int fbfe_run(frontend_t *this_gen) { fbfe_t *this = (fbfe_t*)this_gen; if (!this || this->fe.xine_is_finished(this_gen, 0) != FE_XINE_RUNNING) return 0; /* just sleep 500ms */ select(0, NULL, NULL, NULL, &(struct timeval){ .tv_sec = 0, .tv_usec = 500*1000 }); return 1; } static void fbfe_display_close(frontend_t *this_gen) { fbfe_t *this = (fbfe_t*)this_gen; if (!this) return; if (this->x.xine) this->fe.xine_exit(this_gen); if (this->fd_tty >= 0) { #if defined(KDSETMODE) && defined(KD_TEXT) if(ioctl(this->fd_tty, KDSETMODE, KD_TEXT) == -1) LOGERR("fbfe_display_close: failed to set /dev/tty to text mode"); #endif close(this->fd_tty); this->fd_tty = -1; } free(this->x.video_port_name); this->x.video_port_name = NULL; free(this->x.aspect_controller); this->x.aspect_controller = NULL; #if 0 free(this->modeline); this->modeline = NULL; #endif } static int fbfe_xine_init(frontend_t *this_gen, const char *audio_driver, const char *audio_port, const char *video_driver, int pes_buffers, const char *static_post_plugins, const char *config_file) { fbfe_t *this = (fbfe_t*)this_gen; if (video_driver && !strcmp(video_driver, "DirectFB")) update_DFBARGS(this->x.video_port_name); return this->fe_xine_init(this_gen, audio_driver, audio_port, video_driver, pes_buffers, static_post_plugins, config_file); } static frontend_t *fbfe_get_frontend(void) { fbfe_t *this = calloc(1, sizeof(fbfe_t)); this->fd_tty = -1; init_fe((fe_t*)this); this->fe.fe_display_open = fbfe_display_open; this->fe.fe_display_config = fbfe_display_config; this->fe.fe_display_close = fbfe_display_close; this->fe.fe_run = fbfe_run; this->fe.fe_interrupt = fbfe_interrupt; this->x.update_display_size_cb = fbfe_update_display_size; /* override */ this->fe_xine_init = this->fe.xine_init; this->fe.xine_init = fbfe_xine_init; return (frontend_t*)this; } /* ENTRY POINT */ const fe_creator_f fe_creator __attribute__((visibility("default"))) = fbfe_get_frontend; xineliboutput-2.0.0/xine/0000755000175000017500000000000013061253352013167 5ustar phphxineliboutput-2.0.0/xine/xvdr_metronom.h0000644000175000017500000000401013061253352016236 0ustar phph/* * xvdr_metronom.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XVDR_METRONOM_H #define XVDR_METRONOM_H #include #ifdef METRONOM_INTERNAL # error METRONOM_INTERNAL defined, struct xvdr_metronom_s size will be incorrect #endif #define XVDR_METRONOM_OPTION_BASE 0x1001 #define XVDR_METRONOM_LAST_VO_PTS (XVDR_METRONOM_OPTION_BASE) #define XVDR_METRONOM_TRICK_SPEED (XVDR_METRONOM_OPTION_BASE + 1) #define XVDR_METRONOM_STILL_MODE (XVDR_METRONOM_OPTION_BASE + 2) #define XVDR_METRONOM_ID (XVDR_METRONOM_OPTION_BASE + 3) #define XVDR_METRONOM_LIVE_BUFFERING (XVDR_METRONOM_OPTION_BASE + 4) #define XVDR_METRONOM_STREAM_START (XVDR_METRONOM_OPTION_BASE + 5) typedef struct xvdr_metronom_s xvdr_metronom_t; struct adjustable_scr_s; struct xvdr_metronom_s { /* xine-lib metronom interface */ metronom_t metronom; /* management interface */ void (*dispose) (xvdr_metronom_t *); void (*wire) (xvdr_metronom_t *); void (*unwire) (xvdr_metronom_t *); /* master SCR for buffering control */ struct adjustable_scr_s *scr; /* private data */ #ifdef XVDR_METRONOM_COMPILE /* original metronom */ metronom_t *orig_metronom; xine_stream_t *stream; int trickspeed; /* current trick speed */ int still_mode; int64_t last_vo_pts; /* last displayed video frame PTS */ int wired; /* true if currently wired to stream */ /* initial buffering in live mode */ uint8_t buffering; /* buffering active */ uint8_t live_buffering; /* live buffering enabled */ uint8_t stream_start; int64_t vid_pts; /* last seen video pts */ int64_t aud_pts; /* last seen audio pts */ int64_t disc_pts; /* reported discontinuity pts */ uint64_t buffering_start_time; uint64_t first_frame_seen_time; pthread_mutex_t mutex; #endif }; xvdr_metronom_t *xvdr_metronom_init(xine_stream_t *); #endif /* XVDR_METRONOM_H */ xineliboutput-2.0.0/xine/xvdr_metronom.c0000644000175000017500000002527013061253352016244 0ustar phph/* * xvdr_metronom.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include #include "../tools/time_ms.h" #define LOG_MODULENAME "[metronom ] " #define SysLogLevel iSysLogLevel #include "../logdefs.h" #include "adjustable_scr.h" #define XVDR_METRONOM_COMPILE #include "xvdr_metronom.h" static int warnings = 0; static int64_t absdiff(int64_t a, int64_t b) { int64_t diff = a-b; if (diff<0) diff = -diff; return diff; } static int64_t min64(int64_t a, int64_t b) { return a < b ? a : b; } static void check_buffering_done(xvdr_metronom_t *this) { /* both audio and video timestamps seen ? */ if (this->vid_pts && this->aud_pts) { int64_t da = this->aud_pts - this->disc_pts; int64_t dv = this->vid_pts - this->disc_pts; int64_t d_min = min64(da, dv); LOGMSG(" stream A-V diff %d ms", (int)(this->vid_pts - this->aud_pts)/90); LOGMSG(" reported stream start at pts %"PRId64, this->disc_pts); LOGMSG(" output fifo end at: audio %"PRId64" video %"PRId64, this->aud_pts, this->vid_pts); LOGMSG(" dA %"PRId64" dV %"PRId64, da, dv); if (d_min < 0 && d_min > -10*90000) { LOGMSG(" *** output is late %"PRId64" ticks (%"PRId64" ms) ***", d_min, -d_min/90); this->scr->jump(this->scr, d_min); } this->buffering = 0; this->stream_start = 0; this->scr->set_buffering(this->scr, 0); return; } if (this->first_frame_seen_time) { int64_t ms_since_first_frame = elapsed(this->first_frame_seen_time); if (ms_since_first_frame > 1000) { this->stream_start = 0; /* abort buffering if no audio */ if (this->vid_pts && !this->aud_pts) { LOGMSG("buffering stopped: NO AUDIO ? elapsed time %d ms", (int)ms_since_first_frame); this->buffering = 0; this->scr->set_buffering(this->scr, 0); return; } /* abort buffering if no video */ if (!this->vid_pts && this->aud_pts) { LOGMSG("buffering stopped: NO VIDEO ? elapsed time %d ms", (int)ms_since_first_frame); this->buffering = 0; this->scr->set_buffering(this->scr, 0); return; } } } } static void got_video_frame(metronom_t *metronom, vo_frame_t *frame) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; int64_t pts = frame->pts; #if 1 /* xine-lib master-slave metronom causes some problems ... */ if (metronom->got_video_frame != got_video_frame) { if (!warnings++) LOGMSG("got_video_frame: invalid object"); return; } warnings = 0; #endif if (this->still_mode) { LOGVERBOSE("Still frame, type %d", frame->picture_coding_type); frame->pts = 0; } if (this->trickspeed) { frame->pts = 0; frame->duration *= 12; /* GOP */ } /* initial buffering */ pthread_mutex_lock(&this->mutex); if (this->buffering && !frame->bad_frame) { /* track video pts */ if (pts) { if (this->vid_pts && (absdiff(this->vid_pts, pts) > 5*90000)) { LOGMSG("buffering: video jump resetted audio pts"); this->aud_pts = 0; } if (this->vid_pts && this->aud_pts && (absdiff(this->vid_pts, this->aud_pts) > 5*90000)) { LOGMSG("buffering: A-V diff resetted audio pts"); this->aud_pts = 0; } if (!this->vid_pts) { LOGMSG("got video pts, frame type %d (@%d ms)", frame->picture_coding_type, (int)elapsed(this->buffering_start_time)); this->first_frame_seen_time = time_ms(); } this->vid_pts = pts; } /* some logging */ if (!pts) { LOGMSG("got video, pts 0, buffering, frame type %d, bad_frame %d", frame->picture_coding_type, frame->bad_frame); } if (pts && !frame->pts) { LOGMSG("*** ERROR: hiding video pts while buffering ***"); } check_buffering_done(this); } pthread_mutex_unlock(&this->mutex); if (this->orig_metronom) this->orig_metronom->got_video_frame (this->orig_metronom, frame); frame->pts = pts; } static int64_t got_audio_samples(metronom_t *metronom, int64_t pts, int nsamples) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; pthread_mutex_lock(&this->mutex); /* initial buffering */ if (this->buffering) { /* track audio pts */ if (pts) { if (this->aud_pts && (this->aud_pts > pts || absdiff(pts, this->aud_pts) > 5*90000)) { LOGMSG("audio jump resetted video pts"); this->vid_pts = 0; } if (this->aud_pts && this->vid_pts && (absdiff(this->vid_pts, this->aud_pts) > 5*90000)) { LOGMSG("buffering: A-V diff resetted video pts"); this->vid_pts = 0; } if (!this->aud_pts) { LOGMSG("got audio pts (@%d ms)", (int)elapsed(this->buffering_start_time)); this->first_frame_seen_time = time_ms(); } this->aud_pts = pts; } /* some logging */ if (!pts && !this->aud_pts) { LOGMSG("got audio, pts 0, buffering"); } check_buffering_done(this); } pthread_mutex_unlock(&this->mutex); return this->orig_metronom->got_audio_samples (this->orig_metronom, pts, nsamples); } /* * dummy hooks */ static int64_t got_spu_packet(metronom_t *metronom, int64_t pts) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; return this->orig_metronom->got_spu_packet(this->orig_metronom, pts); } static void start_buffering(xvdr_metronom_t *this, int64_t disc_off) { if (this->live_buffering && this->stream_start && disc_off) { if (!this->buffering) { LOGMSG("live mode buffering started (@%d ms)", (int)elapsed(this->buffering_start_time)); this->aud_pts = 0; this->vid_pts = 0; this->disc_pts = disc_off; this->first_frame_seen_time = 0; this->buffering = 1; this->scr->set_buffering(this->scr, 1); } } else { if (this->buffering) { LOGMSG("live mode buffering aborted (@%d ms)", (int)elapsed(this->buffering_start_time)); this->buffering = 0; this->scr->set_buffering(this->scr, 0); } } } static void handle_audio_discontinuity(metronom_t *metronom, int type, int64_t disc_off) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; start_buffering(this, disc_off); this->orig_metronom->handle_audio_discontinuity(this->orig_metronom, type, disc_off); } static void handle_video_discontinuity(metronom_t *metronom, int type, int64_t disc_off) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; start_buffering(this, disc_off); this->orig_metronom->handle_video_discontinuity(this->orig_metronom, type, disc_off); } static void set_audio_rate(metronom_t *metronom, int64_t pts_per_smpls) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; this->orig_metronom->set_audio_rate(this->orig_metronom, pts_per_smpls); } static void set_option(metronom_t *metronom, int option, int64_t value) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; if (option == XVDR_METRONOM_LAST_VO_PTS) { if (value > 0) { pthread_mutex_lock(&this->mutex); this->last_vo_pts = value; pthread_mutex_unlock(&this->mutex); } return; } if (option == XVDR_METRONOM_LIVE_BUFFERING) { pthread_mutex_lock(&this->mutex); this->live_buffering = value; pthread_mutex_unlock(&this->mutex); return; } if (option == XVDR_METRONOM_STREAM_START) { pthread_mutex_lock(&this->mutex); this->stream_start = 1; this->buffering_start_time = time_ms(); pthread_mutex_unlock(&this->mutex); return; } if (option == XVDR_METRONOM_TRICK_SPEED) { this->trickspeed = value; return; } if (option == XVDR_METRONOM_STILL_MODE) { this->still_mode = value; return; } this->orig_metronom->set_option(this->orig_metronom, option, value); } static int64_t get_option(metronom_t *metronom, int option) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; if (option == XVDR_METRONOM_LAST_VO_PTS) { int64_t pts; pthread_mutex_lock(&this->mutex); pts = this->last_vo_pts; pthread_mutex_unlock(&this->mutex); return pts; } if (option == XVDR_METRONOM_TRICK_SPEED) { return this->trickspeed; } if (option == XVDR_METRONOM_STILL_MODE) { return this->still_mode; } if (option == XVDR_METRONOM_ID) { return XVDR_METRONOM_ID; } return this->orig_metronom->get_option(this->orig_metronom, option); } static void set_master(metronom_t *metronom, metronom_t *master) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; this->orig_metronom->set_master(this->orig_metronom, master); } static void metronom_exit(metronom_t *metronom) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; LOGMSG("xvdr_metronom: metronom_exit() called !"); /* un-hook */ this->unwire(this); this->stream = NULL; if (this->orig_metronom) { metronom_t *orig_metronom = this->orig_metronom; this->orig_metronom = NULL; orig_metronom->exit(orig_metronom); } } /* * xvdr_metronom_t */ static void xvdr_metronom_wire(xvdr_metronom_t *this) { if (!this->stream) { LOGMSG("xvdr_metronom_wire(): stream == NULL !"); return; } if (!this->stream->metronom) { LOGMSG("xvdr_metronom_wire(): stream->metronom == NULL !"); return; } if (!this->wired) { this->wired = 1; /* attach to stream */ this->orig_metronom = this->stream->metronom; this->stream->metronom = &this->metronom; } } static void xvdr_metronom_unwire(xvdr_metronom_t *this) { if (this->stream && this->wired) { this->wired = 0; /* detach from stream */ this->stream->metronom = this->orig_metronom; } } static void xvdr_metronom_dispose(xvdr_metronom_t *this) { xvdr_metronom_unwire(this); pthread_mutex_destroy(&this->mutex); free(this); } /* * init */ xvdr_metronom_t *xvdr_metronom_init(xine_stream_t *stream) { if (stream->metronom->get_option(stream->metronom, XVDR_METRONOM_ID) == XVDR_METRONOM_ID) { LOGMSG("xvdr_metronom_init(): stream already hooked !"); return (xvdr_metronom_t*)stream->metronom; } xvdr_metronom_t *this = calloc(1, sizeof(xvdr_metronom_t)); this->stream = stream; this->orig_metronom = stream->metronom; this->wire = xvdr_metronom_wire; this->unwire = xvdr_metronom_unwire; this->dispose = xvdr_metronom_dispose; this->metronom.set_audio_rate = set_audio_rate; this->metronom.got_video_frame = got_video_frame; this->metronom.got_audio_samples = got_audio_samples; this->metronom.got_spu_packet = got_spu_packet; this->metronom.handle_audio_discontinuity = handle_audio_discontinuity; this->metronom.handle_video_discontinuity = handle_video_discontinuity; this->metronom.set_option = set_option; this->metronom.get_option = get_option; this->metronom.set_master = set_master; this->metronom.exit = metronom_exit; pthread_mutex_init(&this->mutex, NULL); xvdr_metronom_wire(this); return this; } xineliboutput-2.0.0/xine/vo_props.h0000644000175000017500000000262513061253352015214 0ustar phph/* * vo_props.h: Extended video-out capabilities and properties * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_VO_PROPS_H_ #define XINELIBOUTPUT_VO_PROPS_H_ /* * Extended vo capabilities */ /* output can scale OSD */ #ifdef VO_CAP_CUSTOM_EXTENT_OVERLAY /* xine-lib 1.2 */ # define VO_XCAP_OSDSCALING VO_CAP_CUSTOM_EXTENT_OVERLAY #else # define VO_XCAP_OSDSCALING 0x01000000 #endif /* Output can blend ARGB surfaces */ #ifdef VO_CAP_ARGB_LAYER_OVERLAY # define VO_XCAP_ARGB_LAYER_OVERLAY VO_CAP_ARGB_LAYER_OVERLAY #else # define VO_XCAP_ARGB_LAYER_OVERLAY 0x02000000 #endif #ifdef VO_CAP_VIDEO_WINDOW_OVERLAY # define VO_XCAP_VIDEO_WINDOW_OVERLAY VO_CAP_VIDEO_WINDOW_OVERLAY #else # define VO_XCAP_VIDEO_WINDOW_OVERLAY 0x04000000 #endif /* * Extended vo properties */ /* enable/disable OSD scaling */ #define VO_PROP_OSD_SCALING 0x1001 /* * VDR OSD tagging and extra data */ /* VDR OSD , stored in overlay hili_rgb_clut member */ #define VDR_OSD_MAGIC -9999 /* VDR OSD extra data, stored in overlay hili clut data */ typedef struct { /* extent of reference coordinate system */ uint16_t extent_width; uint16_t extent_height; /* overlay layer */ uint32_t layer; /* scaling: 0 - disable , 1...N - quality */ uint8_t scaling; } vdr_osd_extradata_t; #endif /* XINELIBOUTPUT_VO_PROPS_H_ */ xineliboutput-2.0.0/xine/vo_post.h0000644000175000017500000000110013061253352015021 0ustar phph/* * vo_post.h: Intercept video driver * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_VO_POST_H #define _XINELIBOUTPUT_VO_POST_H #include /* * synchronous video post plugins * public API */ /* Wire / unwire hook chain to video port */ int wire_video_driver(xine_video_port_t *video_port, vo_driver_t *hook); int unwire_video_driver(xine_video_port_t *video_port, vo_driver_t *hook, vo_driver_t *video_out); #endif /* _XINELIBOUTPUT_VO_POST_H */ xineliboutput-2.0.0/xine/vo_osdscaler.h0000644000175000017500000000052213061253352016022 0ustar phph/* * vo_osdscaler.h: OSD scaling video-out post plugin * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_VO_OSDSCALER_H #define _XINELIBOUTPUT_VO_OSDSCALER_H vo_driver_t *osdscaler_init(void); #endif /* _XINELIBOUTPUT_VO_OSDSCALER_H */ xineliboutput-2.0.0/xine/vo_osdscaler.c0000644000175000017500000002756613061253352016036 0ustar phph/* * vo_osdscaler.c: OSD scaling video-out post plugin * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include #ifdef SWBLEND # include #endif #define LOG_MODULENAME "[osdscaler] " #include "../logdefs.h" #include "vo_props.h" #include "vo_hook.h" /*#define LOGOSD(x...) LOGMSG(x)*/ #define LOGOSD(x...) typedef rle_elem_t osd_rle_elem_t; #include "../tools/rle.h" #include "vo_osdscaler.h" /* Make sure our properties won't overlap with xine's properties */ #if VO_NUM_PROPERTIES > VO_PROP_OSD_SCALING # error VO_NUM_PROPERTIES > VO_PROP_OSD_SCALING #endif #undef ABS #define ABS(x) ((x)<0?-(x):(x)) /* * osd_data_t * * - cache scaled OSD data */ typedef struct osd_data_s osd_data_t; struct osd_data_s { /* original source */ vo_overlay_t *source; /* scaled overlay */ uint8_t scaled : 1; vo_overlay_t ovl; /* for what output resolution overlay was scaled */ uint16_t output_width; uint16_t output_height; /* linked list */ osd_data_t *next; }; /* * osd_data_dispose() * * - free() osd_data_t and all allocated memory */ static void osd_data_dispose(osd_data_t *data) { if (data->scaled) free(data->ovl.rle); free(data); } /* * osd_data_clear() * * - free() whole linked list */ static void osd_data_clear(osd_data_t *data) { if (data) { if (data->next) osd_data_clear(data->next); osd_data_dispose(data); } } /* * osd_data_remove() * * - remove (and free) specific osd_data_t item from linked list */ static void osd_data_remove(osd_data_t **list, osd_data_t *data) { if (!list || !*list) return; /* special handling for list head */ if (data == *list) { *list = data->next; free(data); return; } osd_data_t *it = *list; while (it) { if (data == it->next) { it->next = data->next; free(data); return; } it = it->next; } } /* * osd_data_init() * * - allocate and fill new osd_data_t * */ static osd_data_t *osd_data_init(vo_overlay_t *ovl, osd_data_t *next, uint32_t factor_x, uint32_t factor_y) { osd_data_t *data = calloc(1, sizeof(osd_data_t)); data->source = ovl; data->next = next; memcpy(&data->ovl, ovl, sizeof(vo_overlay_t)); int num_rle = data->ovl.num_rle; /* new position and size */ int x2 = ovl->x + ovl->width + 1; int y2 = ovl->y + ovl->height + 1; x2 = ((x2+1) * factor_x) >> 16; y2 = ((y2+1) * factor_y) >> 16; data->ovl.x = (ovl->x * factor_x) >> 16; data->ovl.y = (ovl->y * factor_y) >> 16; data->ovl.width = x2 - data->ovl.x - 1; data->ovl.height = y2 - data->ovl.y - 1; #ifdef VO_CAP_VIDEO_WINDOW_OVERLAY if (ovl->video_window_x >= 0 && ovl->video_window_y >= 0 && ovl->video_window_width > 0 && ovl->video_window_height > 0) { data->ovl.video_window_x = (ovl->video_window_x * factor_x) >> 16; data->ovl.video_window_y = (ovl->video_window_y * factor_y) >> 16; data->ovl.video_window_width = (ovl->video_window_width * factor_x) >> 16; data->ovl.video_window_height = (ovl->video_window_height * factor_y) >> 16; } #endif data->ovl.rle = (rle_elem_t*) rle_scale_nearest((struct osd_rle_elem_s*)ovl->rle, &num_rle, ovl->width, ovl->height, data->ovl.width, data->ovl.height); data->ovl.num_rle = num_rle; data->scaled = 1; LOGOSD("I: %d,%d %dx%d", ovl->x, ovl->y, ovl->width, ovl->height); LOGOSD("O: %d,%d %dx%d", data->ovl.x, data->ovl.y, data->ovl.width, data->ovl.height); return data; } /* * osdscaler_hook_t */ typedef struct { vo_driver_hook_t h; /* configuration */ uint8_t enable; uint8_t unscaled_supported; uint8_t custom_extent_supported; uint8_t argb_supported; uint8_t video_window_supported; /* current output */ uint16_t output_width; uint16_t output_height; uint32_t factor_x; /* scaling factor << 16 */ uint32_t factor_y; uint16_t x_move; /* OSD displacement */ uint16_t y_move; /* currently showing OSDs - pre-scaled data */ osd_data_t *active_osds; } osdscaler_hook_t; /* * */ /* * override overlay_begin() */ static void osdscaler_overlay_begin (vo_driver_t *self, vo_frame_t *frame, int changed) { osdscaler_hook_t *this = (osdscaler_hook_t*)self; /* assume we're wired when called */ if (!this->h.orig_driver) { LOGMSG("osdscaler_overlay_begin: assertion this->h.orig_driver failed !"); abort(); } /* re-scale all if OSD changed */ if (changed) { LOGOSD("osdscaler_overlay_begin: changed = 1"); osd_data_clear(this->active_osds); this->active_osds = NULL; uint64_t caps = vo_def_get_capabilities(self); this->unscaled_supported = !!(caps & VO_CAP_UNSCALED_OVERLAY); this->custom_extent_supported = !!(caps & VO_XCAP_OSDSCALING); /* == VO_CAP_CUSTOM_EXTENT_OVERLAY */ this->argb_supported = !!(caps & VO_XCAP_ARGB_LAYER_OVERLAY); this->video_window_supported = !!(caps & VO_XCAP_VIDEO_WINDOW_OVERLAY); } /* redirect */ if (this->h.orig_driver->overlay_begin) this->h.orig_driver->overlay_begin(this->h.orig_driver, frame, changed); } static int check_for_scaling(osdscaler_hook_t *this, vo_frame_t *frame, vo_overlay_t *overlay) { int extent_width, extent_height; this->x_move = this->y_move = 0; if (!this->enable) { LOGVERBOSE("check_for_scaling(): scaling disabled in config"); return 0; } if (!overlay->rle) { LOGVERBOSE("check_for_scaling(): no overlay->rle"); return 0; } #ifdef VO_CAP_ARGB_LAYER_OVERLAY if (overlay->argb_layer) { LOGVERBOSE("check_for_scaling(): overlay has argb layer"); return 0; } #endif /* check for VDR OSD */ if (overlay->hili_rgb_clut != VDR_OSD_MAGIC /* not from vdr */) { LOGOSD("overlay: source not VDR"); if (!frame->stream || frame->stream == XINE_ANON_STREAM) return 0; return 0; } /* VDR input plugin stores some control data in hili clut area */ vdr_osd_extradata_t *data = (vdr_osd_extradata_t *)overlay->hili_color; extent_width = data->extent_width; extent_height = data->extent_height; if (data->scaling < 1) { return 0; } #ifdef VO_CAP_CUSTOM_EXTENT_OVERLAY if (this->custom_extent_supported) { /* let the "real" video driver do scaling */ LOGOSD("let the driver to scale overlay"); overlay->extent_width = extent_width; overlay->extent_height = extent_height; return 0; } /* disable VDPAU HW scaler */ overlay->extent_width = 0; overlay->extent_height = 0; #endif if (extent_width < 128 || extent_height < 128) { LOGMSG("VDR overlay: invalid extent size %dx%d", extent_width, extent_height); return 0; } /* detect output size */ if (overlay->unscaled && this->unscaled_supported) { this->output_width = vo_def_get_property((vo_driver_t*)this, VO_PROP_WINDOW_WIDTH); this->output_height = vo_def_get_property((vo_driver_t*)this, VO_PROP_WINDOW_HEIGHT); } else { this->output_width = frame->width; this->output_height = frame->height; /* check cropping */ if (frame->crop_top > 0) this->output_height -= frame->crop_top; if (frame->crop_bottom > 0) this->output_height -= frame->crop_bottom; if (frame->crop_left > 0) this->output_width -= frame->crop_left; if (frame->crop_right > 0) this->output_width -= frame->crop_right; } if (this->output_width < 128 || this->output_height < 128) { LOGMSG("invalid output dimensions: %dx%d", this->output_width, this->output_height); return 0; } /* check if scaling should be done */ if (ABS(this->output_width - extent_width) > extent_width /20 || ABS(this->output_height - extent_height) > extent_height/20 ) { LOGOSD("scaling required to %dx%d", this->output_width, this->output_height); this->factor_x = 0x10000 * this->output_width / extent_width; this->factor_y = 0x10000 * this->output_height / extent_height; return 1; } /* if no scaling was required, we may still need to re-center OSD */ if(this->output_width != extent_width) this->x_move = (this->output_width - extent_width)/2; if(this->output_height != extent_height) this->y_move = (this->output_height - extent_height)/2; return 0; } static vo_overlay_t *scale_overlay(osdscaler_hook_t *this, vo_frame_t *frame, vo_overlay_t *overlay) { if (check_for_scaling(this, frame, overlay)) { /* find old scaled OSD */ osd_data_t *scaled = this->active_osds; while (scaled && scaled->source != overlay) scaled = scaled->next; /* output size changed since last scaling (need to re-scale) ? */ if (scaled && (scaled->output_width != this->output_width || scaled->output_height != this->output_height)) { LOGOSD("re-scaling required: output size changed %dx%d -> %dx%d", scaled->output_width, scaled->output_height, this->output_width, this->output_height); osd_data_remove(&this->active_osds, scaled); scaled = NULL; } /* not scaled found ? */ if (!scaled) { LOGOSD("scaling OSD"); scaled = this->active_osds = osd_data_init(overlay, this->active_osds, this->factor_x, this->factor_y); scaled->output_width = this->output_width; scaled->output_height = this->output_height; } /* override */ overlay = &scaled->ovl; } return overlay; } /* * interface */ /* * override overlay_blend() */ static void osdscaler_overlay_blend (vo_driver_t *self, vo_frame_t *frame, vo_overlay_t *overlay) { osdscaler_hook_t *this = (osdscaler_hook_t*)self; overlay = scale_overlay(this, frame, overlay); /* redirect */ if (this->h.orig_driver->overlay_blend) this->h.orig_driver->overlay_blend(this->h.orig_driver, frame, overlay); } /* * override overlay_end() */ static void osdscaler_overlay_end (vo_driver_t *self, vo_frame_t *vo_img) { osdscaler_hook_t *this = (osdscaler_hook_t*)self; /* redirect */ if (this->h.orig_driver->overlay_end) this->h.orig_driver->overlay_end(this->h.orig_driver, vo_img); } /* * Management interface */ /* * override get_capabilities() */ static uint32_t osdscaler_get_capabilities(vo_driver_t *self) { return vo_def_get_capabilities(self) | VO_XCAP_OSDSCALING; } /* * override get_property() */ static int osdscaler_get_property(vo_driver_t *self, int prop) { osdscaler_hook_t *this = (osdscaler_hook_t*)self; switch (prop) { case VO_PROP_OSD_SCALING: return this->enable; default:; } return vo_def_get_property(self, prop); } /* * override set_property() */ static int osdscaler_set_property(vo_driver_t *self, int prop, int val) { osdscaler_hook_t *this = (osdscaler_hook_t*)self; switch (prop) { case VO_PROP_OSD_SCALING: if (this->enable != val) { LOGOSD("osdscaler_set_property: enable %d->%d", this->enable, val); this->enable = val?1:0; } return this->enable; default:; } return vo_def_set_property(self, prop, val); } /* * dispose() */ static void osdscaler_dispose(vo_driver_t *self) { osdscaler_hook_t *this = (osdscaler_hook_t *) self; osd_data_clear(this->active_osds); vo_def_dispose(self); } /* * init() */ vo_driver_t *osdscaler_init(void) { osdscaler_hook_t *this = calloc(1, sizeof(osdscaler_hook_t)); /* OSD interface */ this->h.vo.overlay_begin = osdscaler_overlay_begin; this->h.vo.overlay_blend = osdscaler_overlay_blend; this->h.vo.overlay_end = osdscaler_overlay_end; /* management interface */ this->h.vo.get_capabilities = osdscaler_get_capabilities; this->h.vo.get_property = osdscaler_get_property; this->h.vo.set_property = osdscaler_set_property; this->h.vo.dispose = osdscaler_dispose; /* initialize data */ this->enable = 0; return &this->h.vo; } xineliboutput-2.0.0/xine/vo_osdreorder.h0000644000175000017500000000053413061253352016216 0ustar phph/* * vo_osdreorder.h: OSD re-ordering video-out post plugin * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_VO_OSDREORDER_H #define _XINELIBOUTPUT_VO_OSDREORDER_H vo_driver_t *osd_reorder_init(void); #endif /* _XINELIBOUTPUT_VO_OSDREORDER_H */ xineliboutput-2.0.0/xine/vo_osdreorder.c0000644000175000017500000000403713061253352016213 0ustar phph/* * vo_osdreorder.c: OSD re-ordering video-out post plugin * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include "vo_props.h" #include "vo_hook.h" #include "vo_osdreorder.h" /* * osdreorder_hook_t */ typedef struct { vo_driver_hook_t h; /* currently showing OSDs in new drawing order */ vo_overlay_t *overlay[50]; } osdreorder_hook_t; /* * */ static int osd_level(vo_overlay_t *overlay) { if (overlay->hili_rgb_clut != VDR_OSD_MAGIC /* not from vdr */) { return 9999; } /* VDR input plugin stores some control data in hili clut area */ vdr_osd_extradata_t *data = (vdr_osd_extradata_t *)overlay->hili_color; return data->layer; } /* * interface */ /* * override overlay_blend() */ static void osdreorder_overlay_blend (vo_driver_t *self, vo_frame_t *frame, vo_overlay_t *overlay) { osdreorder_hook_t *this = (osdreorder_hook_t*)self; int my_level = osd_level(overlay); int i; /* search position */ for (i = 0; this->overlay[i] && osd_level(this->overlay[i]) >= my_level; i++) ; /* make room */ if (this->overlay[i]) memmove(&this->overlay[i+1], &this->overlay[i], (49-i)*sizeof(vo_overlay_t*)); this->overlay[i] = overlay; return; } /* * override overlay_end() */ static void osdreorder_overlay_end (vo_driver_t *self, vo_frame_t *vo_img) { osdreorder_hook_t *this = (osdreorder_hook_t*)self; int i; for (i = 0; this->overlay[i]; i++) { this->h.orig_driver->overlay_blend(this->h.orig_driver, vo_img, this->overlay[i]); this->overlay[i] = NULL; } /* redirect */ if (this->h.orig_driver->overlay_end) this->h.orig_driver->overlay_end(this->h.orig_driver, vo_img); } /* * init() */ vo_driver_t *osd_reorder_init(void) { osdreorder_hook_t *this = calloc(1, sizeof(osdreorder_hook_t)); /* OSD interface */ this->h.vo.overlay_blend = osdreorder_overlay_blend; this->h.vo.overlay_end = osdreorder_overlay_end; return &this->h.vo; } xineliboutput-2.0.0/xine/vo_lastpts.h0000644000175000017500000000045113061253352015536 0ustar phph/* * vo_lastpts.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_VO_LASTPTS_H #define _XINELIBOUTPUT_VO_LASTPTS_H vo_driver_t *vo_lastpts_init(void); #endif /* _XINELIBOUTPUT_VO_LASTPTS_H */ xineliboutput-2.0.0/xine/vo_lastpts.c0000644000175000017500000000454413061253352015540 0ustar phph/* * vo_lastpts.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include #include #include "xvdr_metronom.h" #include "vo_hook.h" #define LOG_MODULENAME "[lastpts ] " #include "../logdefs.h" #include "vo_lastpts.h" /* * lastpts_hook_t */ typedef struct { vo_driver_hook_t h; xine_stream_t *prev_stream; xine_stream_t *xvdr_stream; metronom_t *xvdr_metronom; } lastpts_hook_t; static void detect_xvdr_metronom(lastpts_hook_t *this, xine_stream_t *stream) { if (stream->metronom->get_option(stream->metronom, XVDR_METRONOM_ID) == XVDR_METRONOM_ID) { LOGDBG("new stream is vdr stream"); this->xvdr_metronom = stream->metronom; this->xvdr_stream = stream; } } /* * interface */ /* * override display_frame() */ static void lastpts_display_frame(vo_driver_t *self, vo_frame_t *vo_img) { lastpts_hook_t *this = (lastpts_hook_t*)self; ASSERT_RET(self, return); ASSERT_RET(vo_img, return); if (!vo_img->stream || vo_img->stream == XINE_ANON_STREAM || vo_img->pts <= 0) { this->h.orig_driver->display_frame(this->h.orig_driver, vo_img); return; } /* detect intercepted metronom with XVDR_* option support. * This prevents flooding log with "unknown option in set_option" messages */ if (vo_img->stream != this->prev_stream) { LOGDBG("stream changed from %p to %p", this->prev_stream, vo_img->stream); this->prev_stream = vo_img->stream; detect_xvdr_metronom(this, vo_img->stream); } if (vo_img->stream == this->xvdr_stream) { if (this->xvdr_metronom != vo_img->stream->metronom) { LOGMSG("detected new metronom"); this->xvdr_metronom = NULL; detect_xvdr_metronom(this, vo_img->stream); } if (this->xvdr_metronom) { ASSERT_RET(this->xvdr_metronom->set_option, return); LOGVERBOSE("last pts %"PRId64, vo_img->pts); this->xvdr_metronom->set_option(this->xvdr_metronom, XVDR_METRONOM_LAST_VO_PTS, vo_img->pts); } } this->h.orig_driver->display_frame(this->h.orig_driver, vo_img); } /* * init() */ vo_driver_t *vo_lastpts_init(void) { lastpts_hook_t *this = calloc(1, sizeof(lastpts_hook_t)); this->h.vo.display_frame = lastpts_display_frame; return &this->h.vo; } xineliboutput-2.0.0/xine/vo_hook.h0000644000175000017500000000151613061253352015007 0ustar phph/* * vo_hook.h: Intercept video driver * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_VO_HOOK_H #define _XINELIBOUTPUT_VO_HOOK_H #include /* * synchronous video post plugins * internal API */ /* * vo_driver_hook_t * * Used as base for video driver hooks */ typedef struct driver_hook_s { vo_driver_t vo; /* public part */ vo_driver_t *orig_driver; } vo_driver_hook_t; /* proxy handlers: forward call to original driver */ uint32_t vo_def_get_capabilities(vo_driver_t *self); int vo_def_get_property(vo_driver_t *self, int prop); int vo_def_set_property(vo_driver_t *self, int prop, int val); void vo_def_dispose(vo_driver_t *self); #endif /* _XINELIBOUTPUT_VO_HOOK_H */ xineliboutput-2.0.0/xine/vo_hook.c0000644000175000017500000001476213061253352015011 0ustar phph/* * vo_hook.c: Intercept video driver * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #define LOG_MODULENAME "[xine-vo ] " #include "../logdefs.h" #include "vo_hook.h" #include "vo_post.h" /* This module supports only video driver interface version 21 */ #if (VIDEO_OUT_DRIVER_IFACE_VERSION < 21) # error xine-lib VIDEO_OUT_DRIVER_IFACE_VERSION < 21 #endif /* * default handlers * * - Forward function call to original driver */ #define DEF_HANDLER3(RET, NAME, ARG1, ARG2, ARG3) \ RET vo_def_##NAME (vo_driver_t *self, ARG1 a1, ARG2 a2, ARG3 a3) { \ vo_driver_hook_t *this = (vo_driver_hook_t *) self; \ return this->orig_driver-> NAME (this->orig_driver, a1, a2, a3); \ } #define DEF_HANDLER2(RET, NAME, ARG1, ARG2) \ RET vo_def_##NAME (vo_driver_t *self, ARG1 a1, ARG2 a2) { \ vo_driver_hook_t *this = (vo_driver_hook_t *) self; \ return this->orig_driver-> NAME (this->orig_driver, a1, a2); \ } #define DEF_HANDLER1(RET, NAME, ARG1) \ RET vo_def_##NAME (vo_driver_t *self, ARG1 a1) { \ vo_driver_hook_t *this = (vo_driver_hook_t *) self; \ return this->orig_driver-> NAME (this->orig_driver, a1); \ } #define DEF_HANDLER0(RET, NAME) \ RET vo_def_##NAME (vo_driver_t *self) { \ vo_driver_hook_t *this = (vo_driver_hook_t *) self; \ return this->orig_driver-> NAME (this->orig_driver); \ } /* * */ DEF_HANDLER0(uint32_t, get_capabilities); static DEF_HANDLER0(vo_frame_t*, alloc_frame); static void vo_def_update_frame_format (vo_driver_t *self, vo_frame_t *img, uint32_t width, uint32_t height, double ratio, int format, int flags) { vo_driver_hook_t *this = (vo_driver_hook_t *) self; return this->orig_driver-> update_frame_format (this->orig_driver, img, width, height, ratio, format, flags); } static DEF_HANDLER1(void, display_frame, vo_frame_t * ); static DEF_HANDLER2(void, overlay_begin, vo_frame_t *, int ); static DEF_HANDLER2(void, overlay_blend, vo_frame_t *, vo_overlay_t * ); static DEF_HANDLER1(void, overlay_end, vo_frame_t *); DEF_HANDLER1(int, get_property, int); DEF_HANDLER2(int, set_property, int, int); static DEF_HANDLER3(void, get_property_min_max, int, int*, int*); static DEF_HANDLER2(int, gui_data_exchange, int, void * ); static DEF_HANDLER0(int, redraw_needed ); #ifdef HAVE_XINE_GRAB_VIDEO_FRAME static DEF_HANDLER0(xine_grab_video_frame_t*, new_grab_video_frame ); #endif void vo_def_dispose(vo_driver_t *self) { vo_driver_hook_t *this = (vo_driver_hook_t *) self; if (this->orig_driver) this->orig_driver->dispose(this->orig_driver); free(self); } /* * vo_def_hooks_init() * * initialize driver_hook_t with default handlers * */ static void vo_proxy_hooks_init(vo_driver_t *drv, vo_driver_t *next_driver) { drv->get_capabilities = drv->get_capabilities ?: vo_def_get_capabilities; drv->alloc_frame = drv->alloc_frame ?: vo_def_alloc_frame; drv->update_frame_format = drv->update_frame_format ?: vo_def_update_frame_format; drv->display_frame = drv->display_frame ?: vo_def_display_frame; drv->get_property = drv->get_property ?: vo_def_get_property; drv->set_property = drv->set_property ?: vo_def_set_property; drv->get_property_min_max = drv->get_property_min_max ?: vo_def_get_property_min_max; drv->gui_data_exchange = drv->gui_data_exchange ?: vo_def_gui_data_exchange; drv->redraw_needed = drv->redraw_needed ?: vo_def_redraw_needed; drv->dispose = drv->dispose ?: vo_def_dispose; #ifdef HAVE_XINE_GRAB_VIDEO_FRAME if (!drv->new_grab_video_frame && next_driver->new_grab_video_frame) drv->new_grab_video_frame = vo_def_new_grab_video_frame; #endif /* drop old default handlers for OSD (OSD handlers are optional) */ if (drv->overlay_begin == vo_def_overlay_begin) drv->overlay_begin = NULL; if (drv->overlay_blend == vo_def_overlay_blend) drv->overlay_blend = NULL; if (drv->overlay_end == vo_def_overlay_end) drv->overlay_end = NULL; /* Set proxy handlers for OSD only if next driver has OSD handlers */ if (!drv->overlay_begin && next_driver->overlay_begin) drv->overlay_begin = vo_def_overlay_begin; if (!drv->overlay_blend && next_driver->overlay_blend) drv->overlay_blend = vo_def_overlay_blend; if (!drv->overlay_end && next_driver->overlay_end) drv->overlay_end = vo_def_overlay_end; drv->node = next_driver->node; /* ??? used only by plugin_loader ? */ } /* * */ /* from xine-lib video_out.c */ typedef struct { xine_video_port_t vo; /* public part */ vo_driver_t *driver; /* ... */ } vos_t; /* * wire_video_driver() * */ int wire_video_driver(xine_video_port_t *video_port, vo_driver_t *hook) { vo_driver_t *vos_driver = ((vos_t*)video_port)->driver; if (video_port->driver != vos_driver) { LOGMSG("wire_video_driver() FAILED (vo_driver != vos_driver)"); return 0; } /*LOGMSG("wire_video_driver: vo_driver == vos_driver");*/ /* wire */ /* set proxy handlers for undefined methods */ vo_proxy_hooks_init(hook, video_port->driver); /* append original driver chain to new driver */ ((vo_driver_hook_t *)hook)->orig_driver = video_port->driver; /* push new driver to start of driver chain */ video_port->driver = hook; ((vos_t*)video_port)->driver = hook; return 1; } /* * unwire_video_driver() * */ int unwire_video_driver(xine_video_port_t *video_port, vo_driver_t *hook_gen, vo_driver_t *video_out) { vo_driver_hook_t *hook = (vo_driver_hook_t*)hook_gen; vo_driver_hook_t *next = (vo_driver_hook_t*)video_port->driver; if (next == hook) { /* special handling for first entry */ video_port->driver = next->orig_driver; /* need to patch video_port private driver pointer too ... */ ((vos_t*)video_port)->driver = next->orig_driver; next->orig_driver = NULL; return 1; } vo_driver_hook_t *prev = next; while (next && next != hook && (vo_driver_t*)next != video_out) { prev = next; next = (vo_driver_hook_t*)next->orig_driver; } if (prev && next == hook) { prev->orig_driver = next->orig_driver; next->orig_driver = NULL; return 1; } return 0; } xineliboutput-2.0.0/xine/vo_frameoutput.h0000644000175000017500000000054513061253352016423 0ustar phph/* * vo_frameoutput.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_VO_FRAMEOUTPUT_H #define _XINELIBOUTPUT_VO_FRAMEOUTPUT_H vo_driver_t *vo_frameoutput_init(void *handle, void (*cb)(void*, vo_frame_t*)); #endif /* _XINELIBOUTPUT_VO_FRAMEOUTPUT_H */ xineliboutput-2.0.0/xine/vo_frameoutput.c0000644000175000017500000000221013061253352016405 0ustar phph/* * vo_frameoutput.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include #include "vo_hook.h" #define LOG_MODULENAME "[frame_out] " #include "../logdefs.h" #include "vo_frameoutput.h" /* * frameoutput_hook_t */ typedef struct { vo_driver_hook_t h; /* callback */ void *handle; void (*cb)(void *, vo_frame_t *); } frameoutput_hook_t; /* * interface */ /* * override display_frame() */ static void display_frame(vo_driver_t *self, vo_frame_t *vo_img) { frameoutput_hook_t *this = (frameoutput_hook_t*)self; ASSERT_RET(self, return); ASSERT_RET(vo_img, return); if (this->cb) this->cb(this->handle, vo_img); this->h.orig_driver->display_frame(this->h.orig_driver, vo_img); } /* * init() */ vo_driver_t *vo_frameoutput_init(void *handle, void (*cb)(void*, vo_frame_t*)) { frameoutput_hook_t *this = calloc(1, sizeof(frameoutput_hook_t)); this->h.vo.display_frame = display_frame; this->handle = handle; this->cb = cb; return &this->h.vo; } xineliboutput-2.0.0/xine/ts2es.h0000644000175000017500000000111113061253352014372 0ustar phph/* * ts2es.h: demux MPEG-TS -> ES * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _DEMUX_XVDR_TS2ES_H_ #define _DEMUX_XVDR_TS2ES_H_ typedef struct ts2es_s ts2es_t; ts2es_t *ts2es_init (fifo_buffer_t *dst_fifo, ts_stream_type stream_type, unsigned stream_index); buf_element_t *ts2es_put (ts2es_t *ts2es, uint8_t *ts_packet, fifo_buffer_t *src_fifo); void ts2es_flush (ts2es_t *ts2es); void ts2es_dispose (ts2es_t *ts2es); #endif /* _DEMUX_XVDR_TS2ES_H_ */ xineliboutput-2.0.0/xine/ts2es.c0000644000175000017500000002254413061253352014402 0ustar phph/* * ts2es.c: demux MPEG-TS -> ES * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include "../tools/ts.h" #include "../tools/pes.h" #include "ts2es.h" #define LOG_MODULENAME "[demux_vdr] " #define SysLogLevel iSysLogLevel #include "../logdefs.h" struct ts2es_s { fifo_buffer_t *fifo; uint32_t stream_type; uint32_t xine_buf_type; buf_element_t *buf; int pes_len; /* PES payload length left */ uint8_t first_pusi_seen; uint8_t video; uint8_t pes_error; }; static void ts2es_parse_pes(ts2es_t *this) { if (!DATA_IS_PES(this->buf->content)) { LOGDBG("ts2es: payload not PES ?"); this->pes_error = 1; return; } this->pes_error = 0; /* parse PES header */ unsigned hdr_len = PES_HEADER_LEN(this->buf->content); uint8_t pes_pid = this->buf->content[3]; /* Check if header is complete */ if (this->buf->size < 9 || this->buf->size < hdr_len) { LOGMSG("ts2es: PES header not in first TS fragment"); this->pes_error = 1; return; } /* Check if PES packet size is known */ this->pes_len = (this->buf->content[4] << 8) | this->buf->content[5]; if (this->pes_len > hdr_len + 6) { this->pes_len += 6; } else { this->pes_len = 0; } /* parse PTS */ this->buf->pts = pes_get_pts(this->buf->content, this->buf->size); if (this->buf->pts < 0) this->buf->pts = 0; /* parse DTS */ if (this->video && this->buf->pts > 0) { int64_t dts = pes_get_dts(this->buf->content, this->buf->size); if (dts > 0) this->buf->decoder_info[0] = this->buf->pts - dts; } /* strip PES header */ this->buf->content += hdr_len; this->buf->size -= hdr_len; /* parse substream header */ if (this->stream_type == HDMV_AUDIO_80_PCM) { this->buf->decoder_flags |= BUF_FLAG_SPECIAL; this->buf->decoder_info[1] = BUF_SPECIAL_LPCM_CONFIG; this->buf->decoder_info[2] = (this->buf->content[3] << 24) | (this->buf->content[2] << 16) | (this->buf->content[1] << 8) | this->buf->content[0]; this->buf->content += 4; this->buf->size -= 4; return; } if (pes_pid != PRIVATE_STREAM1) return; /* RAW audio ? -> do nothing */ switch (this->stream_type) { case STREAM_AUDIO_AC3: case STREAM_AUDIO_EAC3: case STREAM_AUDIO_AAC: case STREAM_AUDIO_DTS: return; default:; } /* AC3 syncword in beginning of PS1 payload ? */ if (this->buf->content[0] == 0x0B && this->buf->content[1] == 0x77) { /* --> RAW AC3 audio - do nothing */ this->xine_buf_type |= BUF_AUDIO_A52; this->buf->type = this->xine_buf_type; return; } /* audio in PS1 */ if (this->stream_type == ISO_13818_PES_PRIVATE) { if ((this->buf->content[0] & 0xf0) == 0x80) { /* AC3, strip substream header */ this->buf->content += 4; this->buf->size -= 4; this->xine_buf_type |= BUF_AUDIO_A52; this->buf->type = this->xine_buf_type; return; } if ((this->buf->content[0] & 0xf0) == 0xa0) { /* PCM, strip substream header */ int pcm_offset; for (pcm_offset=0; ++pcm_offset < this->buf->size-1 ; ) { if (this->buf->content[pcm_offset] == 0x01 && this->buf->content[pcm_offset+1] == 0x80 ) { /* START */ pcm_offset += 2; break; } } this->buf->content += pcm_offset; this->buf->size -= pcm_offset; this->xine_buf_type |= BUF_AUDIO_LPCM_BE; this->buf->type = this->xine_buf_type; return; } LOGMSG("ts2es: unhandled PS1 substream 0x%x", this->buf->content[0]); return; } /* DVB SPU */ if (this->stream_type == STREAM_DVBSUB) { if (this->buf->content[0] != 0x20 || this->buf->content[1] != 0x00) LOGMSG("ts2es: DVB SPU, invalid PES substream header"); if (this->pes_len > hdr_len) { this->buf->decoder_info[2] = this->pes_len - hdr_len; } return; } } buf_element_t *ts2es_put(ts2es_t *this, uint8_t *data, fifo_buffer_t *src_fifo) { buf_element_t *result = NULL; int bytes = ts_PAYLOAD_SIZE(data); int pusi = ts_PAYLOAD_START(data); if (ts_HAS_ERROR(data)) { LOGDBG("ts2es: transport error"); return NULL; } if (!ts_HAS_PAYLOAD(data)) { LOGVERBOSE("ts2es: no payload, size %d", bytes); return NULL; } /* drop broken PES packets */ if (this->pes_error && !pusi) { if (this->buf) { LOGDBG("ts2es: dropping broken PES packet"); this->buf->free_buffer(this->buf); this->buf = NULL; } return NULL; } /* handle new payload unit */ if (pusi) { if (this->first_pusi_seen && !this->buf) { this->buf = this->fifo->buffer_pool_alloc(this->fifo); this->buf->type = this->xine_buf_type; } if (this->buf) { this->buf->decoder_flags |= BUF_FLAG_FRAME_END; result = this->buf; this->buf = NULL; } this->first_pusi_seen = 1; } /* split large packets */ if (this->buf) { if ((this->video && this->buf->size >= 2048) || this->buf->size >= this->buf->max_size-2*TS_SIZE) { result = this->buf; this->buf = NULL; } } /* need new buffer ? */ if (!this->buf) { /* discard data until first payload start indicator */ if (!this->first_pusi_seen) return NULL; if (src_fifo && src_fifo != this->fifo) { if (!this->video) this->buf = this->fifo->buffer_pool_try_alloc(this->fifo); if (!this->buf) this->buf = src_fifo->buffer_pool_try_alloc(src_fifo); if (!this->buf) this->buf = this->fifo->buffer_pool_alloc(this->fifo); } else { this->buf = this->fifo->buffer_pool_alloc(this->fifo); } this->buf->type = this->xine_buf_type; } /* strip ts header */ data += TS_SIZE - bytes; /* copy payload */ memcpy(this->buf->content + this->buf->size, data, bytes); this->buf->size += bytes; /* parse PES header */ if (pusi) { ts2es_parse_pes(this); } /* check if PES packet is complete */ if (this->pes_len > 0) { if (this->pes_len <= bytes) { /* XXX FIXME: one call to ts2es_put may result in two output packets * if stream mixes known-length and unknown-length packets. */ if (!result) { this->buf->decoder_flags |= BUF_FLAG_FRAME_END; result = this->buf; this->buf = NULL; this->pes_error = 1; /* to drop rest of data */ } this->pes_len = 0; } else { this->pes_len -= bytes; } } return result; } void ts2es_flush(ts2es_t *this) { if (this->buf) { this->buf->decoder_flags |= BUF_FLAG_FRAME_END; /* clear PTS (frame is bypassing demuxer timestamp checks) */ this->buf->pts = 0; this->fifo->put (this->fifo, this->buf); this->buf = NULL; } } void ts2es_dispose(ts2es_t *data) { if (data) { if (data->buf) data->buf->free_buffer(data->buf); free(data); } } ts2es_t *ts2es_init(fifo_buffer_t *dst_fifo, ts_stream_type stream_type, unsigned stream_index) { ts2es_t *data = calloc(1, sizeof(ts2es_t)); if (!data) { return NULL; } data->fifo = dst_fifo; data->stream_type = stream_type; switch(stream_type) { /* VIDEO (PES streams 0xe0...0xef) */ case ISO_11172_VIDEO: case ISO_13818_VIDEO: case STREAM_VIDEO_MPEG: data->xine_buf_type = BUF_VIDEO_MPEG; break; case ISO_14496_PART2_VIDEO: data->xine_buf_type = BUF_VIDEO_MPEG4; break; case ISO_14496_PART10_VIDEO: data->xine_buf_type = BUF_VIDEO_H264; break; case STREAM_VIDEO_VC1: data->xine_buf_type = BUF_VIDEO_VC1; break; #ifdef BUF_VIDEO_HEVC case STREAM_VIDEO_HEVC: data->xine_buf_type = BUF_VIDEO_HEVC; break; #endif /* AUDIO (PES streams 0xc0...0xdf) */ case ISO_11172_AUDIO: case ISO_13818_AUDIO: data->xine_buf_type = BUF_AUDIO_MPEG; break; case ISO_14496_PART3_AUDIO: #ifdef BUF_AUDIO_AAC_LATM data->xine_buf_type = BUF_AUDIO_AAC_LATM; break; #endif case ISO_13818_PART7_AUDIO: data->xine_buf_type = BUF_AUDIO_AAC; break; /* AUDIO (PES stream 0xbd) */ case ISO_13818_PES_PRIVATE: data->xine_buf_type = 0; /* detect from PES substream header */ break; /* DVB SPU (PES stream 0xbd) */ case STREAM_DVBSUB: data->xine_buf_type = BUF_SPU_DVB; break; /* RAW AC3 */ case STREAM_AUDIO_AC3: case HDMV_AUDIO_83_TRUEHD: data->xine_buf_type = BUF_AUDIO_A52; break; /* EAC3 (xine-lib > 1.1.18.1) */ #ifdef BUF_AUDIO_EAC3 case STREAM_AUDIO_EAC3: case HDMV_AUDIO_84_EAC3: data->xine_buf_type = BUF_AUDIO_EAC3; break; #endif case HDMV_AUDIO_80_PCM: data->xine_buf_type = BUF_AUDIO_LPCM_BE; break; /* DTS (PES stream 0xbd) */ case STREAM_AUDIO_DTS: case HDMV_AUDIO_82_DTS: case HDMV_AUDIO_85_DTS_HRA: case HDMV_AUDIO_86_DTS_HD_MA: data->xine_buf_type = BUF_AUDIO_DTS; break; /* AAC (PES stream 0xbd) */ case STREAM_AUDIO_AAC: data->xine_buf_type = BUF_AUDIO_AAC; break; default: LOGMSG("ts2es: unknown stream type 0x%x", stream_type); break; } /* substream ID (audio/SPU) */ data->xine_buf_type |= stream_index; if ((data->xine_buf_type & BUF_MAJOR_MASK) == BUF_VIDEO_BASE) data->video = 1; return data; } xineliboutput-2.0.0/xine/post_util.h0000644000175000017500000000651713061253352015373 0ustar phph/* * post_util.h: post plugin utility functions * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #if POST_PLUGIN_IFACE_VERSION < 9 # warning POST_PLUGIN_IFACE_VERSION < 9 not supported ! #endif #if POST_PLUGIN_IFACE_VERSION > 10 # warning POST_PLUGIN_IFACE_VERSION > 10 not supported ! #endif /* * class util prototypes */ static void *init_plugin(xine_t *xine, void *data); #if POST_PLUGIN_IFACE_VERSION < 10 static char *get_identifier(post_class_t *class_gen); static char *get_description(post_class_t *class_gen); static void class_dispose(post_class_t *class_gen); #endif /* required from plugin: */ static post_plugin_t *open_plugin(post_class_t *class_gen, int inputs, xine_audio_port_t **audio_target, xine_video_port_t **video_target); /* * plugin util prototypes */ static int dispatch_draw(vo_frame_t *frame, xine_stream_t *stream); static int intercept_frame_yuy(post_video_port_t *port, vo_frame_t *frame); static int post_draw(vo_frame_t *frame, xine_stream_t *stream); #ifdef ENABLE_SLICED static void dispatch_slice(vo_frame_t *vo_img, uint8_t **src); #endif /* required from plugin: */ static vo_frame_t *got_frame(vo_frame_t *frame); static void draw_internal(vo_frame_t *frame, vo_frame_t *new_frame); /* * class utils */ static void *init_plugin(xine_t *xine, void *data) { post_class_t *class = calloc(1, sizeof(post_class_t)); if (!class) return NULL; class->open_plugin = open_plugin; #if POST_PLUGIN_IFACE_VERSION < 10 class->get_identifier = get_identifier; class->get_description = get_description; class->dispose = class_dispose; #else class->identifier = PLUGIN_ID; class->description = PLUGIN_DESCR; class->dispose = default_post_class_dispose; #endif return class; } #if POST_PLUGIN_IFACE_VERSION < 10 static char *get_identifier(post_class_t *class_gen) { return PLUGIN_ID; } static char *get_description(post_class_t *class_gen) { return PLUGIN_DESCR; } static void class_dispose(post_class_t *class_gen) { free(class_gen); } #endif /* * plugin utils */ #ifdef ENABLE_SLICED static void dispatch_slice(vo_frame_t *vo_img, uint8_t **src) { if (vo_img->next->proc_slice) { _x_post_frame_copy_down(vo_img, vo_img->next); vo_img->next->proc_slice(vo_img->next, src); _x_post_frame_copy_up(vo_img, vo_img->next); } } #endif static int dispatch_draw(vo_frame_t *frame, xine_stream_t *stream) { int skip; _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; } static int intercept_frame_yuy(post_video_port_t *port, vo_frame_t *frame) { return (frame->format == XINE_IMGFMT_YV12 || frame->format == XINE_IMGFMT_YUY2); } static int post_draw(vo_frame_t *frame, xine_stream_t *stream) { vo_frame_t *new_frame; int skip; if (frame->bad_frame) return dispatch_draw(frame, stream); new_frame = got_frame(frame); if (!new_frame) return dispatch_draw(frame, stream); _x_post_frame_copy_down(frame, new_frame); draw_internal(frame, new_frame); skip = new_frame->draw(new_frame, stream); _x_post_frame_copy_up(frame, new_frame); new_frame->free(new_frame); return skip; } xineliboutput-2.0.0/xine/post.h0000644000175000017500000000637213061253352014335 0ustar phph/* * Copyright (C) 2003 by Dirk Meyer * * This file is part of xine, a unix 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * Modified for xineliboutput by Petri Hintukainen, 2006 * */ #ifndef POST_HH #define POST_HH #include #include #include #include typedef struct { xine_post_t *post; char *name; char *args; int enable; /* 0 - disabled, 1 - enabled, 2 - can't disable */ } post_element_t; typedef struct post_plugins_s post_plugins_t; struct post_plugins_s { /* frontend data */ char *static_post_plugins; /* post plugins from command line; always on */ xine_stream_t *video_source; /* stream to take video from */ xine_stream_t *audio_source; /* stream to take audio from */ xine_stream_t *pip_stream; /* pip stream */ /* xine */ xine_t *xine; xine_video_port_t *video_port; xine_audio_port_t *audio_port; /* post.c internal use */ int post_audio_elements_num; int post_video_elements_num; int post_vis_elements_num; int post_pip_elements_num; post_element_t **post_audio_elements; post_element_t **post_video_elements; post_element_t **post_vis_elements; /* supports only one */ post_element_t **post_pip_elements; /* supports only one and two input */ int post_audio_enable; int post_video_enable; int post_vis_enable; int post_pip_enable; }; void vpplugin_rewire_posts(post_plugins_t *fe); void applugin_rewire_posts(post_plugins_t *fe); /* load and config post plugin(s) */ /* post == "plugin:arg1=val1,arg2=val2;plugin2..." */ void vpplugin_parse_and_store_post(post_plugins_t *fe, const char *post); void applugin_parse_and_store_post(post_plugins_t *fe, const char *post); /* enable (and load if not loaded), but don't rewire */ /* result indicates only unwiring condition, not enable result */ /* -> if result <> 0, something was enabled and post chain is unwired */ int vpplugin_enable_post(post_plugins_t *fe, const char *name, int *found); int applugin_enable_post(post_plugins_t *fe, const char *name, int *found); /* disable (and unwire if found), but don't unload */ /* result indicates only unwiring condition, not disable result */ int vpplugin_disable_post(post_plugins_t *fe, const char *name); int applugin_disable_post(post_plugins_t *fe, const char *name); /* unload (and unwire) plugin(s) */ /* result indicates only unwiring condition, not unload result */ int vpplugin_unload_post(post_plugins_t *fe, const char *name); int applugin_unload_post(post_plugins_t *fe, const char *name); #endif /* end of post.h */ xineliboutput-2.0.0/xine/post.c0000644000175000017500000006364713061253352014340 0ustar phph/* * Copyright (C) 2003 by Dirk Meyer * * This file is part of xine, a unix 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * The code is taken from xine-ui/src/xitk/post.c at changed to work with fbxine * * Modified for VDR xineliboutput plugin by Petri Hintukainen, 2006 * - runtime re-configuration (load/unload, enable/disable) * - support for multiple streams * - support for mosaico post plugin (picture-in-picture) * */ #include #include #include #include "post.h" #include #include #include #define LOG_MODULENAME "[xine-post] " #include "../logdefs.h" #define fe_t post_plugins_t typedef struct { xine_post_t *post; xine_post_api_t *api; xine_post_api_descr_t *descr; xine_post_api_parameter_t *param; char *param_data; int x; int y; int readonly; char **properties_names; } post_object_t; static int __pplugin_retrieve_parameters(post_object_t *pobj) { xine_post_in_t *input_api; if((input_api = (xine_post_in_t *) xine_post_input(pobj->post, "parameters"))) { xine_post_api_t *post_api; xine_post_api_descr_t *api_descr; xine_post_api_parameter_t *parm; int pnum = 0; post_api = (xine_post_api_t *) input_api->data; api_descr = post_api->get_param_descr(); parm = api_descr->parameter; pobj->param_data = malloc(api_descr->struct_size); while(parm->type != POST_PARAM_TYPE_LAST) { post_api->get_parameters(pobj->post, pobj->param_data); if(!pnum) pobj->properties_names = (char **) calloc(2, sizeof(char *)); else pobj->properties_names = (char **) realloc(pobj->properties_names, sizeof(char *) * (pnum + 2)); pobj->properties_names[pnum] = strdup(parm->name); pobj->properties_names[pnum + 1] = NULL; pnum++; parm++; } pobj->api = post_api; pobj->descr = api_descr; pobj->param = api_descr->parameter; return 1; } return 0; } static void _pplugin_update_parameter(post_object_t *pobj) { pobj->api->set_parameters(pobj->post, pobj->param_data); pobj->api->get_parameters(pobj->post, pobj->param_data); } static void __pplugin_update_parameters(xine_post_t *post, char *args) { char *p; post_object_t pobj = { .post = post, }; if(__pplugin_retrieve_parameters(&pobj)) { int i; if(pobj.properties_names && args && *args) { char *param; while((param = xine_strsep(&args, ",")) != NULL) { p = param; while((*p != '\0') && (*p != '=')) p++; if(*p) { int param_num = 0; *p++ = '\0'; while(pobj.properties_names[param_num] && strcasecmp(pobj.properties_names[param_num], param)) param_num++; if(pobj.properties_names[param_num]) { pobj.param = pobj.descr->parameter; pobj.param += param_num; pobj.readonly = pobj.param->readonly; switch(pobj.param->type) { case POST_PARAM_TYPE_INT: if(!pobj.readonly) { if(pobj.param->enum_values) { char **values = pobj.param->enum_values; int i = 0; while(values[i]) { if(!strcasecmp(values[i], p)) { *(int *)(pobj.param_data + pobj.param->offset) = i; break; } i++; } if( !values[i] ) *(int *)(pobj.param_data + pobj.param->offset) = (int) strtol(p, &p, 10); } else { *(int *)(pobj.param_data + pobj.param->offset) = (int) strtol(p, &p, 10); } _pplugin_update_parameter(&pobj); } break; case POST_PARAM_TYPE_DOUBLE: if(!pobj.readonly) { *(double *)(pobj.param_data + pobj.param->offset) = strtod(p, &p); _pplugin_update_parameter(&pobj); } break; case POST_PARAM_TYPE_CHAR: case POST_PARAM_TYPE_STRING: if(!pobj.readonly) { if(pobj.param->type == POST_PARAM_TYPE_CHAR) { int maxlen = pobj.param->size / sizeof(char); snprintf((char *)(pobj.param_data + pobj.param->offset), maxlen, "%s", p); _pplugin_update_parameter(&pobj); } else fprintf(stderr, "parameter type POST_PARAM_TYPE_STRING not supported yet.\n"); } break; case POST_PARAM_TYPE_STRINGLIST: /* unsupported */ if(!pobj.readonly) fprintf(stderr, "parameter type POST_PARAM_TYPE_STRINGLIST not supported yet.\n"); break; case POST_PARAM_TYPE_BOOL: if(!pobj.readonly) { *(int *)(pobj.param_data + pobj.param->offset) = ((int) strtol(p, &p, 10)) ? 1 : 0; _pplugin_update_parameter(&pobj); } break; case POST_PARAM_TYPE_LAST: break; /* terminator of parameter list */ default: LOGMSG("%s(%d): Unknown post parameter type %d!", __FUNCTION__, __LINE__, pobj.param->type); } } else { LOGMSG("Unknown post plugin parameter %s !", param); } } } i = 0; while(pobj.properties_names[i]) { free(pobj.properties_names[i]); i++; } free(pobj.properties_names); } free(pobj.param_data); } } /* -post :option1=value1,option2=value2... */ static post_element_t **pplugin_parse_and_load(fe_t *fe, int plugin_type, const char *pchain, int *post_elements_num) { post_element_t **post_elements = NULL; *post_elements_num = 0; if(pchain && strlen(pchain)) { char *post_chain, *freeme, *p; freeme = post_chain = strdup(pchain); while((p = xine_strsep(&post_chain, ";"))) { if(p && strlen(p)) { char *plugin, *args = NULL; xine_post_t *post; while(*p == ' ') p++; plugin = strdup(p); if((p = strchr(plugin, ':'))) *p++ = '\0'; if(p && (strlen(p) > 1)) args = p; #if 0 post = xine_post_init(fe->xine, plugin, 0, &fe->audio_port, &fe->video_port); #else if(plugin_type == XINE_POST_TYPE_VIDEO_COMPOSE) { post = xine_post_init(fe->xine, plugin, 2, &fe->audio_port, &fe->video_port); } else post = xine_post_init(fe->xine, plugin, 0, &fe->audio_port, &fe->video_port); #endif if (post && plugin_type) { if (post->type != plugin_type) { xine_post_dispose(fe->xine, post); post = NULL; } } if(post) { if(!(*post_elements_num)) post_elements = (post_element_t **) calloc(2, sizeof(post_element_t *)); else post_elements = (post_element_t **) realloc(post_elements, sizeof(post_element_t *) * ((*post_elements_num) + 2)); post_elements[(*post_elements_num)] = calloc(1, sizeof(post_element_t)); post_elements[(*post_elements_num)]->post = post; post_elements[(*post_elements_num)]->name = strdup(plugin); #if 1 post_elements[(*post_elements_num)]->args = args ? strdup(args) : NULL; post_elements[(*post_elements_num)]->enable = 0; #endif (*post_elements_num)++; post_elements[(*post_elements_num)] = NULL; __pplugin_update_parameters(post, args); } free(plugin); } } free(freeme); } return post_elements; } static void pplugin_parse_and_store_post(fe_t *fe, int plugin_type, const char *post_chain) { post_element_t ***_post_elements; int *_post_elements_num; post_element_t **posts = NULL; int num; switch(plugin_type) { case XINE_POST_TYPE_VIDEO_FILTER: _post_elements = &fe->post_video_elements; _post_elements_num = &fe->post_video_elements_num; break; case XINE_POST_TYPE_VIDEO_COMPOSE: _post_elements = &fe->post_pip_elements; _post_elements_num = &fe->post_pip_elements_num; break; case XINE_POST_TYPE_AUDIO_VISUALIZATION: _post_elements = &fe->post_vis_elements; _post_elements_num = &fe->post_vis_elements_num; break; default: _post_elements = &fe->post_audio_elements; _post_elements_num = &fe->post_audio_elements_num; break; } if((posts = pplugin_parse_and_load(fe, plugin_type, post_chain, &num))) { if(*_post_elements_num) { int i; int ptot = *_post_elements_num + num; *_post_elements = (post_element_t **) realloc(*_post_elements, sizeof(post_element_t *) * (ptot + 1)); for(i = *_post_elements_num; i < ptot; i++) (*_post_elements)[i] = posts[i - *_post_elements_num]; (*_post_elements)[i] = NULL; (*_post_elements_num) += num; free(posts); } else { *_post_elements = posts; *_post_elements_num = num; } #if 1 if(SysLogLevel > 2) { /* dump list of all loaded plugins */ int ptot = *_post_elements_num; int i; char s[4096]=""; for(i=0; ipost) { if(((*_post_elements)[i])->enable) strcat(s, "*"); if(((*_post_elements)[i])->name) strcat(s, ((*_post_elements)[i])->name); else strcat(s, ""); strcat(s, " "); } LOGDBG(" loaded plugins (type %d.%d): %s", (plugin_type>>16), (plugin_type&0xffff), s); } #endif } } void vpplugin_parse_and_store_post(fe_t *fe, const char *post_chain) { pplugin_parse_and_store_post(fe, XINE_POST_TYPE_VIDEO_FILTER, post_chain); pplugin_parse_and_store_post(fe, XINE_POST_TYPE_VIDEO_COMPOSE, post_chain); } void applugin_parse_and_store_post(fe_t *fe, const char *post_chain) { pplugin_parse_and_store_post(fe, XINE_POST_TYPE_AUDIO_FILTER, post_chain); pplugin_parse_and_store_post(fe, XINE_POST_TYPE_AUDIO_VISUALIZATION, post_chain); } static void _vpplugin_unwire(fe_t *fe) { xine_post_out_t *vo_source; vo_source = xine_get_video_source(fe->video_source); (void) xine_post_wire_video_port(vo_source, fe->video_port); } static void _applugin_unwire(fe_t *fe) { xine_post_out_t *ao_source; ao_source = xine_get_audio_source(fe->audio_source); (void) xine_post_wire_audio_port(ao_source, fe->audio_port); } static void _vpplugin_rewire_from_post_elements(fe_t *fe, post_element_t **post_elements, int post_elements_num) { if(post_elements_num) { xine_post_out_t *vo_source; int i = 0; for(i = (post_elements_num - 1); i >= 0; i--) { const char *const *outs = xine_post_list_outputs(post_elements[i]->post); xine_post_out_t *vo_out = xine_post_output(post_elements[i]->post, *outs); if(i == (post_elements_num - 1)) { LOGDBG(" wiring %10s[out] -> [in]video_out", post_elements[i]->name); xine_post_wire_video_port(vo_out, fe->video_port); } else { xine_post_in_t *vo_in; /* look for standard input names */ vo_in = xine_post_input(post_elements[i + 1]->post, "video"); if( !vo_in ) vo_in = xine_post_input(post_elements[i + 1]->post, "video in"); LOGDBG(" wiring %10s[out] -> [in]%-10s ", post_elements[i]->name, post_elements[i+1]->name); xine_post_wire(vo_out, vo_in); } } if(fe->post_pip_enable && !strcmp(post_elements[0]->name, "mosaico") && fe->pip_stream) { vo_source = xine_get_video_source(fe->pip_stream); LOGDBG(" wiring %10s[out] -> [in1]%-10s ", "pip stream", post_elements[0]->name); xine_post_wire_video_port(vo_source, post_elements[0]->post->video_input[1]); } vo_source = xine_get_video_source(fe->video_source); LOGDBG(" wiring %10s[out] -> [in]%-10s", "stream", post_elements[0]->name); xine_post_wire_video_port(vo_source, post_elements[0]->post->video_input[0]); } } static void _applugin_rewire_from_post_elements(fe_t *fe, post_element_t **post_elements, int post_elements_num) { if(post_elements_num) { xine_post_out_t *ao_source; int i = 0; for(i = (post_elements_num - 1); i >= 0; i--) { const char *const *outs = xine_post_list_outputs(post_elements[i]->post); xine_post_out_t *ao_out = xine_post_output(post_elements[i]->post, *outs); if(i == (post_elements_num - 1)) { LOGDBG(" wiring %10s[out] -> [in]audio_out", post_elements[i]->name); xine_post_wire_audio_port(ao_out, fe->audio_port); } else { xine_post_in_t *ao_in; /* look for standard input names */ ao_in = xine_post_input(post_elements[i + 1]->post, "audio"); if( !ao_in ) ao_in = xine_post_input(post_elements[i + 1]->post, "audio in"); LOGDBG(" wiring %10s[out] -> [in]%-10s ", post_elements[i]->name, post_elements[i+1]->name); xine_post_wire(ao_out, ao_in); } } ao_source = xine_get_audio_source(fe->audio_source); LOGDBG(" wiring %10s[out] -> [in]%-10s", "stream", post_elements[0]->name); xine_post_wire_audio_port(ao_source, post_elements[0]->post->audio_input[0]); } } static post_element_t **_pplugin_join_deinterlace_and_post_elements(fe_t *fe, int *post_elements_num) { post_element_t **post_elements; int i = 0, j = 0, n = 0, p = 0; static const char *order[] = {"autocrop", "thread", "tvtime", "swscale", NULL}; *post_elements_num = 0; if( fe->post_video_enable ) *post_elements_num += fe->post_video_elements_num; if( fe->post_pip_enable ) *post_elements_num += fe->post_pip_elements_num; if( *post_elements_num == 0 ) return NULL; post_elements = calloc( (*post_elements_num), sizeof(post_element_t *)); if(fe->post_pip_enable) for( i = 0; i < fe->post_pip_elements_num; i++ ) { if(fe->post_pip_elements[i]->enable) post_elements[i+j-n] = fe->post_pip_elements[i]; else n++; } if(fe->post_video_enable) for( j = 0; j < fe->post_video_elements_num; j++ ) { if(fe->post_video_elements[j]->enable) { post_elements[i+j-n] = fe->post_video_elements[j]; } else n++; } *post_elements_num -= n; if( *post_elements_num == 0 ) { free(post_elements); return NULL; } /* in some special cases order is important. By default plugin order * in post plugin chain is post plugin loading order. * But, we want: * * 1. autocrop - less data to process for other plugins * - accepts only YV12 * 2. deinterlace - blur etc. makes deinterlacing difficult. * - upscales chroma (YV12->YUY2) in some modes. * 3. anything else * * So let's move those two to beginning ... */ n = 0; while(order[p]) { for(i = 0; i<*post_elements_num; i++) if(!strcmp(post_elements[i]->name, order[p])) { if(i != n) { post_element_t *tmp = post_elements[i]; for(j=i; j>n; j--) post_elements[j] = post_elements[j-1]; post_elements[n] = tmp; LOGDBG(" moved %s to post slot %d", order[p], n); } n++; break; } p++; } return post_elements; } static post_element_t **_pplugin_join_visualization_and_post_elements(fe_t *fe, int *post_elements_num) { post_element_t **post_elements; int i = 0, j = 0, n = 0; *post_elements_num = 0; if( fe->post_audio_enable ) *post_elements_num += fe->post_audio_elements_num; if( fe->post_vis_enable ) *post_elements_num += fe->post_vis_elements_num; if( *post_elements_num == 0 ) return NULL; post_elements = calloc( (*post_elements_num), sizeof(post_element_t *)); if(fe->post_audio_enable) for( j = 0; j < fe->post_audio_elements_num; j++ ) { if(fe->post_audio_elements[j]->enable) post_elements[i+j-n] = fe->post_audio_elements[j]; else n++; } if(fe->post_vis_enable) for( i = 0; i < fe->post_vis_elements_num; i++ ) { if(fe->post_vis_elements[i]->enable) post_elements[i+j-n] = fe->post_vis_elements[i]; else n++; } *post_elements_num -= n; if( *post_elements_num == 0 ) { free(post_elements); return NULL; } return post_elements; } static void _vpplugin_rewire(fe_t *fe) { static post_element_t **post_elements; int post_elements_num; post_elements = _pplugin_join_deinterlace_and_post_elements(fe, &post_elements_num); if( post_elements ) { _vpplugin_rewire_from_post_elements(fe, post_elements, post_elements_num); free(post_elements); } } static void _applugin_rewire(fe_t *fe) { static post_element_t **post_elements; int post_elements_num; post_elements = _pplugin_join_visualization_and_post_elements(fe, &post_elements_num); if( post_elements ) { _applugin_rewire_from_post_elements(fe, post_elements, post_elements_num); free(post_elements); } } void vpplugin_rewire_posts(fe_t *fe) { /*TRACELINE;*/ _vpplugin_unwire(fe); _vpplugin_rewire(fe); } void applugin_rewire_posts(fe_t *fe) { /*TRACELINE;*/ _applugin_unwire(fe); _applugin_rewire(fe); } static int _pplugin_enable_post(post_plugins_t *fe, const char *name, const char *args, post_element_t **post_elements, int post_elements_num, int *found) { int i, result = 0; for(i=0; iname, name)) { if(post_elements[i]->enable == 0) { post_elements[i]->enable = 1; result = 1; } *found = 1; if(args && *args) { if(post_elements[i]->enable != 2) { char *tmp = strdup(args); __pplugin_update_parameters(post_elements[i]->post, tmp); free(tmp); if(post_elements[i]->args) free(post_elements[i]->args); post_elements[i]->args = strdup(args); } else { LOGDBG(" * enable post %s, parameters fixed in command line.", name); LOGDBG(" requested: %s", args ? : "none"); LOGDBG(" using : %s", post_elements[i]->args ? : "none"); } } } return result; } static int _vpplugin_enable_post(post_plugins_t *fe, const char *name, const char *args, int *found) { int result = 0; if(!*found) result = _pplugin_enable_post(fe, name, args, fe->post_video_elements, fe->post_video_elements_num, found); if(!*found) result = _pplugin_enable_post(fe, name, args, fe->post_pip_elements, fe->post_pip_elements_num, found); return result; } static int _applugin_enable_post(post_plugins_t *fe, const char *name, const char *args, int *found) { int result = 0; if(!*found) result = _pplugin_enable_post(fe, name, args, fe->post_audio_elements, fe->post_audio_elements_num, found); if(!*found) result = _pplugin_enable_post(fe, name, args, fe->post_vis_elements, fe->post_vis_elements_num, found); return result; } static char * _pp_name_strdup(const char *initstr) { char *name = strdup(initstr), *pt; if(NULL != (pt = strchr(name, ':'))) *pt = 0; return name; } static const char * _pp_args(const char *initstr) { char *pt = strchr(initstr, ':'); if(pt && *(pt+1)) return pt+1; return NULL; } int vpplugin_enable_post(post_plugins_t *fe, const char *initstr, int *found) { char *name = _pp_name_strdup(initstr); const char *args = _pp_args(initstr); int result = _vpplugin_enable_post(fe, name, args, found); LOGDBG(" * enable post %s --> %s, %s", name, *found ? "found" : "not found", result ? "enabled" : "no action"); if(!*found) { LOGDBG(" * loading post %s", initstr); vpplugin_parse_and_store_post(fe, initstr); result = _vpplugin_enable_post(fe, name, NULL, found); LOGDBG(" * enable post %s --> %s, %s", name, *found ? "found" : "not found", result ? "enabled" : "no action"); } if(result) _vpplugin_unwire(fe); free(name); return result; } int applugin_enable_post(post_plugins_t *fe, const char *initstr, int *found) { const char * args = _pp_args(initstr); char *name = _pp_name_strdup(initstr); int result = _applugin_enable_post(fe, name, args, found); LOGDBG(" * enable post %s --> %s, %s", name, *found ? "found" : "not found", result ? "enabled" : "no action"); if(!*found) { LOGDBG(" * loading post %s", initstr); applugin_parse_and_store_post(fe, initstr); result = _applugin_enable_post(fe, name, NULL, found); LOGDBG(" * enable post %s --> %s, %s", name, *found ? "found" : "not found", result ? "enabled" : "no action"); } if(result) _applugin_unwire(fe); free(name); return result; } static int _pplugin_disable_post(post_plugins_t *fe, const char *name, post_element_t **post_elements, int post_elements_num) { int i, result = 0; /*TRACELINE;*/ if(post_elements) for(i = 0; i < post_elements_num; i++) if(post_elements[i]) if(!name || !strcmp(post_elements[i]->name, name)) if(post_elements[i]->enable == 1) { post_elements[i]->enable = 0; result = 1; } return result; } int vpplugin_disable_post(post_plugins_t *fe, const char *name) { /*TRACELINE;*/ if(_pplugin_disable_post(fe, name, fe->post_video_elements, fe->post_video_elements_num) || _pplugin_disable_post(fe, name, fe->post_pip_elements, fe->post_pip_elements_num)) { _vpplugin_unwire(fe); return 1; } return 0; } int applugin_disable_post(post_plugins_t *fe, const char *name) { /*TRACELINE;*/ if(_pplugin_disable_post(fe, name, fe->post_audio_elements, fe->post_audio_elements_num) || _pplugin_disable_post(fe, name, fe->post_vis_elements, fe->post_vis_elements_num)) { _applugin_unwire(fe); return 1; } return 0; } static int _pplugin_unload_post(post_plugins_t *fe, const char *name, post_element_t ***post_elements, int *post_elements_num) { /* does not unwrire plugins ! */ int i, j, result = 0; /*TRACELINE;*/ if(!*post_elements || !*post_elements_num) return 0; for(i=0; i < *post_elements_num; i++) { if((*post_elements)[i]) { if(!name || !strcmp((*post_elements)[i]->name, name)) { if((*post_elements)[i]->enable == 0 || !name) { xine_post_dispose(fe->xine, (*post_elements)[i]->post); free((*post_elements)[i]->name); if((*post_elements)[i]->args) free((*post_elements)[i]->args); free((*post_elements)[i]); for(j=i; j < *post_elements_num - 1; j++) (*post_elements)[j] = (*post_elements)[j+1]; (*post_elements_num) --; (*post_elements)[(*post_elements_num)] = 0; result = 1; i--; } else { LOGDBG("Unload %s failed: plugin enabled and in use", (*post_elements)[i]->name); } } } } if(*post_elements_num <= 0) { if(*post_elements) free(*post_elements); *post_elements = NULL; } return result; } int vpplugin_unload_post(post_plugins_t *fe, const char *name) { int result = vpplugin_disable_post(fe, name); /* unload already disabled plugins too (result=0) */ _pplugin_unload_post(fe, name, &fe->post_video_elements, &fe->post_video_elements_num); _pplugin_unload_post(fe, name, &fe->post_pip_elements, &fe->post_pip_elements_num); /* result indicates only unwiring condition, not unload result */ return result; } int applugin_unload_post(post_plugins_t *fe, const char *name) { int result = applugin_disable_post(fe, name); /* unload already disabled plugins too (result=0) */ _pplugin_unload_post(fe, name, &fe->post_audio_elements, &fe->post_audio_elements_num); _pplugin_unload_post(fe, name, &fe->post_vis_elements, &fe->post_vis_elements_num); /* result indicates only unwiring condition, not unload result */ return result; } /* end of post.c */ xineliboutput-2.0.0/xine/osd_manager.h0000644000175000017500000000152213061253352015617 0ustar phph/* * osd_manager.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_OSD_MANAGER_H_ #define XINELIBOUTPUT_OSD_MANAGER_H_ /* * OSD manager executes OSD control messages from VDR. * - cache OSD data * - scale OSD when required * - re-scale OSD when video size changes * - generate xine overlay events */ struct osd_command_s; typedef struct osd_manager_s osd_manager_t; struct osd_manager_s { int (*command)(osd_manager_t *, struct osd_command_s *, xine_stream_t *); void (*dispose)(osd_manager_t *, xine_stream_t *); void (*video_size_changed)(osd_manager_t *, xine_stream_t *, int width, int height); int (*argb_supported)(xine_stream_t *); }; osd_manager_t *init_osd_manager(void); #endif /* XINELIBOUTPUT_OSD_MANAGER_H_ */ xineliboutput-2.0.0/xine/osd_manager.c0000644000175000017500000007014113061253352015615 0ustar phph/* * osd_manager.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #define XINE_ENGINE_INTERNAL #include #include #include "../xine_input_vdr.h" #include "../tools/osd_command.h" #include "../tools/rle.h" #include "vo_props.h" #include "osd_manager.h" /*#define LOGOSD(x...) LOGMSG(x)*/ #define LOGOSD(x...) do {} while(0) static const char log_module_input_osd[] = "[input_osd] "; #define LOG_MODULENAME log_module_input_osd #define SysLogLevel iSysLogLevel #include "../logdefs.h" typedef struct { int handle; /* xine-lib overlay handle */ osd_command_t cmd; /* Full OSD data: last OSD_Set_RLE event */ uint16_t extent_width; /* output size this OSD was designed for */ uint16_t extent_height; uint16_t video_window_x; uint16_t video_window_y; uint16_t video_window_w; uint16_t video_window_h; int64_t last_changed_vpts; #ifdef VO_CAP_ARGB_LAYER_OVERLAY argb_layer_t *argb_layer; uint32_t *argb_buffer; #endif } osd_data_t; typedef struct osd_manager_impl_s { osd_manager_t mgr; pthread_mutex_t lock; uint8_t ticket_acquired; xine_stream_t *stream; uint16_t video_width; uint16_t video_height; uint16_t win_width; uint16_t win_height; uint8_t vo_scaling; osd_data_t osd[MAX_OSD_OBJECT]; } osd_manager_impl_t; /************************************* Tools ************************************/ /* * argb layer (originally copied from xine - why aren't these functions exported ?) */ #ifdef VO_CAP_ARGB_LAYER_OVERLAY static argb_layer_t *argb_layer_create() { argb_layer_t *argb_layer = (argb_layer_t *)calloc(1, sizeof (argb_layer_t)); pthread_mutex_init(&argb_layer->mutex, NULL); return argb_layer; } static void argb_layer_destroy(argb_layer_t *argb_layer) { pthread_mutex_destroy(&argb_layer->mutex); free(argb_layer); } static void set_argb_layer(argb_layer_t **dst, argb_layer_t *src) { if (src) { pthread_mutex_lock(&src->mutex); ++src->ref_count; pthread_mutex_unlock(&src->mutex); } if (*dst) { int free_argb_layer; pthread_mutex_lock(&(*dst)->mutex); free_argb_layer = (0 == --(*dst)->ref_count); pthread_mutex_unlock(&(*dst)->mutex); if (free_argb_layer) argb_layer_destroy(*dst); } *dst = src; } #endif /* VO_CAP_ARGB_LAYER_OVERLAY */ /* * acquire_ticket() */ static void acquire_ticket(osd_manager_impl_t *this) { if (!this->ticket_acquired) { this->stream->xine->port_ticket->acquire(this->stream->xine->port_ticket, 1); this->ticket_acquired = 1; } } static void release_ticket(osd_manager_impl_t *this) { if (this->ticket_acquired) { this->stream->xine->port_ticket->release(this->stream->xine->port_ticket, 1); this->ticket_acquired = 0; } } /* * get_overlay_manager() */ static video_overlay_manager_t *get_ovl_manager(osd_manager_impl_t *this) { /* Get overlay manager. We need ticket ... */ acquire_ticket(this); video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager(this->stream->video_out); if (!ovl_manager) { LOGMSG("Stream has no overlay manager !"); return NULL; } return ovl_manager; } /* * palette_argb_to_ayuv() */ #define saturate(x,min,max) ( (x)<(min) ? (min) : (x)>(max) ? (max) : (x)) static void palette_argb_to_ayuv(osd_clut_t *clut, int colors) { if (clut && colors>0) { int c; for (c=0; c> 8) + 16; int CR = (( + 112*R - 94*G - 18*B + 0x80) >> 8) + 128; int CB = (( - 38*R - 74*G + 112*B + 0x80) >> 8) + 128; clut[c].y = saturate( Y, 16, 235); clut[c].cb = saturate(CB, 16, 240); clut[c].cr = saturate(CR, 16, 240); } } } /* * clear_osdcmd() * * - free allocated memory from osd_command_t */ static void clear_osdcmd(osd_command_t *cmd) { free(cmd->data); cmd->data = NULL; free(cmd->palette); cmd->palette = NULL; } /* * osdcmd_to_overlay() * * - fill xine-lib vo_overlay_t from osd_command_t */ static void osdcmd_to_overlay(vo_overlay_t *ovl, osd_command_t *cmd) { unsigned i; ovl->rle = (rle_elem_t*)cmd->data; ovl->data_size = cmd->datalen; ovl->num_rle = cmd->datalen / 4; ovl->x = cmd->x; ovl->y = cmd->y; ovl->width = cmd->w; ovl->height = cmd->h; /* palette */ for (i=0; icolors; i++) { ovl->color[i] = (*(uint32_t*)(cmd->palette + i)) & 0x00ffffff; ovl->trans[i] = (cmd->palette[i].alpha + 0x7)/0xf; } ovl->rgb_clut = cmd->flags & OSDFLAG_YUV_CLUT ? 0 : 1; ovl->unscaled = cmd->flags & OSDFLAG_UNSCALED ? 1 : 0; ovl->hili_top = ovl->hili_bottom = ovl->hili_left = ovl->hili_right = -1; } /* * osdcmd_scale() * * Scale OSD_Set_RLE data * - modified fields: x, y, w, h, (RLE) data and datalen * - old RLE data is stored to osd_data_t *osd */ static void osdcmd_scale(osd_manager_impl_t *this, osd_command_t *cmd, osd_data_t *osd, int output_width, int output_height) { LOGOSD("Size out of margins, rescaling rle image"); /* new position and size */ int new_x = cmd->x * this->video_width / osd->extent_width; int new_y = cmd->y * this->video_height / osd->extent_height; int x2 = cmd->x + cmd->w + 1; int y2 = cmd->y + cmd->h + 1; x2 = ((x2+1) * this->video_width - 1) / osd->extent_width; y2 = ((y2+1) * this->video_height - 1) / osd->extent_height; int new_w = x2 - new_x - 1; int new_h = y2 - new_y - 1; /* store original RLE data */ osd->cmd.data = cmd->data; osd->cmd.datalen = cmd->datalen; /* scale */ int rle_elems = cmd->datalen / sizeof(osd_rle_elem_t); cmd->data = rle_scale_nearest(cmd->data, &rle_elems, cmd->w, cmd->h, new_w, new_h); cmd->datalen = rle_elems * sizeof(osd_rle_elem_t); cmd->x = new_x; cmd->y = new_y; cmd->w = new_w; cmd->h = new_h; } /* * osd_exec_vpts() * * - calculate execution time (vpts) for OSD update */ static int64_t osd_exec_vpts(osd_manager_impl_t *this, osd_command_t *cmd) { int64_t vpts = 0; /* now */ /* calculate exec time */ if (cmd->pts || cmd->delay_ms) { int64_t now = xine_get_current_vpts(this->stream); if (cmd->pts) vpts = cmd->pts + this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET); else vpts = this->osd[cmd->wnd].last_changed_vpts + cmd->delay_ms*90; /* execution time must be in future */ if (vpts < now) vpts = 0; /* limit delay to 5 seconds */ if (vpts > now + 5*90000) vpts = vpts + 5*90000; LOGOSD("OSD Command %d scheduled to +%dms", cmd->cmd, (int)(vpts>now ? vpts-now : 0)/90); } return vpts; } /***************************** OSD command handlers *****************************/ /* * exec_osd_size() * * - set the assumed full OSD size */ static int exec_osd_size(osd_manager_impl_t *this, osd_command_t *cmd) { osd_data_t *osd = &this->osd[cmd->wnd]; osd->extent_width = cmd->w; osd->extent_height = cmd->h; osd->video_window_x = 0; osd->video_window_y = 0; osd->video_window_w = 0; osd->video_window_h = 0; acquire_ticket(this); #ifdef VO_CAP_ARGB_LAYER_OVERLAY set_argb_layer(&osd->argb_layer, NULL); free(osd->argb_buffer); osd->argb_buffer = NULL; #endif xine_video_port_t *video_out = this->stream->video_out; this->vo_scaling = !!(video_out->get_capabilities(video_out) & VO_XCAP_OSDSCALING); return CONTROL_OK; } /* * exec_osd_video_window() * * - set video window inside OSD */ static int exec_osd_video_window(osd_manager_impl_t *this, osd_command_t *cmd) { osd_data_t *osd = &this->osd[cmd->wnd]; osd->video_window_x = cmd->x; osd->video_window_y = cmd->y; osd->video_window_w = cmd->w; osd->video_window_h = cmd->h; return CONTROL_OK; } /* * exec_osd_nop() * * - update last changed time of an OSD window */ static int exec_osd_nop(osd_manager_impl_t *this, osd_command_t *cmd) { this->osd[cmd->wnd].last_changed_vpts = xine_get_current_vpts(this->stream); return CONTROL_OK; } /* * exec_osd_flush() * * - commit all pending OSD events immediately */ static int exec_osd_flush(osd_manager_impl_t *this, osd_command_t *cmd) { video_overlay_manager_t *ovl_manager = get_ovl_manager(this); if (!ovl_manager) return CONTROL_PARAM_ERROR; ovl_manager->flush_events(ovl_manager); return CONTROL_OK; } /* * exec_osd_close() * */ static int exec_osd_close(osd_manager_impl_t *this, osd_command_t *cmd) { video_overlay_manager_t *ovl_manager = get_ovl_manager(this); osd_data_t *osd = &this->osd[cmd->wnd]; int handle = osd->handle; if (cmd->flags & OSDFLAG_REFRESH) { LOGDBG("Ignoring OSD_Close(OSDFLAG_REFRESH)"); return CONTROL_OK; } if (handle < 0) { LOGMSG("OSD_Close(%d): non-existing OSD !", cmd->wnd); return CONTROL_PARAM_ERROR; } if (!ovl_manager) return CONTROL_PARAM_ERROR; video_overlay_event_t ov_event = {0}; ov_event.event_type = OVERLAY_EVENT_FREE_HANDLE; ov_event.vpts = osd_exec_vpts(this, cmd); ov_event.object.handle = handle; while (ovl_manager->add_event(ovl_manager, (void *)&ov_event) < 0) { LOGMSG("OSD_Close(%d): overlay manager queue full !", cmd->wnd); ovl_manager->flush_events(ovl_manager); } clear_osdcmd(&osd->cmd); osd->handle = -1; osd->extent_width = 720; osd->extent_height = 576; osd->last_changed_vpts = 0; #ifdef VO_CAP_ARGB_LAYER_OVERLAY set_argb_layer(&osd->argb_layer, NULL); free(osd->argb_buffer); osd->argb_buffer = NULL; #endif return CONTROL_OK; } /* * exec_osd_set_rle() * */ static int exec_osd_set_rle(osd_manager_impl_t *this, osd_command_t *cmd) { video_overlay_manager_t *ovl_manager = get_ovl_manager(this); video_overlay_event_t ov_event = {0}; vo_overlay_t ov_overlay = {0}; osd_data_t *osd = &this->osd[cmd->wnd]; int use_unscaled = 0; int rle_scaled = 0; int unscaled_supported; int handle = osd->handle; if (!ovl_manager) return CONTROL_PARAM_ERROR; this->stream->video_out->enable_ovl(this->stream->video_out, 1); /* get / allocate OSD handle */ if (handle < 0) { handle = ovl_manager->get_handle(ovl_manager,0); osd->handle = handle; osd->extent_width = osd->extent_width ?: 720; osd->extent_height = osd->extent_height ?: 576; osd->last_changed_vpts = 0; } /* fill SHOW event */ ov_event.event_type = OVERLAY_EVENT_SHOW; ov_event.vpts = osd_exec_vpts(this, cmd); ov_event.object.handle = handle; ov_event.object.overlay = &ov_overlay; ov_event.object.object_type = 1; /* menu */ /* check for unscaled OSD capability and request */ xine_video_port_t *video_out = this->stream->video_out; unscaled_supported = !!(video_out->get_capabilities(video_out) & VO_CAP_UNSCALED_OVERLAY); if (unscaled_supported) { if (cmd->flags & OSDFLAG_UNSCALED) use_unscaled = 1; if (cmd->flags & (OSDFLAG_UNSCALED | OSDFLAG_UNSCALED_LOWRES)) { this->win_width = video_out->get_property(video_out, VO_PROP_WINDOW_WIDTH); this->win_height = video_out->get_property(video_out, VO_PROP_WINDOW_HEIGHT); } } /* store osd for later rescaling (done if video size changes) */ clear_osdcmd(&osd->cmd); memcpy(&osd->cmd, cmd, sizeof(osd_command_t)); osd->cmd.data = NULL; if (cmd->palette) { /* RGB -> YUV */ if(!(cmd->flags & OSDFLAG_YUV_CLUT)) palette_argb_to_ayuv(cmd->palette, cmd->colors); cmd->flags |= OSDFLAG_YUV_CLUT; osd->cmd.palette = malloc(sizeof(osd_clut_t)*cmd->colors); memcpy(osd->cmd.palette, cmd->palette, 4*cmd->colors); osd->cmd.flags |= OSDFLAG_YUV_CLUT; } /* request OSD scaling from video_out layer */ this->vo_scaling = !!(video_out->get_capabilities(video_out) & VO_XCAP_OSDSCALING); if (this->vo_scaling) { video_out->set_property(video_out, VO_PROP_OSD_SCALING, cmd->scaling ? 1 : 0); } /* if video size differs from expected (VDR osd is designed for 720x576), scale osd to video size or use unscaled (display resolution) blending */ /* scale OSD ? */ if (!this->vo_scaling && !use_unscaled) { int w_diff = (this->video_width < ((osd->extent_width *242) >> 8) /* 95% */) ? -1 : (this->video_width > ((osd->extent_width *280) >> 8) /* 110% */) ? 1 : 0; int h_diff = (this->video_height < ((osd->extent_height*242) >> 8) /* 95% */) ? -1 : (this->video_height > ((osd->extent_height*280) >> 8) /* 110% */) ? 1 : 0; if (w_diff || h_diff) { /* unscaled OSD instead of downscaling ? */ if (w_diff < 0 || h_diff < 0) if (cmd->flags & OSDFLAG_UNSCALED_LOWRES) if (0 < (use_unscaled = unscaled_supported)) { LOGOSD("Size out of margins, using unscaled overlay"); } if (!use_unscaled && cmd->scaling > 0) { osdcmd_scale(this, cmd, osd, this->video_width, this->video_height); rle_scaled = 1; } } } /* Scale unscaled OSD ? */ if (!this->vo_scaling && use_unscaled && cmd->scaling > 0) { if (this->win_width >= 360 && this->win_height >= 288) { if (this->win_width != osd->extent_width || this->win_height != osd->extent_height) { osdcmd_scale(this, cmd, osd, this->win_width, this->win_height); rle_scaled = 1; } } } /* fill ov_overlay */ osdcmd_to_overlay(&ov_overlay, cmd); ov_overlay.unscaled = use_unscaled; /* tag this overlay */ ov_overlay.hili_rgb_clut = VDR_OSD_MAGIC; /* fill extra data */ const vdr_osd_extradata_t extra_data = { extent_width: osd->extent_width, extent_height: osd->extent_height, layer: cmd->layer, scaling: cmd->scaling }; memcpy(ov_overlay.hili_color, &extra_data, sizeof(extra_data)); #ifdef VO_CAP_CUSTOM_EXTENT_OVERLAY if (cmd->scaling && !rle_scaled) { ov_overlay.extent_width = osd->extent_width; ov_overlay.extent_height = osd->extent_height; } #endif #ifdef VO_CAP_VIDEO_WINDOW_OVERLAY ov_overlay.video_window_x = osd->video_window_x ?: -1; ov_overlay.video_window_y = osd->video_window_y ?: -1; ov_overlay.video_window_width = osd->video_window_w ?: -1; ov_overlay.video_window_height = osd->video_window_h ?: -1; #endif /* if no scaling was required, we may still need to re-center OSD */ if (!this->vo_scaling && !rle_scaled) { if (!use_unscaled) { if (this->video_width != osd->extent_width) ov_overlay.x += (this->video_width - osd->extent_width)/2; if (this->video_height != osd->extent_height) ov_overlay.y += (this->video_height - osd->extent_height)/2; } else { if (this->win_width >= 360 && this->win_height >= 288) { if (this->win_width != osd->extent_width) ov_overlay.x += (this->win_width - osd->extent_width)/2; if (this->win_height != osd->extent_height) ov_overlay.y += (this->win_height - osd->extent_height)/2; } } } /* store rle for later scaling (done if video size changes) */ if (!rle_scaled /* if scaled, we already have a copy (original data) */ ) { osd->cmd.data = malloc(cmd->datalen); memcpy(osd->cmd.data, cmd->data, cmd->datalen); } cmd->data = NULL; /* we 'consume' data (ownership goes for xine-lib osd manager) */ /* send event to overlay manager */ while (ovl_manager->add_event(ovl_manager, (void *)&ov_event) < 0) { LOGMSG("OSD_Set_RLE(%d): overlay manager queue full !", cmd->wnd); ovl_manager->flush_events(ovl_manager); } osd->last_changed_vpts = ov_event.vpts ?: xine_get_current_vpts(this->stream); return CONTROL_OK; } static int exec_osd_set_lut8(osd_manager_impl_t *this, osd_command_t *cmd) { osd_command_t cmdrle; memcpy(&cmdrle, cmd, sizeof(osd_command_t)); if (cmd->data) { cmdrle.num_rle = rle_compress(&cmdrle.data, cmdrle.raw_data, cmdrle.w, cmdrle.h); cmdrle.datalen = 4 * cmdrle.num_rle; cmdrle.cmd = OSD_Set_RLE; } return exec_osd_set_rle(this, &cmdrle); } static int exec_osd_set_argb(osd_manager_impl_t *this, osd_command_t *cmd) { #ifdef VO_CAP_ARGB_LAYER_OVERLAY video_overlay_manager_t *ovl_manager = get_ovl_manager(this); video_overlay_event_t ov_event = {0}; vo_overlay_t ov_overlay = {0}; osd_data_t *osd = &this->osd[cmd->wnd]; int handle = osd->handle; if (!ovl_manager) return CONTROL_PARAM_ERROR; if (!this->mgr.argb_supported(this->stream)) { LOGMSG("ARGB overlay not supported by video driver"); return CONTROL_PARAM_ERROR; } if (osd->extent_width < 32 || osd->extent_height < 32) { LOGMSG("ARGB overlay: incorrect extent"); return CONTROL_PARAM_ERROR; } this->stream->video_out->enable_ovl(this->stream->video_out, 1); /* get / allocate OSD handle */ if (handle < 0) { handle = ovl_manager->get_handle(ovl_manager,0); osd->handle = handle; osd->extent_width = osd->extent_width ?: 720; osd->extent_height = osd->extent_height ?: 576; osd->last_changed_vpts = 0; } /* fill SHOW event */ ov_event.event_type = OVERLAY_EVENT_SHOW; ov_event.vpts = osd_exec_vpts(this, cmd); ov_event.object.handle = handle; ov_event.object.overlay = &ov_overlay; ov_event.object.object_type = 1; /* menu */ /* ARGB overlays are not cached for re-scaling */ clear_osdcmd(&osd->cmd); /* fill ov_overlay */ ov_overlay.x = 0; ov_overlay.y = 0; ov_overlay.width = osd->extent_width; ov_overlay.height = osd->extent_height; /* tag this overlay */ ov_overlay.hili_rgb_clut = VDR_OSD_MAGIC; /* fill extra data */ const vdr_osd_extradata_t extra_data = { extent_width: osd->extent_width, extent_height: osd->extent_height, layer: cmd->layer, scaling: cmd->scaling }; memcpy(ov_overlay.hili_color, &extra_data, sizeof(extra_data)); /* xine-lib 1.2 extensions */ ov_overlay.extent_width = osd->extent_width; ov_overlay.extent_height = osd->extent_height; ov_overlay.video_window_x = osd->video_window_x ?: -1; ov_overlay.video_window_y = osd->video_window_y ?: -1; ov_overlay.video_window_width = osd->video_window_w ?: -1; ov_overlay.video_window_height = osd->video_window_h ?: -1; /* this should trigger blending at output resolution (after scaling video frame) ... */ //ov_overlay.unscaled = 1; ov_overlay.unscaled = cmd->flags & OSDFLAG_UNSCALED ? 1 : 0; /* allocate buffer */ if (!osd->argb_buffer) { osd->argb_buffer = calloc(sizeof(uint32_t), (size_t)osd->extent_width * (size_t)osd->extent_height); } if (!osd->argb_layer) { set_argb_layer(&osd->argb_layer, argb_layer_create()); osd->argb_layer->buffer = osd->argb_buffer; osd->argb_layer->x1 = 0; osd->argb_layer->x2 = osd->extent_width - 1; osd->argb_layer->y1 = 0; osd->argb_layer->y2 = osd->extent_height - 1; } /* copy changed data to buffer */ /* TODO: do we need double-buffering or locking to prevent tearing ? */ uint32_t *src = (uint32_t*)cmd->raw_data; uint32_t* dst = osd->argb_buffer; int stride = cmd->w; int lines = cmd->h; /* clip */ if (cmd->x + cmd->w > osd->extent_width) { stride = osd->extent_width - cmd->x; if (stride < 0) stride = 0; LOGMSG("ARGB overlay: incorrect extent, cropping right side"); } if (cmd->y + cmd->h > osd->extent_height) { lines = osd->extent_height - cmd->y; LOGMSG("ARGB overlay: incorrect extent, cropping bottom"); } /* blend */ dst += cmd->y * osd->extent_width + cmd->x; for (; lines > 0; lines--) { memcpy(dst, src, stride * sizeof(uint32_t)); src += cmd->w; dst += osd->extent_width; } /* set dirty area. not used in opengl2 driver ... */ #if 0 /* this can't work... There's no way to know if driver has copied the dirty area to display. So dirty area can never be shrinked, just expanded ... */ osd->argb_layer->x1 = cmd->x; osd->argb_layer->x2 = cmd->x + cmd->w - 1; osd->argb_layer->y1 = cmd->y; osd->argb_layer->y2 = cmd->y + cmd->h - 1; #else /* argb layer update area accumulation */ #define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) osd->argb_layer->x1 = MIN( osd->argb_layer->x1, cmd->x ); osd->argb_layer->x2 = MAX( osd->argb_layer->x2, cmd->x + cmd->w - 1); osd->argb_layer->y1 = MIN( osd->argb_layer->y1, cmd->y ); osd->argb_layer->y2 = MAX( osd->argb_layer->y2, cmd->y + cmd->h - 1); #endif /* set buffer (ref-counted) */ set_argb_layer(&ov_overlay.argb_layer, osd->argb_layer); /* send event to overlay manager */ while (ovl_manager->add_event(ovl_manager, (void *)&ov_event) < 0) { LOGMSG("OSD_Set_ARGB(%d): overlay manager queue full !", cmd->wnd); ovl_manager->flush_events(ovl_manager); } /* release buffer (ref-counted) */ set_argb_layer(&ov_overlay.argb_layer, NULL); osd->last_changed_vpts = ov_event.vpts ?: xine_get_current_vpts(this->stream); return CONTROL_OK; #else /* VO_CAP_ARGB_LAYER_OVERLAY */ LOGMSG("OSD_Set_ARGB: plugin was build without ARGB support"); return CONTROL_PARAM_ERROR; #endif } /* * exec_osd_set_palette() * * - replace palette of an already existing OSD */ static int exec_osd_set_palette(osd_manager_impl_t *this, osd_command_t *cmd) { osd_data_t *osd = &this->osd[cmd->wnd]; if (!osd->cmd.data) { LOGMSG("OSD_SetPalette(%d): old RLE data missing !", cmd->wnd); return CONTROL_PARAM_ERROR; } if (!cmd->palette) { LOGMSG("OSD_SetPalette(%d): new palette missing !", cmd->wnd); return CONTROL_PARAM_ERROR; } /* use cached event to re-create Set_RLE command with modified palette */ osd_command_t tmp; /* steal the original command */ memcpy(&tmp, &osd->cmd, sizeof(osd_command_t)); memset(&osd->cmd, 0, sizeof(osd_command_t)); /* replace palette */ tmp.cmd = OSD_Set_RLE; free(tmp.palette); tmp.palette = malloc(cmd->colors * sizeof(osd_rle_elem_t)); memcpy(tmp.palette, cmd->palette, cmd->colors * sizeof(osd_rle_elem_t)); tmp.colors = cmd->colors; tmp.pts = cmd->pts; tmp.delay_ms = cmd->delay_ms; tmp.flags |= cmd->flags & OSDFLAG_YUV_CLUT; /* redraw */ int r = exec_osd_set_rle(this, &tmp); clear_osdcmd(&tmp); return r; } /* * exec_osd_move() * * - move existing OSD to new position */ static int exec_osd_move(osd_manager_impl_t *this, osd_command_t *cmd) { osd_data_t *osd = &this->osd[cmd->wnd]; if (!osd->cmd.data) { LOGMSG("OSD_Move(%d): old RLE data missing !", cmd->wnd); return CONTROL_PARAM_ERROR; } if (!osd->cmd.palette) { LOGMSG("OSD_Move(%d): old palette missing !", cmd->wnd); return CONTROL_PARAM_ERROR; } /* use cached event to re-create Set_RLE command with modified palette */ osd_command_t tmp; /* steal the original command */ memcpy(&tmp, &osd->cmd, sizeof(osd_command_t)); memset(&osd->cmd, 0, sizeof(osd_command_t)); /* replace position */ tmp.cmd = OSD_Set_RLE; tmp.x = cmd->x; tmp.y = cmd->y; tmp.pts = cmd->pts; tmp.delay_ms = cmd->delay_ms; /* redraw */ int r = exec_osd_set_rle(this, &tmp); clear_osdcmd(&tmp); return r; } /* * exec_osd_command_internal() */ static int exec_osd_command_internal(osd_manager_impl_t *this, struct osd_command_s *cmd) { LOGOSD("exec_osd_command %d", cmd->cmd); switch (cmd->cmd) { case OSD_Nop: return exec_osd_nop(this, cmd); case OSD_Size: return exec_osd_size(this, cmd); case OSD_SetPalette: return exec_osd_set_palette(this, cmd); case OSD_Move: return exec_osd_move(this, cmd); case OSD_Flush: return exec_osd_flush(this, cmd); case OSD_Set_RLE: return exec_osd_set_rle(this, cmd); case OSD_Set_LUT8: return exec_osd_set_lut8(this, cmd); case OSD_Set_ARGB: return exec_osd_set_argb(this, cmd); case OSD_Close: return exec_osd_close(this, cmd); case OSD_VideoWindow:return exec_osd_video_window(this, cmd); case OSD_Commit: /* All OSD areas have been updated, commit changes to display */ /* - not used with traditional xine-lib OSD */ return CONTROL_OK; case OSD_Set_YUV: /* TODO */ LOGMSG("OSD_Set_YUV not implemented !"); return CONTROL_PARAM_ERROR; default: LOGMSG("Unknown OSD command %d", cmd->cmd); return CONTROL_PARAM_ERROR; } return CONTROL_PARAM_ERROR; } /****************************** Interface handlers ******************************/ /* * exec_osd_command() * * - handler for VDR-based osd_command_t events */ static int exec_osd_command(osd_manager_t *this_gen, struct osd_command_s *cmd, xine_stream_t *stream) { osd_manager_impl_t *this = (osd_manager_impl_t*)this_gen; int result; if (!cmd || !stream) { LOGMSG("exec_osd_command: Stream not initialized !"); return CONTROL_DISCONNECTED; } if (cmd->wnd >= MAX_OSD_OBJECT) { LOGMSG("exec_osd_command: OSD window handle %d out of range !", cmd->wnd); return CONTROL_PARAM_ERROR; } if (pthread_mutex_lock(&this->lock)) { LOGERR("exec_osd_command: mutex lock failed"); return CONTROL_DISCONNECTED; } this->stream = stream; result = exec_osd_command_internal(this, cmd); release_ticket(this); pthread_mutex_unlock(&this->lock); return result; } /* * video_size_changed() */ static void video_size_changed(osd_manager_t *this_gen, xine_stream_t *stream, int width, int height) { osd_manager_impl_t *this = (osd_manager_impl_t*)this_gen; int i; if (!stream) { LOGMSG("video_size_changed: Stream not initialized !"); return; } if (width < 1 || height < 1) { LOGMSG("video_size_changed: Invalid video size %dx%d", width, height); return; } if (pthread_mutex_lock(&this->lock)) { LOGERR("video_size_changed: mutex lock failed"); return; } if (this->video_width == width && this->video_height == height) { pthread_mutex_unlock(&this->lock); return; } LOGOSD("New video size (%dx%d->%dx%d)", this->video_width, this->video_height, width, height); this->stream = stream; this->video_width = width; this->video_height = height; /* just call exec_osd_command for all stored osd's. scaling is done automatically if required. */ if (!this->vo_scaling) for (i = 0; i < MAX_OSD_OBJECT; i++) if (this->osd[i].handle >= 0 && this->osd[i].cmd.data && this->osd[i].cmd.scaling > 0) { osd_command_t tmp; memcpy(&tmp, &this->osd[i].cmd, sizeof(osd_command_t)); memset(&this->osd[i].cmd, 0, sizeof(osd_command_t)); exec_osd_command_internal(this, &tmp); clear_osdcmd(&tmp); } release_ticket(this); pthread_mutex_unlock(&this->lock); } static int argb_supported(xine_stream_t *stream) { #ifdef VO_CAP_ARGB_LAYER_OVERLAY xine_video_port_t *video_out = stream->video_out; return !!(video_out->get_capabilities(video_out) & VO_CAP_ARGB_LAYER_OVERLAY); #else return 0; #endif } /* * osd_manager_dispose() */ static void osd_manager_dispose(osd_manager_t *this_gen, xine_stream_t *stream) { osd_manager_impl_t *this = (osd_manager_impl_t*)this_gen; int i; while (pthread_mutex_destroy(&this->lock) == EBUSY) { LOGMSG("osd_manager_dispose: lock busy ..."); pthread_mutex_lock(&this->lock); pthread_mutex_unlock(&this->lock); } /* close all */ for (i=0; iosd[i].handle >= 0) { osd_command_t cmd = { .cmd = OSD_Close, .wnd = i, }; LOGOSD("Closing osd %d", i); exec_osd_close(this, &cmd); } } release_ticket(this); free(this); } /* * init_osd_manager() * * - exported */ osd_manager_t *init_osd_manager(void) { osd_manager_impl_t *this = calloc(1, sizeof(osd_manager_impl_t)); int i; this->mgr.command = exec_osd_command; this->mgr.dispose = osd_manager_dispose; this->mgr.video_size_changed = video_size_changed; this->mgr.argb_supported = argb_supported; pthread_mutex_init(&this->lock, NULL); this->video_width = 720; this->video_height = 576; for (i = 0; i < MAX_OSD_OBJECT; i++) this->osd[i].handle = -1; return &this->mgr; } xineliboutput-2.0.0/xine/demux_xvdr_tsdata.h0000644000175000017500000000147213061253352017071 0ustar phph/* * demux_xvdr_tsdata.h: data for MPEG-TS demuxer * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _DEMUX_XVDR_TSDATA_H_ #define _DEMUX_XVDR_TSDATA_H_ struct ts2es_s; struct ts_data_s { uint16_t pmt_pid; uint16_t program_number; pmt_data_t pmt; struct ts2es_s *video; struct ts2es_s *audio[TS_MAX_AUDIO_TRACKS]; struct ts2es_s *spu[TS_MAX_SPU_TRACKS]; }; typedef struct ts_data_s ts_data_t; void ts_data_ts2es_init (ts_data_t **ts_data, fifo_buffer_t *video_fifo, fifo_buffer_t *audio_fifo); void ts_data_flush (ts_data_t *ts_data); void ts_data_dispose (ts_data_t **ts_data); void ts_data_reset_audio(ts_data_t *ts_data, fifo_buffer_t *audio_fifo, int keep_channel); #endif /* _DEMUX_XVDR_TSDATA_H_ */ xineliboutput-2.0.0/xine/demux_xvdr_tsdata.c0000644000175000017500000000446113061253352017065 0ustar phph/* * demux_xvdr_tsdata.h: data for MPEG-TS demuxer * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #define LOG_MODULENAME "[demux_vdr] " #define SysLogLevel iSysLogLevel #include "../logdefs.h" #include "../tools/ts.h" #include "ts2es.h" #include "demux_xvdr_tsdata.h" static void ts_data_ts2es_reset(ts_data_t *ts_data) { int i; ts2es_dispose(ts_data->video); ts_data->video = NULL; for (i = 0; ts_data->audio[i]; i++) { ts2es_dispose(ts_data->audio[i]); ts_data->audio[i] = NULL; } for (i = 0; ts_data->spu[i]; i++) { ts2es_dispose(ts_data->spu[i]); ts_data->spu[i] = NULL; } } void ts_data_reset_audio(ts_data_t *ts_data, fifo_buffer_t *audio_fifo, int keep_channel) { if (ts_data) { int i; for (i = 0; ts_data->audio[i]; i++) { if (i != keep_channel) { ts2es_dispose(ts_data->audio[i]); ts_data->audio[i] = NULL; } } if (audio_fifo) { for (i = 0; i < ts_data->pmt.audio_tracks_count; i++) if (!ts_data->audio[i]) ts_data->audio[i] = ts2es_init(audio_fifo, ts_data->pmt.audio_tracks[i].type, i); } } } void ts_data_ts2es_init(ts_data_t **ts_data, fifo_buffer_t *video_fifo, fifo_buffer_t *audio_fifo) { if (*ts_data) ts_data_ts2es_reset(*ts_data); else *ts_data = calloc (1, sizeof(ts_data_t)); ts_data_t *this = *ts_data; int i; if (video_fifo) { if (this->pmt.video_pid != INVALID_PID) this->video = ts2es_init(video_fifo, this->pmt.video_type, 0); for (i = 0; i < this->pmt.spu_tracks_count; i++) this->spu[i] = ts2es_init(video_fifo, STREAM_DVBSUB, i); } if (audio_fifo) { for (i = 0; i < this->pmt.audio_tracks_count; i++) this->audio[i] = ts2es_init(audio_fifo, this->pmt.audio_tracks[i].type, i); } } void ts_data_flush(ts_data_t *ts_data) { if (ts_data) { int i; if (ts_data->video) ts2es_flush(ts_data->video); for (i = 0; ts_data->audio[i]; i++) ts2es_flush(ts_data->audio[i]); for (i = 0; ts_data->spu[i]; i++) ts2es_flush(ts_data->spu[i]); } } void ts_data_dispose(ts_data_t **ts_data) { if (*ts_data) { ts_data_ts2es_reset(*ts_data); free(*ts_data); *ts_data = NULL; } } xineliboutput-2.0.0/xine/demux_xvdr.c0000644000175000017500000010704213061253352015524 0ustar phph/* * Copyright (C) 2000-2006 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 * * demultiplexer for xineliboutput (xvdr) */ #include #include #include #include #include #include #include #include #include "../xine_input_vdr_mrl.h" #include "../tools/mpeg.h" #include "../tools/h264.h" #include "../tools/h265.h" #include "../tools/pes.h" #include "../tools/ts.h" #include "ts2es.h" #include "demux_xvdr_tsdata.h" #include "xvdr_metronom.h" /* * features */ #define VDR_SUBTITLES #define TEST_DVB_SPU #ifndef BUF_VIDEO_HEVC # warning xine-lib does not support HEVC #endif /* * logging */ static const char log_module_demux_xvdr[] = "[demux_vdr] "; #define LOG_MODULENAME log_module_demux_xvdr #define SysLogLevel iSysLogLevel #include "../logdefs.h" #define LOGSPU(x...) /* * constants */ #define WRAP_THRESHOLD 360000 /* 4 sec */ #define PTS_AUDIO 0 #define PTS_VIDEO 1 /* redefine abs as macro to handle 64-bit diffs. i guess llabs may not be available everywhere */ #define abs(x) ( ((x)<0) ? -(x) : (x) ) typedef struct demux_xvdr_s { demux_plugin_t demux_plugin; xine_stream_t *stream; fifo_buffer_t *audio_fifo; fifo_buffer_t *video_fifo; input_plugin_t *input; ts_data_t *ts_data; /* MPEG-TS stuff */ int64_t last_pts[2]; int64_t last_vpts; int status; uint32_t video_type; uint32_t audio_type; uint32_t subtitle_type; /* current buf_element */ int64_t pts; int64_t dts; uint32_t packet_len; uint8_t stream_id; uint8_t send_newpts : 1; uint8_t buf_flag_seek : 1; uint8_t ffmpeg_mpeg2_decoder : 1; uint8_t coreavc_h264_decoder : 1; uint8_t bih_posted : 1; } demux_xvdr_t ; typedef struct { demux_class_t demux_class; /* class-wide, global variables here */ xine_t *xine; config_values_t *config; } demux_xvdr_class_t; static const char * get_decoder_name(xine_t *xine, int buf_type) { uint32_t base = buf_type & 0xFF000000; uint32_t type = (buf_type >> 16) & 0xFF; plugin_node_t *node = NULL; if (base == BUF_VIDEO_BASE) node = xine->plugin_catalog->video_decoder_map[type][0]; else if (base == BUF_AUDIO_BASE) node = xine->plugin_catalog->audio_decoder_map[type][0]; if (node) { plugin_info_t *info = node->info; if (info) return info->id; } return ""; } #define LOG_DECODER(buf_type, name) \ LOGDBG("Using %-10s decoder \"%s\"", \ name, get_decoder_name(this->stream->xine, buf_type)); static void detect_video_decoders(demux_xvdr_t *this) { const char *name; name = get_decoder_name(this->stream->xine, BUF_VIDEO_MPEG); if (!strcmp(name, "ffmpegvideo")) this->ffmpeg_mpeg2_decoder = 1; LOGDBG("Using MPEG video decoder \"%s\"%s", name, this->ffmpeg_mpeg2_decoder ? " (FFmpeg)" : ""); name = get_decoder_name(this->stream->xine, BUF_VIDEO_H264); if (!strcmp(name, "dshowserver")) this->coreavc_h264_decoder = 1; LOGDBG("Using H.264 decoder \"%s\"%s", name, this->coreavc_h264_decoder ? " (dshowserver (CoreAVC))" : ""); #ifdef BUF_VIDEO_HEVC LOG_DECODER(BUF_VIDEO_HEVC, "HEVC"); #endif LOG_DECODER(BUF_VIDEO_VC1, "VC-1"); } static void detect_audio_decoders(demux_xvdr_t *this) { LOG_DECODER(BUF_AUDIO_MPEG, "MPEG audio"); LOG_DECODER(BUF_AUDIO_A52, "AC-3"); LOG_DECODER(BUF_AUDIO_AAC, "AAC"); LOG_DECODER(BUF_AUDIO_DTS, "DTS"); LOG_DECODER(BUF_AUDIO_LPCM_BE, "LPCM"); #ifdef BUF_AUDIO_EAC3 LOG_DECODER(BUF_AUDIO_EAC3, "E-AC-3"); #endif #ifdef BUF_AUDIO_AAC_LATM LOG_DECODER(BUF_AUDIO_AAC_LATM, "AAC LATM"); #endif } static void pts_wrap_workaround(demux_xvdr_t *this, buf_element_t *buf, int video) { /* PTS wrap workaround */ if (buf->pts > 0) { #ifdef LOG_PES_AV_DIFF static int64_t vpts = 0, apts = 0; if (video) vpts = buf->pts; else apts = buf->pts; if (vpts > 0 && apts > 0) LOGMSG("pts diff [%d] %d", video, (int)(vpts - apts)); #endif if (video) { /* VIDEO wrap in middle of GOP */ if (this->last_vpts < 14400 && this->last_vpts > 0 && buf->pts > (INT64_C(0x1ffffffff) - 14400) && !this->send_newpts) { LOGMSG("VIDEO pts wrap in middle of GOP, ignoring video pts %" PRId64, buf->pts); buf->pts = INT64_C(0); return; } this->last_vpts = buf->pts; return; } /* VIDEO wrap before AUDIO wrap */ else if (buf->pts > INT64_C( 0x40400000 ) && this->last_vpts < INT64_C( 0x40000000 ) && this->last_vpts > INT64_C( 0 )) { LOGMSG("VIDEO pts wrap before AUDIO, ignoring audio pts %" PRId64, buf->pts); buf->pts = INT64_C(0); } } } static void check_newpts(demux_xvdr_t *this, buf_element_t *buf, int video ) { if (buf->pts <= 0) return; if (video) { int still_mode = (int)this->stream->metronom->get_option(this->stream->metronom, XVDR_METRONOM_STILL_MODE); int trick_speed = (int)this->stream->metronom->get_option(this->stream->metronom, XVDR_METRONOM_TRICK_SPEED); if (still_mode > 0 || trick_speed > 0) { LOGMSG("Skipping new pts %"PRId64" (still=%d trickspeed=%d)", buf->pts, still_mode, trick_speed); return; } } pts_wrap_workaround(this, buf, video); if (buf->pts) { int64_t diff = buf->pts - this->last_pts[video]; if (this->send_newpts || (this->last_pts[video] && abs(diff)>WRAP_THRESHOLD)) { LOGVERBOSE("New PTS: %"PRId64" (%s)", buf->pts, video ? "VIDEO" : "AUDIO"); if (this->buf_flag_seek) { _x_demux_control_newpts(this->stream, buf->pts, BUF_FLAG_SEEK); this->buf_flag_seek = 0; } else { _x_demux_control_newpts(this->stream, buf->pts, 0); } this->send_newpts = 0; } this->last_pts[video] = buf->pts; this->last_pts[1-video] = buf->pts; } } static void put_control_buf(fifo_buffer_t *buffer, fifo_buffer_t *pool, int cmd) { buf_element_t *buf = pool->buffer_pool_try_alloc(pool); if(buf) { buf->type = cmd; buffer->put(buffer, buf); } else { LOGERR("put_control_buf(0x%08x): get_buf_element() failed !", (unsigned)cmd); } } /* * post_sequence_end() * * Add sequence end code to fifo buffer */ static void post_sequence_end(fifo_buffer_t *fifo, uint32_t video_type) { uint8_t code; switch (video_type) { case BUF_VIDEO_MPEG: code = SC_SEQUENCE_END; break; case BUF_VIDEO_H264: code = NAL_END_SEQ; break; case BUF_VIDEO_VC1: code = NAL_END_SEQ; break; #ifdef BUF_VIDEO_HEVC case BUF_VIDEO_HEVC: code = (H265_NAL_EOB_NUT << 1); break; #endif default: return; } buf_element_t *buf = fifo->buffer_pool_try_alloc(fifo); if (buf) { buf->type = video_type; buf->size = 4; buf->decoder_flags = BUF_FLAG_FRAME_END; buf->content[0] = 0x00; buf->content[1] = 0x00; buf->content[2] = 0x01; buf->content[3] = code; #ifdef BUF_VIDEO_HEVC if (video_type == BUF_VIDEO_HEVC) { buf->size++; buf->content[4] = 0x01; /* layer_id 0, temporal_id 1 */ } #endif fifo->put(fifo, buf); } else { LOGERR("post_sequence_end(): get_buf_element() failed !"); } } static void track_audio_stream_change(demux_xvdr_t *this, buf_element_t *buf) { #if !defined(BUF_CONTROL_RESET_TRACK_MAP) # warning xine-lib is older than 1.1.2. Multiple audio streams are not supported. #else if (this->audio_type != buf->type) { LOGDBG("audio stream changed: %08x -> %08x", this->audio_type, buf->type); ts_data_reset_audio(this->ts_data, this->audio_fifo, buf->type & 0xff); put_control_buf(this->audio_fifo, this->audio_fifo, BUF_CONTROL_RESET_TRACK_MAP); if (this->audio_type) { buf_element_t *b = this->audio_fifo->buffer_pool_try_alloc(this->audio_fifo); if (b) { b->type = BUF_CONTROL_START; b->decoder_flags = BUF_FLAG_GAPLESS_SW; this->audio_fifo->put(this->audio_fifo, b); } } this->audio_type = buf->type; } #endif } static void demux_xvdr_fwd_buf(demux_xvdr_t *this, buf_element_t *buf) { /* demuxed video --> video_fifo */ if ((buf->type & BUF_MAJOR_MASK) == BUF_VIDEO_BASE) { this->video_type = buf->type; check_newpts (this, buf, PTS_VIDEO); this->video_fifo->put (this->video_fifo, buf); return; } /* demuxed audio --> audio_fifo */ if ((buf->type & BUF_MAJOR_MASK) == BUF_AUDIO_BASE) { if (this->audio_fifo) { check_newpts (this, buf, PTS_AUDIO); track_audio_stream_change (this, buf); this->audio_fifo->put (this->audio_fifo, buf); } else { buf->free_buffer (buf); } return; } /* decoder flush --> video_fifo */ if (buf->type == BUF_CONTROL_FLUSH_DECODER) { /* append sequence end code */ post_sequence_end (this->video_fifo, this->video_type); this->video_fifo->put (this->video_fifo, buf); return; } /* control buffer --> both fifos */ if ((buf->type & BUF_MAJOR_MASK) == BUF_CONTROL_BASE) { if (this->audio_fifo) { /* duplicate goes to audio fifo */ buf_element_t *cbuf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); cbuf->type = buf->type; cbuf->decoder_flags = buf->decoder_flags; memcpy (cbuf->decoder_info, buf->decoder_info, sizeof(cbuf->decoder_info)); memcpy (cbuf->decoder_info_ptr, buf->decoder_info_ptr, sizeof(cbuf->decoder_info_ptr)); this->audio_fifo->put (this->audio_fifo, cbuf); } this->video_fifo->put (this->video_fifo, buf); return; } LOGMSG("Unhandled buffer type %08x", buf->type); buf->free_buffer (buf); } /* * demux_xvdr_parse_ts() * * MPEG-TS demuxing */ static unsigned int vtype_to_xine_buf_type(unsigned vtype) { switch (vtype) { case ISO_11172_VIDEO: return BUF_VIDEO_MPEG; case ISO_13818_VIDEO: return BUF_VIDEO_MPEG; case ISO_14496_PART2_VIDEO: return BUF_VIDEO_MPEG4; case ISO_14496_PART10_VIDEO: return BUF_VIDEO_H264; case STREAM_VIDEO_VC1: return BUF_VIDEO_VC1; #ifdef BUF_VIDEO_HEVC case STREAM_VIDEO_HEVC: return BUF_VIDEO_HEVC; #endif } return 0; } static void demux_xvdr_parse_ts (demux_xvdr_t *this, buf_element_t *buf) { if (!this->ts_data) this->ts_data = calloc(1, sizeof(ts_data_t)); ts_data_t *ts_data = this->ts_data; fifo_buffer_t *src_fifo = buf->source; while (buf->size >= TS_SIZE) { unsigned int ts_pid = ts_PID(buf->content); /* parse PAT */ if (ts_pid == 0) { pat_data_t pat = {{ 0 }}; if (ts_parse_pat(&pat, buf->content)) { if (ts_data->pmt_pid != pat.pmt_pid[0] || ts_data->program_number != pat.program_number[0]) { LOGVERBOSE("PAT: program changed, flushing demuxer"); ts_data_flush(ts_data); ts_data->pmt_pid = pat.pmt_pid[0]; ts_data->program_number = pat.program_number[0]; } else { LOGVERBOSE("Got PAT, PMT pid = %d, program = %d", ts_data->pmt_pid, ts_data->program_number); } } } /* parse PMT */ else if (ts_pid == ts_data->pmt_pid) { if (ts_parse_pmt(&ts_data->pmt, ts_data->program_number, buf->content)) { /* PMT changed, reset ts->es converters */ LOGDBG("PMT changed, resetting demuxer"); ts_data_ts2es_init(&ts_data, this->stream->video_fifo, this->stream->audio_fifo); this->video_type = vtype_to_xine_buf_type(ts_data->pmt.video_type); if (!this->video_type) { LOGMSG("unsupported video codec 0x%02x detected (no support in xine-lib ?)", ts_data->pmt.video_type); ts_data->pmt.video_pid = INVALID_PID; } /* Inform UI of channels changes */ xine_event_t event; event.type = XINE_EVENT_UI_CHANNELS_CHANGED; event.data_length = 0; xine_event_send(this->stream, &event); } } /* demux video */ else if (ts_pid == ts_data->pmt.video_pid) { if (ts_data->video) { buf_element_t *vbuf = ts2es_put(ts_data->video, buf->content, src_fifo); if (vbuf) { check_newpts( this, vbuf, PTS_VIDEO ); this->stream->video_fifo->put(this->stream->video_fifo, vbuf); } } } /* demux audio */ else { int i/*, done = 0*/; for (i=0; i < ts_data->pmt.audio_tracks_count; i++) if (ts_pid == ts_data->pmt.audio_tracks[i].pid) { if (ts_data->audio[i]) { buf_element_t *abuf = ts2es_put(ts_data->audio[i], buf->content, src_fifo); if (abuf) { check_newpts( this, abuf, PTS_AUDIO ); track_audio_stream_change (this, abuf); this->stream->audio_fifo->put(this->stream->audio_fifo, abuf); } } /*done = 1;*/ break; } #if 0 /* demux subtitles */ if (!done) for (i=0; i < ts_data->pmt.spu_tracks_count; i++) if (ts_pid == ts_data->pmt.spu_tracks[i].pid) { if (ts_data->spu[i]) { buf_element_t *sbuf = ts2es_put(ts_data->spu[i], buf->content, NULL); if (sbuf) this->stream->video_fifo->put(this->stream->video_fifo, sbuf); } done = 1; break; } if (!done) LOGMSG("Got unknown TS packet, pid = %d", ts_pid); #endif } buf->content += TS_SIZE; buf->size -= TS_SIZE; } buf->free_buffer(buf); } /* * PES demuxing */ /* * post_frame_end() * * Signal end of video frame to decoder. * * This function is used with: * - FFmpeg mpeg2 decoder * - FFmpeg and CoreAVC H.264 decoders * - NOT with libmpeg2 mpeg decoder */ static void post_frame_end(demux_xvdr_t *this, buf_element_t *vid_buf) { buf_element_t *cbuf = this->video_fifo->buffer_pool_try_alloc (this->video_fifo) ?: this->audio_fifo->buffer_pool_try_alloc (this->audio_fifo); if (!cbuf) { LOGMSG("post_frame_end(): buffer_pool_try_alloc() failed, retrying"); xine_usec_sleep (10*1000); cbuf = this->video_fifo->buffer_pool_try_alloc (this->video_fifo); if (!cbuf) { LOGERR("post_frame_end(): get_buf_element() failed !"); return; } } cbuf->type = this->video_type; cbuf->decoder_flags = BUF_FLAG_FRAME_END; if (!this->bih_posted) { video_size_t size = {0}; if (pes_get_video_size(vid_buf->content, vid_buf->size, &size, this->video_type == BUF_VIDEO_H264)) { /* reset decoder buffer */ cbuf->decoder_flags |= BUF_FLAG_FRAME_START; /* Fill xine_bmiheader for CoreAVC / H.264 */ if (this->video_type == BUF_VIDEO_H264 && this->coreavc_h264_decoder) { xine_bmiheader *bmi = (xine_bmiheader*) cbuf->content; cbuf->decoder_flags |= BUF_FLAG_HEADER; cbuf->decoder_flags |= BUF_FLAG_STDHEADER; /* CoreAVC: buffer contains bmiheader */ cbuf->size = sizeof(xine_bmiheader); memset (bmi, 0, sizeof(xine_bmiheader)); bmi->biSize = sizeof(xine_bmiheader); bmi->biWidth = size.width; bmi->biHeight = size.height; bmi->biPlanes = 1; bmi->biBitCount = 24; bmi->biCompression = 0x34363248; bmi->biSizeImage = 0; bmi->biXPelsPerMeter = size.pixel_aspect.num; bmi->biYPelsPerMeter = size.pixel_aspect.den; bmi->biClrUsed = 0; bmi->biClrImportant = 0; } /* Set aspect ratio for ffmpeg mpeg2 / CoreAVC H.264 decoder * (not for FFmpeg H.264 or libmpeg2 mpeg2 decoders) */ if (size.pixel_aspect.num && (this->video_type != BUF_VIDEO_H264 || this->coreavc_h264_decoder)) { cbuf->decoder_flags |= BUF_FLAG_HEADER; cbuf->decoder_flags |= BUF_FLAG_ASPECT; /* pixel ratio -> frame ratio */ if (size.pixel_aspect.num > size.height) { cbuf->decoder_info[1] = size.pixel_aspect.num / size.height; cbuf->decoder_info[2] = size.pixel_aspect.den / size.width; } else { cbuf->decoder_info[1] = size.pixel_aspect.num * size.width; cbuf->decoder_info[2] = size.pixel_aspect.den * size.height; } } LOGDBG("post_frame_end: video width %d, height %d, pixel aspect %d:%d", size.width, size.height, size.pixel_aspect.num, size.pixel_aspect.den); this->bih_posted = 1; } } this->video_fifo->put (this->video_fifo, cbuf); } /* FIXME: Extension data is not parsed, and is also not skipped. */ static int32_t parse_pes_for_pts(demux_xvdr_t *this, uint8_t *p, buf_element_t *buf) { int32_t header_len; this->packet_len = p[4] << 8 | p[5]; if ((p[6] & 0xC0) != 0x80 /* mpeg1 */) { header_len = 6; p += 6; /* packet_len -= 6; */ while ((p[0] & 0x80) == 0x80) { p++; header_len++; this->packet_len--; } if ((p[0] & 0xc0) == 0x40) { /* STD_buffer_scale, STD_buffer_size */ p += 2; header_len += 2; this->packet_len -= 2; } this->pts = 0; this->dts = 0; if ((p[0] & 0xf0) == 0x20) { this->pts = (int64_t)(p[ 0] & 0x0E) << 29 ; this->pts |= p[ 1] << 22 ; this->pts |= (p[ 2] & 0xFE) << 14 ; this->pts |= p[ 3] << 7 ; this->pts |= (p[ 4] & 0xFE) >> 1 ; p += 5; header_len+= 5; this->packet_len -=5; return header_len; } else if ((p[0] & 0xf0) == 0x30) { this->pts = (int64_t)(p[ 0] & 0x0E) << 29 ; this->pts |= p[ 1] << 22 ; this->pts |= (p[ 2] & 0xFE) << 14 ; this->pts |= p[ 3] << 7 ; this->pts |= (p[ 4] & 0xFE) >> 1 ; this->dts = (int64_t)(p[ 5] & 0x0E) << 29 ; this->dts |= p[ 6] << 22 ; this->dts |= (p[ 7] & 0xFE) << 14 ; this->dts |= p[ 8] << 7 ; this->dts |= (p[ 9] & 0xFE) >> 1 ; p += 10; header_len += 10; this->packet_len -= 10; return header_len; } else { p++; header_len++; this->packet_len--; return header_len; } } else { /* mpeg 2 */ if ((p[6] & 0xC0) != 0x80) { LOGMSG("warning: PES header reserved 10 bits not found"); buf->free_buffer(buf); return -1; } /* check PES scrambling_control */ if ((p[6] & 0x30) != 0) { LOGMSG("encrypted PES ?"); buf->free_buffer(buf); return -1; } if (p[7] & 0x80) { /* pts avail */ this->pts = (int64_t)(p[ 9] & 0x0E) << 29 ; this->pts |= p[10] << 22 ; this->pts |= (p[11] & 0xFE) << 14 ; this->pts |= p[12] << 7 ; this->pts |= (p[13] & 0xFE) >> 1 ; } else this->pts = 0; if (p[7] & 0x40) { /* dts avail */ this->dts = (int64_t)(p[14] & 0x0E) << 29 ; this->dts |= p[15] << 22 ; this->dts |= (p[16] & 0xFE) << 14 ; this->dts |= p[17] << 7 ; this->dts |= (p[18] & 0xFE) >> 1 ; } else this->dts = 0; header_len = p[8]; this->packet_len -= header_len + 3; return header_len + 9; } return 0; } #if defined(TEST_DVB_SPU) /* * parse_dvb_spu() * * DVB subtitle stream demuxing */ static int32_t parse_dvb_spu(demux_xvdr_t *this, uint8_t *p, buf_element_t *buf, int substream_header_len) { unsigned spu_id = p[0] & 0x1f; _x_select_spu_channel(this->stream, spu_id); # ifdef VDR_SUBTITLES if (substream_header_len == 1) { p--; this->packet_len++; } # endif /* VDR_SUBTITLES */ /* Skip substream header */ p += substream_header_len; buf->content = p; buf->size = this->packet_len - substream_header_len; /* Special buffer when payload packet changes */ if (this->pts > 0) { buf_element_t *cbuf = this->video_fifo->buffer_pool_alloc(this->video_fifo); int page_id = (*(p+4) << 8) | *(p+5); spu_dvb_descriptor_t *spu_descriptor = (spu_dvb_descriptor_t *) cbuf->content; memset(spu_descriptor, 0, sizeof(spu_dvb_descriptor_t)); spu_descriptor->comp_page_id = page_id; cbuf->type = BUF_SPU_DVB + spu_id; cbuf->size = 0; cbuf->decoder_flags = BUF_FLAG_SPECIAL; cbuf->decoder_info[1] = BUF_SPECIAL_SPU_DVB_DESCRIPTOR; cbuf->decoder_info[2] = sizeof(spu_dvb_descriptor_t); cbuf->decoder_info_ptr[2] = spu_descriptor; this->video_fifo->put (this->video_fifo, cbuf); } buf->type = BUF_SPU_DVB + spu_id; buf->pts = this->pts; buf->decoder_info[2] = this->pts > 0 ? 0xffff : 0; /* hack - size unknown here (?) */ this->video_fifo->put (this->video_fifo, buf); return -1; } static int detect_dvb_spu(demux_xvdr_t *this, uint8_t *p, buf_element_t *buf) { LOGSPU("%s%02x %02x %02x %02x %02x %02x %02x %02x", this->pts>0?"* ":" ",p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); /* If PES packet has PTS, it starts new subtitle (ES) packet. */ if (this->pts > 0) { /* Reset SPU type */ this->subtitle_type = 0; } # ifdef VDR_SUBTITLES /* Compatibility mode for old subtitles plugin */ if (this->subtitle_type != BUF_SPU_DVD) { if ((buf->content[7] & 0x01) && (p[-3] & 0x81) == 0x01 && p[-2] == 0x81) { LOGDBG("DVB SPU: Old vdr-subtitles compability mode"); return parse_dvb_spu(this, p, buf, 1); } } # endif /* VDR_SUBTITLES */ /* Start of subtitle packet. Guess substream type */ if (this->pts > 0) { if (p[4] == 0x20 && p[5] == 0x00 && (p[6] == 0x0f || p[4] == 0x0f)) { this->subtitle_type = BUF_SPU_DVB; LOGSPU(" -> DVB SPU"); } else if (p[2] || (p[3] & 0xfe)) { this->subtitle_type = BUF_SPU_DVD; LOGSPU(" -> DVD SPU"); } else { this->subtitle_type = BUF_SPU_DVD; LOGMSG(" -> DV? SPU -> DVD"); } } /* DVD SPU ? */ if (this->subtitle_type == BUF_SPU_DVD) return this->packet_len; /* DVB SPU */ return parse_dvb_spu(this, p, buf, 4); } #endif /* TEST_DVB_SPU */ static int32_t parse_private_stream_1(demux_xvdr_t *this, uint8_t *p, buf_element_t *buf) { int track, spu_id; int32_t result; result = parse_pes_for_pts(this, p, buf); if (result < 0) return -1; p += result; /* SPU */ if ((p[0] & 0xE0) == 0x20) { spu_id = (p[0] & 0x1f); if (this->pts <= 0 && !this->subtitle_type) { /* need whole ES packet (after seek etc.) */ buf->free_buffer(buf); return -1; } #ifdef TEST_DVB_SPU if (detect_dvb_spu(this, p, buf) < 0) return -1; #endif /* TEST_DVB_SPU */ this->subtitle_type = BUF_SPU_DVD; /* DVD SPU */ buf->content = p+1; buf->size = this->packet_len-1; buf->type = BUF_SPU_DVD + spu_id; buf->decoder_flags |= BUF_FLAG_SPECIAL; buf->decoder_info[1] = BUF_SPECIAL_SPU_DVD_SUBTYPE; buf->decoder_info[2] = SPU_DVD_SUBTYPE_PACKAGE; buf->pts = this->pts; this->video_fifo->put (this->video_fifo, buf); return -1; } if ((p[0]&0xF0) == 0x80) { track = p[0] & 0x0F; /* hack : ac3 track */ buf->decoder_info[1] = p[1]; /* Number of frame headers */ buf->decoder_info[2] = p[2] << 8 | p[3]; /* First access unit pointer */ buf->content = p+4; buf->size = this->packet_len-4; if (track & 0x8) { buf->type = BUF_AUDIO_DTS + (track & 0x07); /* DVDs only have 8 tracks */ } else { buf->type = BUF_AUDIO_A52 + track; } buf->pts = this->pts; if (this->audio_fifo) { check_newpts( this, buf, PTS_AUDIO ); track_audio_stream_change (this, buf); this->audio_fifo->put (this->audio_fifo, buf); return -1; } else { buf->free_buffer(buf); return -1; } } else if ((p[0]&0xf0) == 0xa0) { track = p[0] & 0x0F; /* send lpcm config byte */ buf->decoder_flags |= BUF_FLAG_SPECIAL; buf->decoder_info[1] = BUF_SPECIAL_LPCM_CONFIG; buf->decoder_info[2] = p[5]; buf->content = p + 7; buf->size = this->packet_len - 7; buf->type = BUF_AUDIO_LPCM_BE + track; buf->pts = this->pts; if (this->audio_fifo) { check_newpts( this, buf, PTS_AUDIO ); track_audio_stream_change (this, buf); this->audio_fifo->put (this->audio_fifo, buf); return -1; } else { buf->free_buffer(buf); return -1; } } buf->free_buffer(buf); return -1; } /* * detect_h264() * * Detect video codec (MPEG2 or H.264) */ static int detect_h264(uint8_t *data) { /* H.264 detection */ if (data[0] == 0 && data[1] == 0 && data[2] == 1) { if (data[3] == NAL_AUD) { LOGMSG("H.264 scanner: Possible H.264 NAL AUD"); return BUF_VIDEO_H264; } if (data[3] == 0) { LOGDBG("H.264 scanner: Possible MPEG2 start code PICTURE (0x00)"); return BUF_VIDEO_MPEG; } if (data[3] >= 0x80) { LOGDBG("H.264 scanner: Possible MPEG2 start code (0x%02x)", data[3]); return BUF_VIDEO_MPEG; } LOGMSG("H.264 scanner: Unregonized header 00 00 01 %02x", data[3]); } return 0; } static int32_t parse_video_stream(demux_xvdr_t *this, uint8_t *p, buf_element_t *buf) { int32_t result; result = parse_pes_for_pts(this, p, buf); if (result < 0) return -1; /* Handle marker packets after still images */ if (buf->size == 14 && result == 14 && this->pts == 0) { /*if (this->stream->metronom->get_option(this->stream->metronom, XVDR_METRONOM_STILL_MODE))*/ { LOGDBG("video fifo flush and decoder reset after still image"); put_control_buf(this->video_fifo, this->video_fifo, BUF_CONTROL_FLUSH_DECODER); put_control_buf(this->video_fifo, this->video_fifo, BUF_CONTROL_RESET_DECODER); buf->free_buffer(buf); return -1; } } p += result; if (this->video_type == 0 && buf->size >= result + 4) { this->video_type = detect_h264(p); } buf->type = this->video_type ?: BUF_VIDEO_MPEG; buf->pts = this->pts; buf->decoder_info[0] = this->pts - this->dts; /* MPEG2 */ if (this->video_type == BUF_VIDEO_MPEG) { /* Special handling for FFMPEG MPEG2 decoder */ if (this->ffmpeg_mpeg2_decoder) { uint8_t type = pes_get_picture_type(buf->content, buf->size); if (type) { /* signal FRAME_END to decoder */ post_frame_end(this, buf); /* for some reason ffmpeg mpeg2 decoder does not understand pts'es in B frames ? * (B-frame pts's are smaller than in previous P-frame) * Anyway, without this block of code B frames with pts are dropped. */ if (type == B_FRAME) buf->pts = 0; } } } /* H.264 */ else if (this->video_type == BUF_VIDEO_H264 && buf->size >= 4) { /* Access Unit Delimiter */ if (IS_NAL_AUD(p)) post_frame_end (this, buf); /* Check for end of still image. VDR ensures that H.264 still images end with an end of sequence NAL unit */ uint8_t *end = buf->content + buf->size; if (IS_NAL_END_SEQ(end-4)) { LOGMSG("post_frame_h264: Still frame ? (frame ends with end of sequence NAL unit)"); buf->decoder_flags |= BUF_FLAG_FRAME_END; } } buf->content = p; buf->size = this->packet_len; check_newpts( this, buf, PTS_VIDEO ); this->video_fifo->put (this->video_fifo, buf); return -1; } static int32_t parse_audio_stream(demux_xvdr_t *this, uint8_t *p, buf_element_t *buf) { int track; int32_t result; result = parse_pes_for_pts(this, p, buf); if (result < 0) return -1; p += result; track = this->stream_id & 0x1f; buf->content = p; buf->size = this->packet_len; buf->type = BUF_AUDIO_MPEG + track; buf->pts = this->pts; if (this->audio_fifo) { check_newpts( this, buf, PTS_AUDIO ); track_audio_stream_change (this, buf); this->audio_fifo->put (this->audio_fifo, buf); } else { buf->free_buffer(buf); } return -1; } static void demux_xvdr_parse_pes (demux_xvdr_t *this, buf_element_t *buf) { uint8_t *p = buf->content; int32_t result; if (IS_PADDING_PACKET(p)) { buf->free_buffer (buf); return; } this->stream_id = p[3]; if (this->ts_data) { ts_data_flush(this->ts_data); ts_data_dispose(&this->ts_data); } if (IS_VIDEO_PACKET(p)) { result = parse_video_stream(this, p, buf); } else if (IS_MPEG_AUDIO_PACKET(p)) { result = parse_audio_stream(this, p, buf); } else if (IS_PS1_PACKET(p)) { result = parse_private_stream_1(this, p, buf); } else { LOGMSG("Unrecognised PES stream 0x%02x", this->stream_id); buf->free_buffer (buf); return; } if (result < 0) { return; } LOGMSG("error! freeing buffer."); buf->free_buffer (buf); } /* * demux main */ static void demux_xvdr_parse_pack (demux_xvdr_t *this) { buf_element_t *buf = NULL; uint8_t *p; buf = this->input->read_block (this->input, this->video_fifo, 8128); if (!buf) { if (errno == EINTR) { LOGVERBOSE("input->read_block() was interrupted"); ts_data_flush(this->ts_data); } else if (errno != EAGAIN) { LOGDBG("DEMUX_FINISHED (input returns NULL with error)"); this->status = DEMUX_FINISHED; ts_data_dispose(&this->ts_data); } return; } /* If this is not a block for the demuxer, pass it * straight through. */ if (buf->type != BUF_DEMUX_BLOCK) { ts_data_flush (this->ts_data); demux_xvdr_fwd_buf (this, buf); return; } p = buf->content; /* len = this->blocksize; */ buf->decoder_flags = 0; if (DATA_IS_TS(p)) { demux_xvdr_parse_ts (this, buf); return; } if (DATA_IS_PES(p)) { demux_xvdr_parse_pes (this, buf); return; } LOGMSG("Header %02x %02x %02x (should be 0x000001 or 0x47)", p[0], p[1], p[2]); buf->free_buffer (buf); return; } /* * interface */ static int demux_xvdr_send_chunk (demux_plugin_t *this_gen) { demux_xvdr_t *this = (demux_xvdr_t *) this_gen; demux_xvdr_parse_pack(this); return this->status; } static void demux_xvdr_dispose (demux_plugin_t *this_gen) { demux_xvdr_t *this = (demux_xvdr_t *) this_gen; LOGDBG("demux_xvdr_dispose()"); ts_data_dispose(&this->ts_data); free (this); } static int demux_xvdr_get_status (demux_plugin_t *this_gen) { demux_xvdr_t *this = (demux_xvdr_t *) this_gen; if (this->status != DEMUX_OK) { if (this->ts_data) { LOGMSG("demux_xvdr_get_status(): status != DEMUX_OK. -> freeing ts_data"); ts_data_dispose(&this->ts_data); } } return this->status; } static void demux_xvdr_send_headers (demux_plugin_t *this_gen) { demux_xvdr_t *this = (demux_xvdr_t *) this_gen; this->video_fifo = this->stream->video_fifo; this->audio_fifo = this->stream->audio_fifo; /* * send start buffer */ _x_demux_control_start(this->stream); this->status = DEMUX_OK; _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 1); _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 1); _x_stream_info_set(this->stream, XINE_STREAM_INFO_BITRATE, 5000000); } static int demux_xvdr_seek (demux_plugin_t *this_gen, off_t start_pos, int start_time, int playing) { demux_xvdr_t *this = (demux_xvdr_t *) this_gen; /* * now start demuxing */ this->send_newpts = 1; this->video_type = 0; this->audio_type = 0; this->subtitle_type = 0; this->bih_posted = 0; ts_data_dispose(&this->ts_data); if (!playing) { this->buf_flag_seek = 0; this->status = DEMUX_OK; this->last_pts[0] = 0; this->last_pts[1] = 0; } else { this->buf_flag_seek = 1; this->last_vpts = INT64_C(-1); _x_demux_flush_engine(this->stream); } return this->status; } /* * demux class */ static int demux_xvdr_get_stream_length (demux_plugin_t *this_gen) { return 0; } static uint32_t demux_xvdr_get_capabilities(demux_plugin_t *this_gen) { return DEMUX_CAP_NOCAP; } static int demux_xvdr_get_optional_data(demux_plugin_t *this_gen, void *data, int data_type) { return DEMUX_OPTIONAL_UNSUPPORTED; } static demux_plugin_t *demux_xvdr_open_plugin (demux_class_t *class_gen, xine_stream_t *stream, input_plugin_t *input_gen) { input_plugin_t *input = (input_plugin_t *) input_gen; demux_xvdr_t *this; const char *mrl = input->get_mrl(input); if (strncmp(mrl, MRL_ID ":/", MRL_ID_LEN + 2 ) && strncmp(mrl, MRL_ID "+pipe://", MRL_ID_LEN + 8) && strncmp(mrl, MRL_ID "+tcp://", MRL_ID_LEN + 7) && strncmp(mrl, MRL_ID "+udp://", MRL_ID_LEN + 7) && strncmp(mrl, MRL_ID "+rtp://", MRL_ID_LEN + 7)) return NULL; this = calloc(1, sizeof(demux_xvdr_t)); this->stream = stream; this->input = input; this->demux_plugin.send_headers = demux_xvdr_send_headers; this->demux_plugin.send_chunk = demux_xvdr_send_chunk; this->demux_plugin.seek = demux_xvdr_seek; this->demux_plugin.dispose = demux_xvdr_dispose; this->demux_plugin.get_status = demux_xvdr_get_status; this->demux_plugin.get_stream_length = demux_xvdr_get_stream_length; this->demux_plugin.get_capabilities = demux_xvdr_get_capabilities; this->demux_plugin.get_optional_data = demux_xvdr_get_optional_data; this->demux_plugin.demux_class = class_gen; this->status = DEMUX_FINISHED; detect_video_decoders(this); detect_audio_decoders(this); return &this->demux_plugin; } #if DEMUXER_PLUGIN_IFACE_VERSION < 27 static const char *demux_xvdr_get_description (demux_class_t *this_gen) { return MRL_ID " demux plugin"; } static const char *demux_xvdr_get_identifier (demux_class_t *this_gen) { return MRL_ID; } static const char *demux_xvdr_get_extensions (demux_class_t *this_gen) { return NULL; } static const char *demux_xvdr_get_mimetypes (demux_class_t *this_gen) { return NULL; } static void demux_xvdr_class_dispose (demux_class_t *this_gen) { demux_xvdr_class_t *this = (demux_xvdr_class_t *) this_gen; free (this); } #endif void *demux_xvdr_init_class (xine_t *xine, void *data) { demux_xvdr_class_t *this; this = calloc(1, sizeof(demux_xvdr_class_t)); this->config = xine->config; this->xine = xine; this->demux_class.open_plugin = demux_xvdr_open_plugin; #if DEMUXER_PLUGIN_IFACE_VERSION < 27 this->demux_class.get_description = demux_xvdr_get_description; this->demux_class.get_identifier = demux_xvdr_get_identifier; this->demux_class.get_mimetypes = demux_xvdr_get_mimetypes; this->demux_class.get_extensions = demux_xvdr_get_extensions; this->demux_class.dispose = demux_xvdr_class_dispose; #else this->demux_class.description = N_("XVDR demux plugin"); this->demux_class.identifier = MRL_ID; this->demux_class.mimetypes = NULL; this->demux_class.extensions = MRL_ID":/ " MRL_ID"+pipe:/ " MRL_ID"+tcp:/ " MRL_ID"+udp:/ " MRL_ID"+rtp:/ " MRL_ID"+slave:/"; this->demux_class.dispose = default_demux_class_dispose; #endif return this; } xineliboutput-2.0.0/xine/adjustable_scr.h0000644000175000017500000000211613061253352016325 0ustar phph/* * adjustable_scr.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_ADJUSTABLE_SCR_H_ #define XINELIBOUTPUT_ADJUSTABLE_SCR_H_ /******************************* SCR ************************************* * * unix System Clock Reference + fine tuning * * fine tuning is used to change playback speed in live mode * to keep in sync with mpeg source *************************************************************************/ typedef struct adjustable_scr_s adjustable_scr_t; struct adjustable_scr_s { scr_plugin_t scr; void (*set_speed_tuning)(adjustable_scr_t *this, double factor); void (*set_speed_base) (adjustable_scr_t *this, int hz); void (*jump) (adjustable_scr_t *this, int pts); void (*set_buffering) (adjustable_scr_t *this, int on); void (*got_pcr) (adjustable_scr_t *this, int64_t pcr); void (*dispose) (adjustable_scr_t *this); }; adjustable_scr_t *adjustable_scr_start (xine_t *xine); #endif /* XINELIBOUTPUT_ADJUSTABLE_SCR_H_ */ xineliboutput-2.0.0/xine/adjustable_scr.c0000644000175000017500000002022413061253352016320 0ustar phph/* * adjustable_scr.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include #include "../tools/time_ms.h" #define LOG_MODULENAME "[scr ] " #define SysLogLevel iSysLogLevel #include "../logdefs.h" #include "adjustable_scr.h" /* * SCR code is mostly copied from xine-lib (src/input/input_pvr.c) * * * Copyright (C) 2000-2005 the xine project * March 2003 - Miguel Freitas * This plugin was sponsored by 1Control * * 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 * */ typedef struct scr_impl_s scr_impl_t; struct scr_impl_s { union { scr_plugin_t scr; adjustable_scr_t ascr; }; xine_t *xine; struct timeval cur_time; int64_t cur_pts; int xine_speed; int scr_speed_base; double speed_factor; double speed_tuning; int buffering; /* clock is freezed while buffering */ uint64_t buffering_start_time; pthread_mutex_t lock; }; /* Only call set_pivot when already mutex locked ! */ static void set_pivot (scr_impl_t *this) { struct timeval tv; int64_t pts; double pts_calc; if (this->buffering) { xine_monotonic_clock(&this->cur_time, NULL); return; } xine_monotonic_clock(&tv,NULL); pts_calc = (tv.tv_sec - this->cur_time.tv_sec) * this->speed_factor; pts_calc += (tv.tv_usec - this->cur_time.tv_usec) * this->speed_factor / 1e6; pts = this->cur_pts + pts_calc; /* This next part introduces a one off inaccuracy * to the scr due to rounding tv to pts. */ this->cur_time.tv_sec=tv.tv_sec; this->cur_time.tv_usec=tv.tv_usec; this->cur_pts=pts; } /* * xine interface (scr_plugin_t) */ static int scr_get_priority (scr_plugin_t *scr) { return 50; /* high priority */ } static int scr_set_fine_speed (scr_plugin_t *scr, int speed) { scr_impl_t *this = (scr_impl_t*) scr; pthread_mutex_lock (&this->lock); set_pivot( this ); this->xine_speed = speed; this->speed_factor = (double) speed * (double)this->scr_speed_base /*90000.0*/ / (1.0*XINE_FINE_SPEED_NORMAL) * this->speed_tuning; pthread_mutex_unlock (&this->lock); return speed; } static void scr_adjust (scr_plugin_t *scr, int64_t vpts) { scr_impl_t *this = (scr_impl_t*) scr; struct timeval tv; pthread_mutex_lock (&this->lock); xine_monotonic_clock(&tv,NULL); this->cur_time.tv_sec=tv.tv_sec; this->cur_time.tv_usec=tv.tv_usec; this->cur_pts = vpts; pthread_mutex_unlock (&this->lock); } static void scr_start (scr_plugin_t *scr, int64_t start_vpts) { scr_impl_t *this = (scr_impl_t*) scr; pthread_mutex_lock (&this->lock); xine_monotonic_clock(&this->cur_time, NULL); this->cur_pts = start_vpts; pthread_mutex_unlock (&this->lock); scr_set_fine_speed (&this->scr, XINE_FINE_SPEED_NORMAL); } static int64_t scr_get_current (scr_plugin_t *scr) { scr_impl_t *this = (scr_impl_t*) scr; struct timeval tv; int64_t pts; double pts_calc; pthread_mutex_lock (&this->lock); pts = this->cur_pts; if (this->buffering) { pthread_mutex_unlock (&this->lock); return pts; } xine_monotonic_clock(&tv,NULL); pts_calc = (tv.tv_sec - this->cur_time.tv_sec) * this->speed_factor; pts_calc += (tv.tv_usec - this->cur_time.tv_usec) * this->speed_factor / 1e6; pts += pts_calc; pthread_mutex_unlock (&this->lock); return pts; } static void scr_exit (scr_plugin_t *scr) { scr_impl_t *this = (scr_impl_t*) scr; pthread_mutex_destroy (&this->lock); free(this); } /* * adjusteble_scr_t */ /* * speed_tuning() * * - fine-tune SCR speed. Actual speed is base_speed * factor. */ static void adjustable_scr_speed_tuning (adjustable_scr_t *scr, double factor) { scr_impl_t *this = (scr_impl_t*) scr; pthread_mutex_lock (&this->lock); set_pivot( this ); this->speed_tuning = factor; this->speed_factor = (double) this->xine_speed * (double)this->scr_speed_base /*90000.0*/ / (1.0*XINE_FINE_SPEED_NORMAL) * this->speed_tuning; pthread_mutex_unlock (&this->lock); } /* * speed_base() * * - set base speed of SCR (default is 90kHz) */ static void adjustable_scr_speed_base (adjustable_scr_t *scr, int hz) { scr_impl_t *this = (scr_impl_t*) scr; pthread_mutex_lock (&this->lock); set_pivot( this ); this->scr_speed_base = hz; this->speed_factor = (double) this->xine_speed * (double)this->scr_speed_base /*90000.0*/ / (1.0*XINE_FINE_SPEED_NORMAL) * this->speed_tuning; pthread_mutex_unlock (&this->lock); } /* * jump() * * - Move SCR 'pts' ticks forward */ static void adjustable_scr_jump (adjustable_scr_t *scr, int pts) { scr_impl_t *this = (scr_impl_t*) scr; pthread_mutex_lock (&this->lock); set_pivot( this ); this->cur_pts += pts; pthread_mutex_unlock (&this->lock); } /* * got_pcr() * * - Synchronize clock to incoming PCR values */ static void adjustable_scr_got_pcr (adjustable_scr_t *scr, int64_t pcr) { } /* * set_buffering() * * - Clock is freezed while buffering */ static void adjustable_scr_set_buffering (adjustable_scr_t *scr, int buffering) { scr_impl_t *this = (scr_impl_t*) scr; pthread_mutex_lock (&this->lock); if (buffering) { if (!this->buffering) { set_pivot( this ); this->buffering = 1; this->buffering_start_time = time_ms(); LOGMSG("start buffering at %"PRId64, this->cur_pts); } } else { if (this->buffering) { set_pivot( this ); this->buffering = 0; LOGMSG("stop buffering at %"PRId64" (buffering took %"PRIu64" ms)", this->cur_pts, elapsed(this->buffering_start_time)); } } pthread_mutex_unlock (&this->lock); } /* * dispose() * * - unregister, stop and free resources */ static void adjustable_scr_dispose(adjustable_scr_t *scr) { scr_impl_t *this = (scr_impl_t*)scr; /* unregister */ if (this->xine) this->xine->clock->unregister_scr(this->xine->clock, &this->scr); /* exit and dispose */ this->scr.exit(&this->scr); } /* * adjusteble_scr_start() * */ adjustable_scr_t* adjustable_scr_start (xine_t *xine) { scr_impl_t *this; this = calloc(1, sizeof(scr_impl_t)); /* xine scr plugin interface */ this->scr.interface_version = 3; this->scr.set_fine_speed = scr_set_fine_speed; this->scr.get_priority = scr_get_priority; this->scr.adjust = scr_adjust; this->scr.start = scr_start; this->scr.get_current = scr_get_current; this->scr.exit = scr_exit; /* tuning / management interface */ this->ascr.set_speed_tuning = adjustable_scr_speed_tuning; this->ascr.set_speed_base = adjustable_scr_speed_base; this->ascr.jump = adjustable_scr_jump; this->ascr.got_pcr = adjustable_scr_got_pcr; this->ascr.set_buffering = adjustable_scr_set_buffering; this->ascr.dispose = adjustable_scr_dispose; /* initialize */ pthread_mutex_init (&this->lock, NULL); this->xine = xine; this->scr_speed_base = 90000; adjustable_scr_speed_tuning(&this->ascr, 1.0 ); scr_set_fine_speed (&this->scr, XINE_SPEED_PAUSE); /* start and register */ uint64_t time = xine->clock->get_current_time(xine->clock); this->scr.start(&this->scr, time); if (xine->clock->register_scr(xine->clock, &this->scr)) { scr_exit(&this->scr); return NULL; } return &this->ascr; } xineliboutput-2.0.0/vdrlogo_720x576.mpg0000644000175000017500000005432313061253352015436 0ustar phph,@3p# @3hѣ 74aF04hцƍ0xѣFk,hѣ 74aF04hцƍ0xѣFo4h d4aF04hцƍ0xѣFo4h Fahѣ 54hцƍ0xѣFo4h Fahѣ 74aF0(ѣFo4h Fahѣ 74aF04hц4h Fahѣ 74aF04hцƍ0xѣF` hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц hѣ 74aѣFP `^zdEIDw;E 7߮w_nil0CYCx hP }@ # ~P B&* Ye0~47@а@p@ @@ @ƀ@ `@ 3[%bYar!e ie o`  @4 `<Z~47@а@p@ F@ ӂL" ~PL011wӂM `@ @4 *  ~PAplYomB߻BA&A(hP2vCP#oPƖPP2vPlhP48?(hVY[~47@Р@@ F@ ӂMX\;V2~lYC~47@А@@ F@` M0+Pd6!e ie o G-T4(>d@vPe`e`꒶!e ie o@@`@ @4 PD@ XJsoSFR- @`` @!% >{_:?ϐ@@@ 1?A]:g$`@ >jD1Xa%F04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h  hѣ 74aѽ! &P &8 &SxV@ @L&Ta&nŁQ6@S-##?Oӑ^ :幹Ev+: {AfA%AEEf ݂-3HJ %"6nM_l=@P 9"c#ɦ`P }  ~Q)@_>rT:E# >+w t r/۾CPaM&:Z4hd*+b4͘%1%=` X 8 xX }(npˀn?n<ȟ3/URkrDx`~2FkRWYu#_ 9g{f(?Hϖ[7Ŋ*mJ2 X xp q@  |w vkҘQ\,hTT8ς0 w2]~j AAHgGxOBװs얜\%>LP!nǂ'e,9<|Z|$ >{@@@ǀ[xoX$o.@@  >FiT=T>GA 4,>>䮽uP_,OFЀ@@@@P@qp`|{9w,B۝|,d& @ _(##;{&o.:岩M/F\Pp~;]2Gۗ,T - >>~ y*;>ߎ4f{}ع]zyBق)m~3S\HX=]ݣ#o: : <2_F: 4l y ` p@@p@D~l ~H!.@5-qYL#sAA({-` >J_Z@W_;]߶vApp10߷vÁe(XDŽ0Iɖ+|;>zAᰶ`@@ @\6e?>m]uEl F߸AlAÁ@ = h,OyM%~} @{@mb9sƿ.633 %u :XiQD<L@H?@?LU >~ x_ '<@V5ӱ;JF` H/K@#o˖Yi* q*O8tFZ/^;Gv  ]YQU.d񥷂F,KlB]{sQĚ X C PA M0AM`AM6Ɠ@ a!P   Z &4-4aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣF` hѣ 74aѽ! &P &8 &SxV@ @L0Հ= Fg~OZP!( 쑬hMF3dc"RZ6d>AE(9 !(@` +7 lb+ewԪ߱A%@ Ӂ|I@l }rϸV 3=>I AͰ (?c 'Ȏ: ?XN+|D7>?v"3p `pfo` &}i;2/ y?p(Éd[v(Nr/o޶UѣyjJܾ%_H{|:B[a6;vK(1{|)nF5 @@ < ^z7@=e&9ZZDd/|xv0%- 8  u&&#;8, ET-l' S{`~kqs8G n _~%*'>~"#ق|| 6x ~ '!)9@FKb8"ڊq G:tS#iv,H>?8?蝰x zP@Lju7q{L_/6{! ? ,NoOހ*Hpmv_S nNV߻Hh~\'a,ˋp2|?EC`_~ݸ =`eidS4GiM8  }>|=>/0~lv,` (` }~ߑdc_yH!ni+7%e~>TbOE|?nq9&j #Ȃ3p(?mLbc:ϓ1  @@@ @@@``@@  @ n?"_?dOn'_l@@@z<JD|/P?P tE@3@뛰?8?X`bwP&_q"<$aĶp@  ?7/?LW>s5tuK18:"LbH݈۔wm};ֆh:|<:N}%|HN\raPιW=G(4X^yP@ISKtlDQnІgdK)I4T)m$=$66ou'G}nu*< >h p } ~  ~ ?0_Oyy>{2ONy%~ ~ |HOZ׿ 4@6|hy~EE ~Sr U\ N@w&gI<[}?ǵbU譾:>,OG1> ~/3  W1]]7%a5hxm-@@@ @H<̉7@)p)G\ QտQ{A'7t0Z O 0\@"<j̀lbhc 5A*4 A4 A4Mb,o! @& &0 h&xm̴hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fa hѣ 74aѽ! &P &8 &SxV@ @L0IlZѣ2Ұ{qS x ["<" ~(C@=&(!_S'P,F7@B>0[!zHmXˁA|"%qӬowuS!_{e)e_@@@_d,vv TQs_c{ @ ~( ~_@VF<+C@ŒI7p( ƂXZO@U@!o {A14 ~&&{O`>B+2@AA {`%0 3mPJ@O_"C1m6vWC &? ?S@p@@@ gE >wsӉ \E"><ߐE:zځ~PH'ހ`  DP_"E "qa>Rخ5\n6bJFsV;A A!;w+ȷ ~]_D GU\o_3nH}g㷐x&Aq@ydC$6F)YN tVF|@?~?<;Z!b*A @>Dp@ ~G@*s~F1p"j&pL>@p =fcA )|ɐ>41_ ξCEg }oy^GrF/7/@ @@ |RA IAloO ( I@,oo  (o߼ߠ@@1Xɛ4q2EnɥdB1UNQ  79_A@6X2Ga6 ,FIkKi1@ pp '  |X H ~  (h<Ǔݜ:«q4vpKܹ'6d`} htPz.?@|o=g:Ԝq5No o >@@ 'uL3Sq }8t+$q$n@an'uJ9+~ %JFie >@="p @ ~e;Pެ)Qcu2V\sW}ڤ$h0wϔ ăI v(` ;%]y*D}s|yʯNobI'rO`"[/m4.z7Ȁ2#t1D'u##F ;&17TBB AtA@@M9 9jThA.h#h l0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 0 hѣ 74aѽ! &P &8 &SxV@ @L0ѣF-LziIT ^P@@@@`@@~MܸP@6h!O`$/!4 })쐲er,g;jf{;H~A"dm@;  #~'H3}J#m@/E A2 S؀:o˳Aտ\O?tDxV@ e:#r 3H]c~.@ 0@@@\L4  ~ k\Ks"7v9;?]ow1 3 >}!2P@<mP0P  @ }@ p@Zs` {+dN\D W暭@@@@l__f_pp, >dDtE.>_ywW(Hߧ}pX_MspEP KRGY*o8 } @ `@@p@@(z@@`r8CpDE nR|\[( X ~ ~`  g }@ |? gOЉ62p@\ 6c%oB>?\'? /?;sb:[bTJ8S(x/6\P)/Tn6@_@pq_Oߧ6m'&{ʗ ":SXg WDo |BA@|ܑ.!@ wI6zn`uH?`;y>z-ۑo b܎,X~#D}by{<)@@P5X?q# ~o>rp #Ŋgf_߂3vmaqwJnf N#yy_Jܷ&J[~2x> 8 }7|(Z`4+P*#mp@ p@/@@@@p@:8oo `No6]D{.P[GT$p'p(Or~mY"- @BA{?pρ_@ݱ_۾8 < e ?8VX?\@>';C[ohgSa D?_Hv^Y<?qs(7WlͲWRd<5e >z2 "g:xM`Q?np޼{ G~,_ 9@q?<Pތ \C`[f&17TBB AtA@@M9 9jThA.h#h +C2ѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74a hѣ 74aѽ! &P &8 &SxV@ @L0-* UiCo8B_@/FxH?GP}/㜖Lߗl@oEՒ+`\ܥJ 841AU |҂$ vl |?n([˶l@@@ _`$b/{` @8y {^3DY=Ͳ֦Aup@$@@='&wa9@_!ހC9w̆~a{@}x6+(O;~Il,) B  @0zH\#۰}_"?@ u5E7F.r(gA"t0 ~x@_-:@`H۽.;l67F=0T Z%m@a?P@`@p@@|T? `t?e< ͼAʑl", -P =L } H ~~O?W 88u׻,H=_ߧ`p Xs[|lĖKw(rq O^nW,湔 %e)`6Xf.d2{~Yt"#syv+x&, 7>X>>@/?=&-iO| (hB5rx@ @@p@ @p P"/@5nz3y#rD='Z<& XW7@ H|d 2ٞS5׈ d0/scM q Ԩ { w8 } p }0 }0 gԈsGVF~@Rwb~ @??8&(v,SloHT:_ {wn~Ey݄a"mqDrwzJ.=D\t `ʊכFZGU9le^~]/Vʩ7A&A2= +{h Ƚ=@ʛhڭ z |  x X ~O fEbF#@,G,X.}>H:ǻ9@ ̈Fgbfmw@^'d^f(Ro<8 x O4;:#BɶF}G[9۲JsFDvD nxTFDyHHS)[eu3.Yu:/:Q_@?PȜ}E;|}.A_P~~fnp?34H}p8)yEPV9dJq6=Hhio @v+Fr?#u4  A  ^k2_(_!u(!۷!e @7(3*x@0@_Aϗ|{?뇈"T'a8 @[ٟ[Ԑ.g8E`{:O@ς(?qcȷ/t_<[mY57\؀@@`@4KA @nr^ ^4 cloς;Q0} "x@`p @34[VLjq2#P(`gP>`@ E| "5OGK#z`zl "d$}@t mjT V7TBB AtA@@M/y@{`qzxt+ 5*4 A4 A4ŔFahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц hѣ 74aѽ! &P &8 &SxV  @ x  oQ A<vgn_p?Z0{ gt>Ȁ@ @ @@|qwhۿ`;Q  wsĀ"h.F@@q ;߿?ң t\> > ܉@뽛 xyP{s A! @ ?~>@8 p`^zvqu瀀Rݲh  ~8GH T;2{"8Tr3#o~ #A?~@b'S"wl;YQ` `ܑ;7/w `>LtH.ӁA}5  wv"@A@@opC`!ڐ-̤ò`@@ @ .EtxJR~#lo|$?W1]WD O½\ Œ@@@opA  ws2@_ Ka , 4>#=`Eh_ϷYw]`B   ~(0< } ~p@^@D| O9΁p@)P@܅X$"  T;IA*)_d mP@?` ~̜(Ux8qpb݄ޜ>C {8 { ~ ~'Ν+۰pkh!;!so/k|P \;P ~0M@BP@opA2v'?p vI}T<?&@D#| `@|3>E@Bgn|\|^^>~ 0)ӳH؝wWn ?bSضA@@opLTdD4?j@@@@__{| . 9 t{]uA x}}3#\R`}9o  >r.{!]_&cEoKw:dD5GԠ uÒHcۂ{ނ&nG؈~`?@"pݼA|رIN1m 8F#7p X h /1,pp߸^8vAo"͠J?szb<R9":ne G41 Q3://g@?O8@D"g^Lo{';!m;X`@@@P@p`x#g#~@ >k8G&fNL'q.F RݹNd20E eD>?lv qэb@@p@p@@ Ep\24z\367:\17y\377:\17x\377" ":\17x\377:\17x\377:\17y\377:\17|\377>\25{\377_C\204\377M*\200\377:\20x\377" ":\20x\377:\20w\377:\20w\377:\20w\377:\17x\377:\17y\377@\31|\357eH\215G\0" "\0\0\0\0\0\0\0\0\0\0\0R.\204\225;\17z\377tY\231e\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0mT\223\40=\24z\373=\33n\377>!f\377lax\254f^n\211f]q\211e" "]r\220_Ov\254J*x\363;\17y\377cH\215\345gbjvgbjskfmsnipsqkrsoiv|^G|\274<\23" "y\377E\36~\352\272\252\320\21\0\0\0\0\0\0\0\0u\\\2261;\22z\377Y7\207\304" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0O+\204\202;\21u\377M4t\370;\20y\377" "rf\207Y\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\240\227\256\"J(\177\341:\21{\377" "sZ\233J\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\342\340\343\1P*\203\266;" "\17x\377zi\220a\0\0\0\0\0\0\0\0\0\0\0\0K&\204\316A\32{\375\243\230\264\37" "\0\0\0\0\0\0\0\0\0\0\0\0\335\313\362\3C\34}\340M1t\370J&\177\251:\20x\377" "rf\207Y\0\0\0\0ddd\6\24\24\24\301\35\35\35\301ZZZ\22hL\217X;\17{\377[=\212" "\265\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0G!~\251:\17y\377se\205" "{\0\0\0\0\0\0\0\0\0\0\0\0W7\203g9\20z\377iM\221}\0\0\0\0\0\0\0\0\0\0\0\0" "\\;\215F:\20y\377eU{\250?\26y\230:\20x\377rf\207Y\0\0\0\0fgg$GB3\377NKF\377" "JJJ\177\231\205\264\21=\23x\377T2\201\360\335\337\320\1\0\0\0\0\0\0\0\0\0" "\0\0\0\236\213\273\17Y7\210i<\23y\372?\32o\377\222\216\227H\0\0\0\0\0\0\0" "\0\0\0\0\0\212t\252\15@\30}\364T2\207\330\351\343\345\1\0\0\0\0\0\0\0\0H" "#~\252=\31r\377\225\216\230E?\26y\230:\20x\377rf\207Y\0\0\0\0\222\217\220" "\"\260\2215\377\237\213K\377788\236\352\332\366\6F\36\201\377L'|\377H$\202" "\366@\30}\365@\30}\365A\27{\365=\23y\375;\17x\377;\31n\377j_w\254\327\322" "\326\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0I&\201\236>\24z\377\226\203\2617\0" "\0\0\0|b\242\24=\24{\367V@x\340\344\342\343\2?\26y\230:\20x\377rf\207Y\0" "\0\0\0TUU\236\353\352\353\377\355\354\354\377('(\366MJP2B\31}\377I'z\377" "bN\201\357T?v\365P8v\367<\27u\377:\21x\377UDq\330\177}\203]\333\335\332\2" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0eM\222;;\20z\377aC\214\223\0\0\0" "\0L*\205n:\21w\377od}\202\0\0\0\0?\26y\230:\20x\377rf\207YY\\\\\40\230\230" "\232\372\364\365\365\377\366\366\366\377||}\3772-8\327:\17x\377U9}\372\337" "\342\332\3\0\0\0\0\340\340\330\2\203w\230TE\40z\365<\23{\367W7\210D\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\327\305\357\1G\"}\330O,\203\353" "\336\322\347\6F\37~\321F&r\375\255\254\256$\0\0\0\0?\26y\230:\17x\377\200" "t\216c\262\222D\221\272\260\216\377\363\364\365\377\366\366\366\377\212\201" "^\3773#%\377:\17x\377[Hx\302\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0xb\232]<\24z" "\377>\27z\353\204l\242\36\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "R2\205t;\20z\377y`\231w;\21v\377`Nz\303\0\0\0\0\0\0\0\0@\27z\230:\17y\377" "\242\217r\245\355\272\21\377\310\240\20\377\331\326\323\377\324\324\324\377" "\311\231\37\377\337\257\25\377S-^\377|u\203e\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0_?\214\2539\20z\377M*\202\262\367\361\363\2\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\205o\246\27?\26{\371J$\177\370:\24s\377\202{\206`" "\0\0\0\0\0\0\0\0?\26z\230;\17x\377gG\200\325}QB\377\214\\C\377<\40L\377$" "\15H\377\207^\25\377V9,\377TM_\307\312\317\307\5\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\242\215\271\26I$\200\355;\20z\377_C\213Y\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0M+\202\2619\20z\377P5u\361\313\314\307\15" "\0\0\0\0\0\0\0\0>\25x\231;\17y\377:\20y\377:\17y\377;\17y\3779\17z\377<\24" "p\377@)`\376bXo\262\266\264\253\26\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0u\\\233`<\21y\377A\34|\351\205s\246\25\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\221\211\2319kct\217|y\206a\0\0\0\0\0\0\0\0\0\0\0" "\0\204y\215Mjcx\217kav\215lbx\217kar\215icr\205\202\202\204i\260\261\251" "&\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\360\355\347\3wo\202zj`u\215\217\220\223?\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", }; xineliboutput-2.0.0/tools.c0000644000175000017500000000073213061253352013532 0ustar phph/* * tools.c: VDR/C++ wrapper for common tools * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_CONSTANT_MACROS #include #define __STDC_FORMAT_MACROS #include #include #include "logdefs.h" //#include "tools/vdrdiscovery.c" #include "tools/pes.c" #include "tools/mpeg.c" #include "tools/h264.c" #include "tools/h265.c" #include "tools/ts.c" xineliboutput-2.0.0/tools/0000755000175000017500000000000013061253352013364 5ustar phphxineliboutput-2.0.0/tools/vdrdiscovery.h0000644000175000017500000000303513061253352016261 0ustar phph/* * vdrdiscovery.h * * Simple broadcast protocol to search VDR with xineliboutput server * from (local) network. * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _VDRDISCOVERY_H_ #define _VDRDISCOVERY_H_ #define DISCOVERY_MSG_MAXSIZE 1024 #ifdef __cplusplus extern "C" { #endif struct sockaddr_in; /* * Client interface */ /* * udp_discovery_find_server() * * Search for server. Return address and port. * Returns > 0 on success. */ int udp_discovery_find_server(int *port, char *address); /* * Server interface */ /* * udp_discovery_init() * * Initialize server socket. Return value is the socket file descriptor, * and can be used for polling. * Returns < 0 on error. */ int udp_discovery_init(void); /* * udp_discovery_broadcast() * * Send server announcement. * Returns 0 on success. */ int udp_discovery_broadcast(int fd_discovery, int server_port, const char *server_address); /* * udp_discovery_recv() * * Receive query or announcement. * Returns number of bytes received, <= 0 on error. * Result is null-terminated string, not more than DISCOVERY_MSG_MAXSIZE bytes. */ int udp_discovery_recv(int fd_discovery, char *buf, int timeout, struct sockaddr_in *source); /* * udp_discovery_is_valid_search() * * Check if string is valid search message. * Returns 1 for valid messages, 0 for invalid messages. */ int udp_discovery_is_valid_search(const char *buf); #ifdef __cplusplus }; #endif #endif // _VDRDISCOVERY_H_ xineliboutput-2.0.0/tools/vdrdiscovery.c0000644000175000017500000001565713061253352016271 0ustar phph/* * vdrdiscovery.c * * Simple broadcast protocol to search VDR with xineliboutput server * from (local) network. * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _GNU_SOURCE # define _GNU_SOURCE /* asprintf */ #endif #include #include #ifdef __FreeBSD__ #include #endif #include #include #include #include #include #include #define LOG_MODULENAME "[discovery] " #include "../logdefs.h" #include "vdrdiscovery.h" /* * constants */ #ifndef DISCOVERY_PORT # define DISCOVERY_PORT 37890 #endif /* discovery protocol strings (v1.0) */ #define DISCOVERY_1_0_HDR "VDR xineliboutput DISCOVERY 1.0" "\r\n" #define DISCOVERY_1_0_CLI "Client: %s:%d" "\r\n" #define DISCOVERY_1_0_SVR "Server port: %d" "\r\n" #define DISCOVERY_1_0_ADDR "Server address: %s" "\r\n" #define DISCOVERY_1_0_VERSION "Server version: " /*vdr-" VDRVERSION "\r\n\t"*/ \ "xineliboutput-" XINELIBOUTPUT_VERSION "\r\n" /* * */ static inline int discovery_init(int port) { int fd_discovery = -1; int iBroadcast = 1, iReuse = 1; struct sockaddr_in sin; if ((fd_discovery = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_TCP*/)) < 0) { LOGERR("discovery_init: socket() failed"); return -1; } if (setsockopt(fd_discovery, SOL_SOCKET, SO_BROADCAST, &iBroadcast, sizeof(int)) < 0) LOGERR("discovery_init: setsockopt(SO_BROADCAST) failed"); if (setsockopt(fd_discovery, SOL_SOCKET, SO_REUSEADDR, &iReuse, sizeof(int)) < 0) LOGERR("discovery_init: setsockopt(SO_REUSEADDR) failed"); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = htonl(INADDR_BROADCAST); if (bind(fd_discovery, (struct sockaddr *)&sin, sizeof(sin)) < 0) { LOGERR("discovery_init: bind() failed"); close(fd_discovery); return -1; } return fd_discovery; } int udp_discovery_init(void) { return discovery_init(DISCOVERY_PORT); } static inline int udp_discovery_send(int fd_discovery, int port, char *msg) { struct sockaddr_in sin; int len = strlen(msg); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = INADDR_BROADCAST; if(len != sendto(fd_discovery, msg, len, 0, (struct sockaddr *)&sin, sizeof(sin))) { LOGERR("UDP broadcast send failed (discovery)"); return -1; } //LOGDBG("UDP broadcast send succeed (discovery)"); return 0; } int udp_discovery_broadcast(int fd_discovery, int server_port, const char *server_address) { char *msg = NULL; int result; if(server_address && *server_address) { result = asprintf(&msg, DISCOVERY_1_0_HDR //"VDR xineliboutput DISCOVERY 1.0" "\r\n" DISCOVERY_1_0_SVR //"Server port: %d" "\r\n" DISCOVERY_1_0_ADDR //"Server Address: %d.%d.%d.%d \r\n" DISCOVERY_1_0_VERSION //"Server version: xineliboutput-" XINELIBOUTPUT_VERSION "\r\n" "\r\n", server_port, server_address); } else { result = asprintf(&msg, DISCOVERY_1_0_HDR //"VDR xineliboutput DISCOVERY 1.0" "\r\n" DISCOVERY_1_0_SVR //"Server port: %d" "\r\n" DISCOVERY_1_0_VERSION //"Server version: xineliboutput-" XINELIBOUTPUT_VERSION "\r\n" "\r\n", server_port); } if (result >= 0) { result = udp_discovery_send(fd_discovery, DISCOVERY_PORT, msg); free(msg); } return result; } static inline int udp_discovery_search(int fd_discovery, int port) { char *msg = NULL; int result; result = asprintf(&msg, DISCOVERY_1_0_HDR /* "VDR xineliboutput DISCOVERY 1.0" "\r\n" */ DISCOVERY_1_0_CLI /* "Client: %s:%d" "\r\n" */ "\r\n", "255.255.255.255", port); if (result >= 0) { result = udp_discovery_send(fd_discovery, port, msg); free(msg); } return result; } int udp_discovery_recv(int fd_discovery, char *buf, int timeout, struct sockaddr_in *source) { socklen_t sourcelen = sizeof(struct sockaddr_in); struct pollfd pfd; int err; pfd.fd = fd_discovery; pfd.events = POLLIN; errno = 0; err = poll(&pfd, 1, timeout); if(err < 1) { if(err < 0) LOGERR("broadcast poll error"); return err; } memset(source, 0, sourcelen); memset(buf, 0, DISCOVERY_MSG_MAXSIZE); err = recvfrom(fd_discovery, buf, DISCOVERY_MSG_MAXSIZE-1, 0, (struct sockaddr *)source, &sourcelen); if(err <= 0) LOGDBG("fd_discovery recvfrom() error"); return err; } int udp_discovery_is_valid_search(const char *buf) { static const char id_string[] = { DISCOVERY_1_0_HDR "Client:" }; if(!strncmp(id_string, buf, strlen(id_string))) { LOGMSG("Received valid discovery message %s", buf); return 1; } LOGDBG("BROADCAST: %s", buf); return 0; } int udp_discovery_find_server(int *port, char *address) { static const char mystring[] = DISCOVERY_1_0_HDR "Server port: "; struct sockaddr_in from; char buf[DISCOVERY_MSG_MAXSIZE]; int fd_discovery = -1; int trycount = 0; int err = 0; *port = DISCOVERY_PORT; strcpy(address, "vdr"); if((fd_discovery = discovery_init(DISCOVERY_PORT)) < 0) return 0; while(err >= 0 && ++trycount < 4) { if((err = udp_discovery_search(fd_discovery, DISCOVERY_PORT) >= 0)) { errno = 0; while( (err = udp_discovery_recv(fd_discovery, buf, 500, &from)) > 0) { uint32_t tmp = ntohl(from.sin_addr.s_addr); buf[err] = 0; LOGDBG("Reveived broadcast: %d bytes from %d.%d.%d.%d \n%s", err, ((tmp>>24)&0xff), ((tmp>>16)&0xff), ((tmp>>8)&0xff), ((tmp)&0xff), buf); if(!strncmp(mystring, buf, strlen(mystring))) { char *iploc; LOGDBG("Valid discovery message"); // default: use broadcast source address sprintf(address, "%d.%d.%d.%d", ((tmp>>24)&0xff), ((tmp>>16)&0xff), ((tmp>>8)&0xff), ((tmp)&0xff)); // Check if announce message includes alternative server address iploc = strstr(buf + strlen(mystring), "Server address: "); if(iploc) { uint32_t svraddr; iploc += strlen("Server address: "); svraddr = inet_addr(iploc); if(svraddr == INADDR_NONE || svraddr == INADDR_ANY) { LOGMSG("Server provided invalid address !"); } else { svraddr = ntohl(svraddr); sprintf(address, "%d.%d.%d.%d", ((svraddr>>24)&0xff), ((svraddr>>16)&0xff), ((svraddr>>8)&0xff), ((svraddr)&0xff)); LOGMSG("Replacing broadcast source address %d.%d.%d.%d " "with server-given address %s", ((tmp>>24)&0xff), ((tmp>>16)&0xff), ((tmp>>8)&0xff), ((tmp)&0xff), address); } } *port = -1; if(1 == sscanf(buf + strlen(mystring), "%d", port) && *port >= 1000 && *port <= 0xffff) { close(fd_discovery); return 1; } LOGMSG("Server-given port is invalid !"); } else { LOGDBG("NOT valid discovery message"); } } } } /* failed */ close(fd_discovery); return 0; } xineliboutput-2.0.0/tools/udp_pes_scheduler.h0000644000175000017500000000640613061253352017240 0ustar phph/* * udp_pes_scheduler.h: PES scheduler for UDP/RTP streams * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __UDP_PES_SCHEDULER_H #define __UDP_PES_SCHEDULER_H #include #include // uchar #include #include "cxsocket.h" #include "time_pts.h" #define MAX_UDP_HANDLES 16 class cUdpBackLog; class cUdpScheduler : public cThread { public: cUdpScheduler(); virtual ~cUdpScheduler(); // fd should be binded & connected to IP:PORT (local+remote) pair ! bool AddHandle(int fd); /* UDP unicast */ void RemoveHandle(int fd); /* UDP unicast */ bool AddRtp(void); /* UDP/RTP multicast */ void RemoveRtp(void); /* UDP/RTP multicast */ bool AddHandle(cxSocket& s) { return AddHandle(s.handle()); } void RemoveHandle(cxSocket& s) { RemoveHandle(s.handle()); } bool Clients(void) { return m_Handles[0] >= 0; } int Poll(int TimeoutMs, bool Master); bool Queue(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int Length); void QueuePadding(void); void ReSend(int fd, uint64_t Pos, int Seq1, int Seq2); void Clear(void); bool Flush(int TimeoutMs); void Pause(bool On); void TrickSpeed(const int Multiplier); void SetScrSpeed(const int Speed = 90000); protected: // Signalling cCondVar m_Cond; cMutex m_Lock; // Clients int m_Handles[MAX_UDP_HANDLES]; uint m_wmem[MAX_UDP_HANDLES]; /* kernel buffer size */ cxSocket m_fd_rtp; cxSocket m_fd_rtcp; // Queue uint m_QueueNextSeq; /* next outgoing */ uint m_QueuePending; /* outgoing queue size */ cUdpBackLog *m_BackLog; /* queue for incoming data (not yet send) and retransmissions */ cMutex m_BackLogDeleteMutex; // Scheduling typedef enum { /* from worst to best */ eScrDetect = 0, eScrFromVideo = 1, eScrFromPS1 = 2, eScrFromAudio = 3, eScrFromPcr = 4, } ScrSource_t; cTimePts m_MasterClock; /* Current MPEG PTS (synchronized to current stream) */ bool m_TrickSpeed; /* current (replay) speed */ bool m_Master; /* if true, we are master metronom for playback */ ScrSource_t m_ScrSource; int64_t m_CurrentAudioVtime; int64_t m_CurrentVideoVtime; int64_t m_CurrentPcr; uint m_BurstBytes; /* number of bytes sent without sleeps */ uint m_BurstFrames; /* number of frames sent without sleeps */ cCondWait m_CondWait; int CalcElapsedVtime(int64_t pts, ScrSource_t ScrSource); void Schedule(const uchar *Data, int Length); void Scheduler_Sleep(int ms); void QueuePaddingInternal(void); // RTP uint32_t m_ssrc; /* RTP synchronization source id */ cTimePts m_RtpScr; /* 90 kHz monotonic time source for RTP timestamps */ // RTCP uint64_t m_LastRtcpTime; uint32_t m_Frames; uint32_t m_Octets; void Send_RTCP(void); // SAP int m_fd_sap; void Send_SAP(bool Announce = true); // Thread virtual void Action(void); }; #endif xineliboutput-2.0.0/tools/udp_pes_scheduler.c0000644000175000017500000006131713061253352017235 0ustar phph/* * udp_pes_scheduler.h: PES scheduler for UDP/RTP streams * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ //#define LOG_UDP_RATE //#define LOG_RESEND //#define LOG_SCR #define __STDC_FORMAT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include #include #include #include #include #include #include #include "../logdefs.h" // logging #include "../config.h" // configuration data #include "../xine_input_vdr_net.h" // frame headers and constants #include "ts.h" #include "pes.h" #include "udp_buffer.h" #include "time_pts.h" #include "cxsocket.h" #include "sap.h" // SAP - Session Announcement Protocol #include "sdp.h" // SDP - Session Description Protocol #include "rtcp.h" // RTCP #include "sys_cap.h" #include "udp_pes_scheduler.h" #ifdef LOG_RESEND # define LOGRESEND LOGDBG #else # define LOGRESEND(x...) #endif #ifdef LOG_SCR # define LOGSCR LOGDBG #else # define LOGSCR(x...) #endif /* * constants */ const uint MAX_QUEUE_SIZE = 64; // ~ 65 ms with typical DVB stream const uint MAX_LIVE_QUEUE_SIZE = (64+60); // ~ 100 ms with typical DVB stream const uint HARD_LIMIT = (4*1024); // ~ 40 Mbit/s === 4 Mb/s const uint MAX_BURST_BYTES = 32768; // 32 kb const uint MAX_BURST_FRAMES = 15; // 15 UDP packets const uint RTCP_MIN_INTERVAL = 45000; // 500 ms (pts units) // initial burst length after seek (500ms = ~13 video frames) const int64_t INITIAL_BURST_TIME = INT64_C(45000); // pts units (90kHz) // assume seek when when pts difference between two frames exceeds this (2,5 seconds) const int64_t JUMP_LIMIT_TIME = INT64_C(225000); // pts units (90kHz) const uint SCHEDULER_MIN_DELAY_MS = 3; const uint SCHEDULER_MAX_DELAY_MS = 20; static inline int64_t abs64(int64_t val) { return val<0 ? -val : val; } cUdpScheduler::cUdpScheduler() { // Scheduler data m_ScrSource = eScrDetect; m_CurrentAudioVtime = 0; m_CurrentVideoVtime = 0; m_CurrentPcr = 0; m_MasterClock.Set(INT64_C(0)); m_BurstBytes = 0; m_BurstFrames = 0; m_Master = false; m_TrickSpeed = false; // RTP srandom(time(NULL) ^ getpid()); m_ssrc = random(); LOGDBG("RTP SSRC: 0x%08x", m_ssrc); m_LastRtcpTime = 0; m_Frames = 0; m_Octets = 0; m_RtpScr.Set((int64_t)random()); m_fd_sap = -1; // Queuing int i; for(i=0; i= limit) { uint64_t WaitEnd = cTimeMs::Now(); if(TimeoutMs >= 0) WaitEnd += (uint64_t)TimeoutMs; while (cTimeMs::Now() < WaitEnd && Running() && m_QueuePending >= limit) m_Cond.TimedWait(m_Lock, 5); } return limit > m_QueuePending ? limit - m_QueuePending : 0; } bool cUdpScheduler::Flush(int TimeoutMs) { cMutexLock ml(&m_Lock); if(m_Handles[0] < 0) return true; if(m_QueuePending > 0) { uint64_t WaitEnd = cTimeMs::Now(); if(TimeoutMs >= 0) WaitEnd += (uint64_t)TimeoutMs; while (cTimeMs::Now() < WaitEnd && Running() && m_QueuePending > 0) m_Cond.TimedWait(m_Lock, 5); } return m_QueuePending == 0; } void cUdpScheduler::Clear(void) { cMutexLock ml(&m_Lock); m_BackLog->Clear(m_QueuePending); m_QueuePending = 0; m_Cond.Broadcast(); m_ScrSource = eScrDetect; } void cUdpScheduler::Pause(bool On) { cMutexLock ml(&m_Lock); if(On) m_MasterClock.Pause(); else m_MasterClock.Resume(); m_TrickSpeed = false; } void cUdpScheduler::TrickSpeed(const int Multiplier) { cMutexLock ml(&m_Lock); #ifdef LOG_SCR if(Multiplier == 1 || Multiplier == -1) { LOGMSG("UDP clock --> normal"); } else if(Multiplier < 0) LOGMSG("UDP clock --> %dx", -Multiplier); else LOGMSG("UDP clock --> 1/%d", Multiplier); #endif m_MasterClock.TrickSpeed(Multiplier); m_TrickSpeed = (Multiplier==-1 || Multiplier==1) ? false : true; } void cUdpScheduler::SetScrSpeed(const int Speed) { cMutexLock ml(&m_Lock); m_MasterClock.SetScrSpeed(Speed); m_RtpScr.SetScrSpeed(Speed); } bool cUdpScheduler::Queue(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int Length) { cMutexLock ml(&m_Lock); if(m_Handles[0] < 0) return true; uint limit = m_Master ? MAX_QUEUE_SIZE : MAX_LIVE_QUEUE_SIZE; if(m_QueuePending >= limit) return false; m_BackLog->MakeFrame(StreamId, StreamPos, Data, Length); m_QueuePending++; m_Cond.Broadcast(); return true; } void cUdpScheduler::QueuePadding(void) { cMutexLock ml(&m_Lock); if (m_Handles[0] < 0) return; if (m_QueuePending > 2) return; QueuePaddingInternal(); m_Cond.Broadcast(); } void cUdpScheduler::QueuePaddingInternal(void) { static unsigned const char Padding[] = {0x00,0x00,0x01,0xBE,0x00,0x02,0xff,0xff}; int PrevSeq = (m_QueueNextSeq + UDP_BUFFER_SIZE - 1) & UDP_BUFFER_MASK; stream_rtp_header_impl_t *Frame = m_BackLog->Get(PrevSeq); if (Frame) { int PrevLen = m_BackLog->PayloadSize(PrevSeq); uint64_t Pos = ntohll(Frame->hdr_ext.pos) + PrevLen - 8; m_BackLog->MakeFrame(sidPadding, Pos, Padding, 8); } else m_BackLog->MakeFrame(sidPadding, 0, Padding, 8); m_QueuePending++; } int cUdpScheduler::CalcElapsedVtime(int64_t pts, ScrSource_t ScrSource) { int64_t diff = 0; switch (ScrSource) { case eScrFromVideo: diff = pts - m_CurrentVideoVtime; if (diff > JUMP_LIMIT_TIME || (-diff) > JUMP_LIMIT_TIME) { // 1 s (must be > GOP) // RESET LOGSCR("cUdpScheduler SCR RESET (Video jump %lld->%lld)", m_CurrentVideoVtime, pts); m_CurrentVideoVtime = pts; // Use video pts for sync only in audioless trickspeeds // (audio has smaller, constant and increasing intervals) if (m_TrickSpeed) m_MasterClock.Set(m_CurrentVideoVtime + INITIAL_BURST_TIME); return -1; } if (diff < 0) /* ignore small negative differences (B/P frames are sent out-of-order) */ diff = 0; else m_CurrentVideoVtime = pts; break; case eScrFromAudio: diff = abs64(pts - m_CurrentAudioVtime); if (diff > JUMP_LIMIT_TIME) { // 1 sec // RESET LOGSCR("cUdpScheduler SCR RESET (Audio jump %lld->%lld)", m_CurrentAudioVtime, pts); m_CurrentAudioVtime = pts; m_MasterClock.Set(m_CurrentAudioVtime + INITIAL_BURST_TIME); return -1; } m_CurrentAudioVtime = pts; break; case eScrFromPcr: diff = pts - m_CurrentPcr; if (diff > JUMP_LIMIT_TIME || diff < 0) { // 1 sec // RESET LOGSCR("cUdpScheduler RESET (PCR jump %lld->%lld)", m_CurrentPcr, pts); m_CurrentPcr = pts; m_MasterClock.Set(m_CurrentPcr + INITIAL_BURST_TIME); return -1; } m_CurrentPcr = pts; break; default: break; } return (int) diff; } void cUdpScheduler::Send_RTCP(void) { if(!m_fd_rtcp.open()) return; uint64_t scr = m_RtpScr.Now(); if(scr > (m_LastRtcpTime + RTCP_MIN_INTERVAL)) { uint8_t frame[2048], *content = frame; char hostname[64] = ""; rtcp_packet_t *msg = (rtcp_packet_t *)content; struct timeval tv; gettimeofday(&tv, NULL); gethostname(hostname, sizeof(hostname)-1); hostname[sizeof(hostname)-1] = 0; // SR (Sender report) msg->hdr.raw[0] = 0x81; // RTP version = 2, Report count = 1 */ msg->hdr.ptype = RTCP_SR; msg->hdr.length = htons(6); // length 6 dwords msg->sr.ssrc = htonl(m_ssrc); msg->sr.ntp_sec = htonl(tv.tv_sec + 0x83AA7E80); msg->sr.ntp_frac = htonl((uint32_t)((double)tv.tv_usec*(double)(1LL<<32)*1.0e-6)); msg->sr.rtp_ts = htonl((uint32_t)(scr & 0xffffffff)); msg->sr.psent = htonl((uint32_t)(m_Frames & 0xffffffff)); msg->sr.osent = htonl((uint32_t)(m_Octets & 0xffffffff)); content += sizeof(rtcp_common_t) + sizeof(rtcp_sr_t); msg = (rtcp_packet_t *)content; // SDES msg->hdr.raw[0] = 0x81; // RTP version = 2, Report count = 1 */ msg->hdr.ptype = RTCP_SDES; msg->hdr.count = 1; msg->sdes.ssrc = m_ssrc; rtcp_sdes_item_t *it = &msg->sdes.item[0]; it->type = RTCP_SDES_CNAME; sprintf(it->data, "VDR@%s:%d%c%c%c", hostname[0] ? hostname : xc.remote_rtp_addr, xc.remote_rtp_port, 0, 0, 0); it->length = strlen(it->data); msg->hdr.length = htons(1 + 1 + ((it->length - 2) + 3) / 4); content += sizeof(rtcp_common_t) + 4*ntohs(msg->hdr.length); msg = (rtcp_packet_t *)content; // Send #ifndef LOG_RTCP (void) m_fd_rtcp.send(frame, content - frame); #else LOGMSG("RTCP send (%d)", m_fd_rtcp.send(frame, content - frame)); for(int i=0; i 10702 ? SDP_PAYLOAD_MPEG_TS : SDP_PAYLOAD_MPEG_PES; const char *sdp_descr = vdr_sdp_description(ip, 2001, xc.listen_port, xc.remote_rtp_addr, m_ssrc, payload_type, xc.remote_rtp_port, xc.remote_rtp_ttl); if(!sdp_descr) return; #if 1 /* store copy of SDP data */ if(m_fd_sap < 0) { cString fname = AddDirectory( #if defined(APIVERSNUM) && (APIVERSNUM < 20102) VideoDirectory, #else cVideoDirectory::Name(), #endif cString::sprintf("xineliboutput@%s.sdp", ip)); FILE *fp = fopen(fname, "w"); if(fp) { fprintf(fp, "%s", sdp_descr); fclose(fp); } } #endif sap_pdu_t *pdu = sap_create_pdu(local_addr, Announce, (m_ssrc >> 16 | m_ssrc) & 0xffff, "application/sdp", sdp_descr); if(!sap_send_pdu(&m_fd_sap, pdu, 0)) LOGERR("SAP/SDP announce failed"); free(pdu); if(!Announce) CLOSESOCKET(m_fd_sap); } #ifdef LOG_SCR static const char ScrSourceName[][6] = {"???", "VIDEO", "PS1", "AUDIO", "PCR"}; #endif void cUdpScheduler::Schedule(const uchar *Data, int Length) { int64_t pts = NO_PTS; int elapsed = 0; ScrSource_t ScrSource = eScrDetect; // Get timestamp from data if (DATA_IS_TS(Data)) { if (ts_get_pcr_n(Data, Length/TS_SIZE, &pts)) { LOGSCR("UDP PCR: %" PRId64, pts); ScrSource = eScrFromPcr; elapsed = CalcElapsedVtime(pts, eScrFromPcr); } } else /* if (DATA_IS_PES(Data)) */ { if (PES_HAS_PTS(Data)) { ScrSource = IS_VIDEO_PACKET(Data) ? eScrFromVideo : IS_AUDIO_PACKET(Data) ? eScrFromAudio : eScrDetect; pts = pes_get_pts(Data, Length); elapsed = CalcElapsedVtime(pts, ScrSource); } } if (m_ScrSource < ScrSource) m_ScrSource = ScrSource; if(elapsed > 0) { int64_t now = m_MasterClock.Now(); int64_t SendTime = ScrSource==eScrFromVideo ? m_CurrentVideoVtime : ScrSource==eScrFromAudio ? m_CurrentAudioVtime : ScrSource==eScrFromPcr ? m_CurrentPcr : now; LOGSCR("PTS: %lld (%s) elapsed %d ms (PID %02x)", pts, ScrSourceStr[ScrSource], pts_to_ms(elapsed), DATA_IS_TS(Data) ? TS_PID(Data) : Data[3]); // // Detect discontinuity (against SCR) // if (now > SendTime && (now - SendTime) > JUMP_LIMIT_TIME) { LOGSCR("cUdpScheduler MasterClock init (was in past) %s", ScrSourceStr[ScrSource]); now = SendTime + INITIAL_BURST_TIME; m_MasterClock.Set(now); } else if (now < SendTime && (SendTime - now) > JUMP_LIMIT_TIME) { LOGSCR("cUdpScheduler MasterClock init (was in future) %s", ScrSourceStr[ScrSource]); now = SendTime + INITIAL_BURST_TIME; m_MasterClock.Set(now); } // // Delay // while (SendTime > now) { uint delay_ms = pts_to_ms(SendTime - now); if (delay_ms < SCHEDULER_MIN_DELAY_MS) break; LOGSCR("cUdpScheduler sleeping %d ms (time ref: %s, beat interval %d ms)", delay_ms, ScrSourceStr[ScrSource], pts_to_ms(elapsed)); if (delay_ms > SCHEDULER_MAX_DELAY_MS) delay_ms = SCHEDULER_MAX_DELAY_MS; Scheduler_Sleep(delay_ms); now = m_MasterClock.Now(); } } } void cUdpScheduler::Action(void) { /* UDP Scheduler needs high priority */ const int priority = -5; if (have_cap_sys_nice()) SetPriority(priority); errno = 0; if ((nice(priority) == -1) && errno) LOGDBG("cUdpScheduler: Can't nice to value: %d", priority); m_Lock.Lock(); while (Running()) { if(m_Handles[0] < 0) { m_Cond.TimedWait(m_Lock, 5000); continue; } // Wait until we have outgoing data in queue if(m_QueuePending <= 0) { m_BurstFrames = m_BurstBytes = 0; m_Cond.TimedWait(m_Lock, 100); if(m_QueuePending <= 0) { // Still nothing... // Send padding frame once in 100ms so clients can detect // possible missing frames and server shutdown QueuePaddingInternal(); } continue; // to check Running() } // Take next frame from queue stream_rtp_header_impl_t *frame = m_BackLog->Get(m_QueueNextSeq); int PayloadSize = m_BackLog->PayloadSize(m_QueueNextSeq); int UdpPacketLen = PayloadSize + sizeof(stream_udp_header_t); int RtpPacketLen = PayloadSize + sizeof(stream_rtp_header_impl_t); m_QueueNextSeq = (m_QueueNextSeq + 1) & UDP_BUFFER_MASK; m_QueuePending--; m_Cond.Broadcast(); m_BackLogDeleteMutex.Lock(); /* ensure frame will not be deleted from queue */ m_Lock.Unlock(); // Schedule frame if(m_Master && eStreamId(frame->hdr_ext.stream) == sidVdr) Schedule(frame->payload, PayloadSize); // Need some bandwidth limit for ex. sequence of still frames when // moving cutting marks very fast (no audio or PTS available). // Hard limit for used bandwidth: // - ~1 frames/ms & 8kb/ms -> 8mb/s -> ~ 80 Mbit/s ( / client) // - max burst 15 frames or 32 kb m_BurstFrames ++; m_BurstBytes += PayloadSize; if (m_BurstFrames >= MAX_BURST_FRAMES && m_BurstBytes >= MAX_BURST_BYTES) { Scheduler_Sleep(4); #ifdef LOG_UDP_RATE static uint64_t dbg_timer = cTimeMs::Now(); static int dbg_bytes = 0; dbg_bytes += bytes; if (dbg_timer + 60000 <= cTimeMs::Now()) { LOGDBG("UDP rate: %4d Kbps (queue %d)", dbg_bytes/(60*1024/8), m_QueuePending); dbg_bytes = 0; dbg_timer = cTimeMs::Now(); } #endif } /* tag frame with ssrc and timestamp */ frame->rtp_hdr.ts = htonl((uint32_t)(m_RtpScr.Now() & 0xffffffff)); frame->rtp_hdr.ssrc = htonl(m_ssrc); /* deliver to all active sockets */ for(int i=0; i=0; i++) { // // use TIOCOUTQ ioctl instead of poll/select. // - poll/select for UDP/RTP may return true even when queue // is (almost) full // - kernel silently drops frames it cant send // -> poll() + send() just causes frames to be dropped // uint size = 0; if(!ioctl(m_Handles[i], TIOCOUTQ, &size)) { if(size >= (m_wmem[i] - 2*RtpPacketLen)) { LOGMSG("cUdpScheduler: kernel transmit queue > ~%dkb (max %dkb) ! (master=%d)", (m_wmem[i] - 2*RtpPacketLen)/1024, m_wmem[i]/1024, m_Master); Scheduler_Sleep(2); } } else { if(m_QueuePending > (MAX_QUEUE_SIZE-5)) LOGDBG("cUdpScheduler: kernel transmit queue > ~30kb ! (master=%d ; Queue=%d)", m_Master, m_QueuePending); Scheduler_Sleep(2); } if(m_Handles[i] == m_fd_rtp.handle()) { if(send(m_Handles[i], frame, RtpPacketLen, 0) <= 0) LOGERR("cUdpScheduler: UDP/RTP send() failed !"); } else { /* UDP: send without rtp header */ if (send(m_Handles[i], RTP_UDP_PAYLOAD(frame), UdpPacketLen, 0) <= 0) LOGERR("cUdpScheduler: UDP send() failed !"); } } m_BackLogDeleteMutex.Unlock(); /* release queue */ m_Lock.Lock(); m_Frames ++; m_Octets += PayloadSize; if(m_fd_rtcp.open() && (m_Frames & 0xff) == 1) { // every 256th frame Send_RTCP(); #if 0 if((m_Frames & 0xff00) == 0) // every 65536th frame (~ 2 min) Send_SAP(); #else if((m_Frames & 0x0300) == 0) // every 1024th frame (~ 2...4 sec) Send_SAP(); #endif } } m_Lock.Unlock(); } void cUdpScheduler::ReSend(int fd, uint64_t Pos, int Seq1, int Seq2) { if(fd < 0) /* no re-send for RTP */ return; struct { stream_udp_header_t hdr; char payload[64-sizeof(stream_udp_header_t)]; } udp_ctrl = {{(uint64_t)INT64_C(-1), (uint16_t)-1, 0, {}}, {0}}; // Handle buffer wrap if(Seq1 > Seq2) Seq2 += UDP_BUFFER_SIZE; cMutexLock ml(&m_Lock); // keeps also scheduler thread suspended ... if(Seq2-Seq1 > 64) { LOGDBG("cUdpScheduler::ReSend: requested range too large (%d-%d)", Seq1, Seq2); snprintf(udp_ctrl.payload, sizeof(udp_ctrl.payload), "UDP MISSING %d-%d %" PRIu64, Seq1, (Seq2 & UDP_BUFFER_MASK), Pos); send(fd, &udp_ctrl, sizeof(udp_ctrl), 0); return; } // re-send whole range for(; Seq1 <= Seq2; Seq1++) { // Wait if kernel queue is full int size = 0; if(!ioctl(fd, TIOCOUTQ, &size)) if(size > ((0x10000)/2 - 2048)) { // assume 64k kernel buffer LOGDBG("cUdpScheduler::ReSend: kernel transmit queue > ~30kb !"); Scheduler_Sleep(2); } stream_rtp_header_impl_t *frame = m_BackLog->Get(Seq1); if(frame) { if(ntohull(frame->hdr_ext.pos) - Pos < 100000) { send(fd, RTP_UDP_PAYLOAD(frame), m_BackLog->PayloadSize(Seq1) + sizeof(stream_udp_header_t), 0); LOGRESEND("cUdpScheduler::ReSend: %d (%d bytes) @%lld sent", Seq1, m_BackLog->PayloadSize(Seq1), Pos); Pos = ntohull(frame->hdr_ext.pos) + m_BackLog->PayloadSize(Seq1); continue; } else { // buffer has been lost long time ago... LOGRESEND("cUdpScheduler::ReSend: Requested position does not match " "(%lld ; has %lld)", Pos, ntohll(frame->hdr_ext.pos)); } } else { LOGRESEND("cUdpScheduler::ReSend: %d @%lld missing", Seq1, Pos); } // buffer has been lost - send packet missing info LOGRESEND("cUdpScheduler::ReSend: missing %d-%d @%d (hdr 0x%llx 0x%x)", Seq1, Seq1, Pos, udp_ctrl.hdr.pos, udp_ctrl.hdr.seq); int Seq0 = Seq1; for(; Seq1 < Seq2; Seq1++) { stream_rtp_header_impl_t *frame = m_BackLog->Get(Seq1+1); if(frame && (ntohull(frame->hdr_ext.pos) - Pos < 100000)) break; } snprintf(udp_ctrl.payload, sizeof(udp_ctrl.payload), "UDP MISSING %d-%d %" PRIu64, Seq0, (Seq1 & UDP_BUFFER_MASK), Pos); send(fd, &udp_ctrl, sizeof(udp_ctrl), 0); } } xineliboutput-2.0.0/tools/udp_buffer.h0000644000175000017500000000772413061253352015670 0ustar phph/* * udp_buffer.h: Ring buffer for UDP/RTP streams * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __UDP_BUFFER_H #define __UDP_BUFFER_H #include #include // VDRVERSNUM #include "../xine_input_vdr_net.h" // frame headers #include "ts.h" #define UDP_BUFFER_SIZE 0x100 // 2^n #define UDP_BUFFER_MASK 0xff // 2^n - 1 #if UDP_BUFFER_MASK != UDP_SEQ_MASK # error Buffer handling error !!! #endif class cUdpBackLog { friend class cUdpScheduler; private: cUdpBackLog(cUdpBackLog&); stream_rtp_header_impl_t *m_UdpBuffer[UDP_BUFFER_SIZE]; int m_UdpBufLen[UDP_BUFFER_SIZE]; /* size of allocated memory, not frame */ int m_PayloadSize[UDP_BUFFER_SIZE]; /* size of frame */ unsigned int m_SeqNo; /* next (outgoing) sequence number */ unsigned int m_RtpSeqNo; /* next (outgoing) RTP sequence number */ protected: cUdpBackLog() { memset(m_UdpBuffer, 0, sizeof(stream_rtp_header_impl_t *)*UDP_BUFFER_SIZE); memset(m_UdpBufLen, 0, sizeof(int) * UDP_BUFFER_SIZE); memset(m_PayloadSize, 0, sizeof(int) * UDP_BUFFER_SIZE); m_SeqNo = 0; m_RtpSeqNo = random(); } void Clear(int HowManyFrames) { // Clear n last frames from buffer. // (called to adjust sequence numbering when some // already allocated frames won't be sent) // // Note: Nothing is freed. // To completely reset buffer it must be deleted and re-created. // m_SeqNo = (m_SeqNo + UDP_BUFFER_SIZE - HowManyFrames) & UDP_BUFFER_MASK; } virtual ~cUdpBackLog() { for(int i=0; ipayload, Data, DataLen); // RTP header header->rtp_hdr.raw[0] = RTP_VERSION_BYTE | RTP_HDREXT_BIT; #if VDRVERSNUM >= 10701 || defined(TSPLAY_PATCH_VERSION) if (DATA_IS_TS(Data)) header->rtp_hdr.raw[1] = RTP_PAYLOAD_TYPE_TS; else header->rtp_hdr.raw[1] = RTP_PAYLOAD_TYPE_PES; #else header->rtp_hdr.raw[1] = RTP_PAYLOAD_TYPE_PES; #endif header->rtp_hdr.seq = htons(m_RtpSeqNo & 0xFFFF); /*header->rtp_hdr.ts = htonl((uint32_t)(RtpScr.Now() & 0xffffffff));*/ /*header->rtp_hdr.ssrc = htonl(m_ssrc);*/ // RTP header extension header->hdr_ext.hdr.size = htons(RTP_HEADER_EXT_X_SIZE); header->hdr_ext.hdr.type = htons(RTP_HEADER_EXT_X_TYPE); // UDP header header->hdr_ext.pos = htonull(StreamPos); header->hdr_ext.seq = htons(m_SeqNo); header->hdr_ext.stream = (uint8_t)StreamId; header->hdr_ext.padding1 = 0; m_RtpSeqNo = (m_RtpSeqNo + 1) & 0xFFFF; m_SeqNo = (m_SeqNo + 1) & UDP_SEQ_MASK; return header; } }; #endif xineliboutput-2.0.0/tools/ts.h0000644000175000017500000001417113061253352014167 0ustar phph/* * ts.h: MPEG-TS header definitions * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_TS_H_ #define _XINELIBOUTPUT_TS_H_ #ifdef __cplusplus extern "C" { #endif /* Avoid warnings when included to VDR plugin */ #undef TS_SYNC_BYTE #undef TS_SIZE #undef TS_PAYLOAD_EXISTS #undef TS_ADAPT_FIELD_EXISTS #undef TS_PAYLOAD_START #undef TS_ERROR #undef TS_PID_MASK_HI /* * Constants */ #define TS_SYNC_BYTE 0x47 #define TS_SIZE 188 #define TS_ADAPT_FIELD_EXISTS 0x20 #define TS_PAYLOAD_EXISTS 0x10 #define TS_PAYLOAD_START 0x40 #define TS_ERROR 0x80 #define TS_PID_MASK_HI 0x1F #define ts_HAS_PAYLOAD(ts) ((ts)[3] & TS_PAYLOAD_EXISTS) #define ts_PAYLOAD_START(ts) ((ts)[1] & TS_PAYLOAD_START) #define ts_HAS_ERROR(ts) ((ts)[1] & TS_ERROR) #define ts_PID(ts) ((((ts)[1] & TS_PID_MASK_HI) << 8) + (ts)[2]) #define ts_PAYLOAD_OFFSET(ts) (((ts)[3] & TS_ADAPT_FIELD_EXISTS) ? (ts)[4] + 5 : 4) #define ts_GET_PAYLOAD(ts) ((ts) + ts_PAYLOAD_OFFSET(ts)) #define ts_PAYLOAD_SIZE(ts) (TS_SIZE - ts_PAYLOAD_OFFSET(ts)) #define ts_ADAPT_FIELD_EXISTS(ts) ((ts)[3] & TS_ADAPT_FIELD_EXISTS) #define ts_ADAPT_FIELD_LENGTH(ts) (ts_ADAPT_FIELD_EXISTS(ts) ? (ts)[4] : 0) #define DATA_IS_TS(data) ((data)[0] == TS_SYNC_BYTE) /* * stream types (PMT) */ typedef enum { ISO_11172_VIDEO = 0x01, /* ISO/IEC 11172 Video */ ISO_13818_VIDEO = 0x02, /* ISO/IEC 13818-2 Video */ ISO_11172_AUDIO = 0x03, /* ISO/IEC 11172 Audio */ ISO_13818_AUDIO = 0x04, /* ISO/IEC 13818-3 Audi */ ISO_13818_PRIVATE = 0x05, /* ISO/IEC 13818-1 private sections */ ISO_13818_PES_PRIVATE = 0x06, /* ISO/IEC 13818-1 PES packets containing private data */ ISO_13522_MHEG = 0x07, /* ISO/IEC 13512 MHEG */ ISO_13818_DSMCC = 0x08, /* ISO/IEC 13818-1 Annex A DSM CC */ ISO_13818_TYPE_A = 0x0a, /* ISO/IEC 13818-6 Multiprotocol encapsulation */ ISO_13818_TYPE_B = 0x0b, /* ISO/IEC 13818-6 DSM-CC U-N Messages */ ISO_13818_TYPE_C = 0x0c, /* ISO/IEC 13818-6 Stream Descriptors */ ISO_13818_TYPE_D = 0x0d, /* ISO/IEC 13818-6 Sections (any type, including private data) */ ISO_13818_AUX = 0x0e, /* ISO/IEC 13818-1 auxiliary */ ISO_13818_PART7_AUDIO = 0x0f, /* ISO/IEC 13818-7 Audio with ADTS transport sytax */ ISO_14496_PART2_VIDEO = 0x10, /* ISO/IEC 14496-2 Visual (MPEG-4) */ ISO_14496_PART3_AUDIO = 0x11, /* ISO/IEC 14496-3 Audio with LATM transport syntax */ ISO_14496_PART10_VIDEO = 0x1b, /* ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264) */ STREAM_VIDEO_HEVC = 0x24, STREAM_VIDEO_MPEG = 0x80, STREAM_AUDIO_AC3 = 0x81, STREAM_VIDEO_VC1 = 0xea, /* private streams, payload type detected from PMT stream descriptor tag */ STREAM_AUDIO_EAC3 = 0x7a06, // ISO_13818_PES_PRIVATE | (STREAM_DESCR_EAC3 << 8) STREAM_AUDIO_DTS = 0x7b06, // ISO_13818_PES_PRIVATE | (STREAM_DESCR_DTS << 8) STREAM_AUDIO_AAC = 0x7c06, // ISO_13818_PES_PRIVATE | (STREAM_DESCR_AAC << 8) STREAM_DVBSUB = 0x5906, // ISO_13818_PES_PRIVATE | (STREAM_DESCR_DVBSUB << 8) /* HDMV */ STREAM_HDMV = 0xff00, HDMV_AUDIO_80_PCM = 0x80 | STREAM_HDMV, /* BluRay PCM */ HDMV_AUDIO_82_DTS = 0x82 | STREAM_HDMV, /* DTS */ HDMV_AUDIO_83_TRUEHD = 0x83 | STREAM_HDMV, /* Dolby TrueHD, primary audio */ HDMV_AUDIO_84_EAC3 = 0x84 | STREAM_HDMV, /* Dolby Digital plus, primary audio */ HDMV_AUDIO_85_DTS_HRA = 0x85 | STREAM_HDMV, /* DTS-HRA */ HDMV_AUDIO_86_DTS_HD_MA = 0x86 | STREAM_HDMV, /* DTS-HD Master audio */ HDMV_SPU_90_BITMAP = 0x90 | STREAM_HDMV, /* Presentation Graphics */ HDMV_SPU_91_INTERACTIVE = 0x91 | STREAM_HDMV, /* Interactive Graphics */ HDMV_SPU_92_TEXT = 0x92 | STREAM_HDMV, /* Text subtitles */ } ts_stream_type; /* stream info descriptors */ #define STREAM_DESCR_DVBSUB 0x59 #define STREAM_DESCR_AC3 0x6a #define STREAM_DESCR_EAC3 0x7a #define STREAM_DESCR_DTS 0x7b #define STREAM_DESCR_AAC 0x7c /* * PAT */ #define TS_MAX_PROGRAMS 64 #define TS_MAX_PMTS 32 #define TS_MAX_AUDIO_TRACKS 32 #define TS_MAX_SPU_TRACKS 32 typedef struct { uint16_t program_number[TS_MAX_PROGRAMS]; uint16_t pmt_pid[TS_MAX_PROGRAMS]; uint8_t version; uint32_t crc32; uint8_t pat_changed_flag; } pat_data_t; /* * ts_parse_pat() * * returns n : number of programs in PAT * 0 : error or unchanged PAT */ int ts_parse_pat(pat_data_t *pat_data, const uint8_t *ts_data); /* * PMT */ #define INVALID_PID 0xffff typedef struct { uint8_t *pmt; /* raw data */ uint8_t *pmt_write_ptr; uint32_t crc32; unsigned version_number; uint16_t pcr_pid; uint16_t video_pid; ts_stream_type video_type; uint8_t audio_tracks_count; uint8_t spu_tracks_count; struct { uint16_t pid; ts_stream_type type; /*uint8_t lang[8];*/ } audio_tracks[TS_MAX_AUDIO_TRACKS]; struct { uint16_t pid; uint8_t lang[8]; uint16_t comp_page_id; uint16_t aux_page_id; } spu_tracks[TS_MAX_SPU_TRACKS]; } pmt_data_t; /* * ts_parse_pmt() * * returns 1 : PMT parsed and changed * 0 : error or unchanged PMT */ int ts_parse_pmt(pmt_data_t *pmt, unsigned program_no, const uint8_t *ts_data); /* * PCR */ int64_t ts_get_pcr(const uint8_t *data); int ts_get_pcr_n(const uint8_t *pkt, int npkt, int64_t *pcr); /* * TS->ES, simple ES parsers */ typedef struct ts_state_s ts_state_t; struct video_size_s; ts_state_t *ts_state_init(size_t buffer_size); void ts_state_reset(ts_state_t *ts); void ts_state_dispose(ts_state_t *ts); int64_t ts_get_pts(ts_state_t *ts, const uint8_t *data); int ts_get_picture_type(ts_state_t *ts, const uint8_t *data, int h264); int ts_get_video_size(ts_state_t *ts, const uint8_t *data, struct video_size_s *size, ts_stream_type vid_type); #ifdef __cplusplus } /* extern "C" { */ #endif #endif // _XINELIBOUTPUT_TS_H_ xineliboutput-2.0.0/tools/ts.c0000644000175000017500000005445213061253352014170 0ustar phph/* * ts.c: MPEG-TS * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ /*#define LOG_PCR*/ /*#define LOG_PMT*/ #include #include #include #ifdef __FreeBSD__ #include #endif #ifndef LOG_MODULENAME # define LOG_MODULENAME "[mpeg-ts ] " # define SysLogLevel iSysLogLevel # include "../logdefs.h" #endif #include "mpeg.h" #include "ts.h" #include "pes.h" #ifdef LOG_PMT # define LOGPMT LOGMSG #else # define LOGPMT(x...) do {} while (0) #endif #ifdef LOG_PCR # define LOGPCR LOGMSG #else # define LOGPCR(x...) do {} while (0) #endif /* * ts_compute_crc32() * * taken from xine-lib demux_ts.c */ static uint32_t ts_compute_crc32(const uint8_t *data, uint32_t length, uint32_t crc32) { static uint32_t crc32_table[256]; static unsigned init_done = 0; if (!init_done) { uint32_t i, j, k; init_done = 1; for (i = 0 ; i < 256 ; i++) { k = 0; for (j = (i << 24) | 0x800000 ; j != 0x80000000 ; j <<= 1) k = (k << 1) ^ (((k ^ j) & 0x80000000) ? 0x04c11db7 : 0); crc32_table[i] = k; } } uint32_t i; for(i = 0; i < length; i++) crc32 = (crc32 << 8) ^ crc32_table[(crc32 >> 24) ^ data[i]]; return crc32; } /* * parse_pat() * * - parse PAT for PMT pid and program number * * modified from xine-lib demux_ts.c */ int ts_parse_pat(pat_data_t *pat, const uint8_t *pkt) { const uint8_t *original_pkt = pkt; unsigned pat_changed = 0; if (! ts_PAYLOAD_START(pkt)) { LOGMSG ("parse_pat: PAT without payload unit start indicator"); return 0; } /* jump to payload */ pkt += pkt[4]; /* pointer */ if (pkt - original_pkt > TS_SIZE) { LOGMSG("parse_pat: PAT with invalid pointer"); return 0; } unsigned section_syntax_indicator = ((pkt[6] >> 7) & 0x01) ; unsigned section_length = ((pkt[6] & 0x03) << 8) | pkt[7]; /*unsigned transport_stream_id = (pkt[8] << 8) | pkt[9];*/ unsigned version_number = (pkt[10] >> 1) & 0x1f; unsigned current_next_indicator = pkt[10] & 0x01; unsigned section_number = pkt[11]; unsigned last_section_number = pkt[12]; uint32_t crc32, calc_crc32; crc32 = pkt[section_length + 4] << 24; crc32 |= pkt[section_length + 5] << 16; crc32 |= pkt[section_length + 6] << 8; crc32 |= pkt[section_length + 7] ; if ((section_syntax_indicator != 1) || !(current_next_indicator)) { LOGMSG("parse_pat: ssi error"); return 0; } if (pkt - original_pkt > TS_SIZE - 4 - 1 - 3 - (int)section_length) { LOGMSG("parse_pat: unsupported PAT does not fit to single TS packet"); return 0; } if ((section_number != 0) || (last_section_number != 0)) { LOGMSG("parse_pat: unsoupported PAT consists of multiple (%d) sections", last_section_number); return 0; } /* Check CRC */ calc_crc32 = ts_compute_crc32 (pkt + 5, section_length + 3 - 4, 0xffffffff); if (crc32 != calc_crc32) { LOGMSG("parse_pat: invalid CRC"); return 0; } /* check if version or crc changed */ if (pat->crc32 != crc32 || pat->version != version_number) { LOGPMT("PAT changed: version %d->%d, crc32 0x%x->0x%x", pat->version, version_number, pat->crc32, crc32); pat->crc32 = crc32; pat->version = version_number; pat_changed++; } /* * Process all programs in the program loop */ const uint8_t *program; unsigned program_count; program_count = 0; for (program = pkt + 13; program < pkt + 13 + section_length - 9; program += 4) { unsigned program_number = (program[0] << 8) | program[1]; unsigned pmt_pid = ((program[2] & 0x1f) << 8) | program[3]; /* skip NIT pids */ if (program_number == 0x0000) continue; if (pat->program_number[program_count] != program_number || pat->pmt_pid[program_count] != pmt_pid) { LOGPMT("PAT: program %d changed: number %d->%d, pmt pid 0x%x->0x%x", program_count, pat->program_number[program_count], program_number, pat->pmt_pid[program_count], pmt_pid); pat->program_number[program_count] = program_number; pat->pmt_pid[program_count] = pmt_pid; pat_changed++; } LOGPMT("PAT acquired count=%d programNumber=0x%04x pmtPid=0x%04x", program_count, pat->program_number[program_count], pat->pmt_pid[program_count]); program_count++; } pat->program_number[program_count] = 0; pat->pat_changed_flag = !!pat_changed; return program_count; } /* * ts_get_reg_desc() * * Find the registration code (tag=5) and return it as a uint32_t * This should return "AC-3" or 0x41432d33 for AC3/A52 audio tracks. * * taken from xine-lib demux_ts.c */ static void ts_get_reg_desc(uint32_t *dest, const uint8_t *data, int length) { const unsigned char *d = data; while (d < (data + length)) { if (d[0] == 5 && d[1] >= 4) { *dest = (d[2] << 24) | (d[3] << 16) | (d[4] << 8) | d[5]; LOGPMT("parse_pmt: found registration format identifier 0x%.4x", *dest); return; } d += 2 + d[1]; } LOGPMT("parse_pmt: found no format id"); *dest = 0; } static int find_audio_track(pmt_data_t *pmt, unsigned pid) { int i; for (i = 0; i < pmt->audio_tracks_count; i++) { if (pmt->audio_tracks[i].pid == pid) return i; } return -1; } static inline int is_audio_descriptor(const uint8_t descriptor_tag) { switch (descriptor_tag) { case STREAM_DESCR_AC3: case STREAM_DESCR_EAC3: case STREAM_DESCR_DTS: case STREAM_DESCR_AAC: return 1; default:; } return 0; } static ts_stream_type descriptor_to_stream_type(const uint8_t descriptor_tag) { switch (descriptor_tag) { case STREAM_DESCR_DVBSUB: return STREAM_DVBSUB; case STREAM_DESCR_AC3: return STREAM_AUDIO_AC3; case STREAM_DESCR_EAC3: return STREAM_AUDIO_EAC3; case STREAM_DESCR_DTS: return STREAM_AUDIO_DTS; case STREAM_DESCR_AAC: return STREAM_AUDIO_AAC; default:; } return (ts_stream_type)0; } /* * ts_parse_pmt() * * modified from xine-lib demux_ts.c */ int ts_parse_pmt (pmt_data_t *pmt, unsigned program_no, const uint8_t *pkt) { const uint8_t *originalPkt = pkt; const uint8_t *ptr = NULL; unsigned pusi = ts_PAYLOAD_START(pkt); uint32_t section_syntax_indicator; uint32_t section_length = 0; /* to calm down gcc */ uint32_t program_number; uint32_t version_number; uint32_t current_next_indicator; uint32_t section_number; uint32_t last_section_number; uint32_t program_info_length; uint32_t crc32; uint32_t calc_crc32; uint32_t coded_length; unsigned pid; uint8_t *stream; unsigned i; int count; uint8_t len; unsigned offset = 0; uint32_t program_info_format_identifier = 0; uint8_t hdmv_pmt = 0; /* * A new section should start with the payload unit start * indicator set. We allocate some mem (max. allowed for a PM section) * to copy the complete section into one chunk. */ if (pusi) { pkt += pkt[4]; /* pointer */ offset = 1; if (pmt->pmt != NULL) free(pmt->pmt); pmt->pmt = (uint8_t *) calloc(4096, sizeof(uint8_t)); pmt->pmt_write_ptr = pmt->pmt; section_syntax_indicator = (pkt[6] >> 7) & 0x01; section_length = ((pkt[6] << 8) | pkt[7]) & 0x03ff; program_number = (pkt[8] << 8) | pkt[9]; version_number = (pkt[10] >> 1) & 0x1f; current_next_indicator = pkt[10] & 0x01; section_number = pkt[11]; last_section_number = pkt[12]; LOGPMT("PMT: section_syntax: %d", section_syntax_indicator); LOGPMT(" section_length: %d", section_length); LOGPMT(" program_number: %#.4x", program_number); LOGPMT(" version_number: %d", version_number); LOGPMT(" c/n indicator: %d", current_next_indicator); LOGPMT(" section_number: %d", section_number); LOGPMT(" last_section_number: %d", last_section_number); if ((section_syntax_indicator != 1) || !current_next_indicator) { LOGMSG("parse_pmt: ssi error"); return 0; } if (program_number != program_no) { /* several programs can share the same PMT pid */ LOGMSG("parse_pmt: program number %i, looking for %i", program_number, program_no); return 0; } if ((section_number != 0) || (last_section_number != 0)) { LOGMSG("parse_pmt: unsupported PMT (%d sections)", last_section_number); return 0; } } if (!pmt->pmt) { LOGMSG("parse_pmt: dropping PMT packet without PUSI"); return 0; } if (!pusi) { section_length = (pmt->pmt[1] << 8 | pmt->pmt[2]) & 0x03ff; version_number = (pkt[10] >> 1) & 0x1f; } count = ts_PAYLOAD_SIZE(originalPkt); ptr = originalPkt + offset + (TS_SIZE - count); len = count - offset; memcpy(pmt->pmt_write_ptr, ptr, len); pmt->pmt_write_ptr += len; if (pmt->pmt_write_ptr < pmt->pmt + section_length) { LOGPMT("parse_pmt: didn't get all PMT TS packets yet..."); return 0; } if (!section_length) { free(pmt->pmt); pmt->pmt = NULL; LOGMSG("parse_pmt: zero-length section"); return 0; } LOGPMT("parse_pmt: have all TS packets for the PMT section"); crc32 = (uint32_t) pmt->pmt[section_length+3-4] << 24; crc32 |= (uint32_t) pmt->pmt[section_length+3-3] << 16; crc32 |= (uint32_t) pmt->pmt[section_length+3-2] << 8; crc32 |= (uint32_t) pmt->pmt[section_length+3-1] ; /* Check CRC. */ calc_crc32 = ts_compute_crc32 (pmt->pmt, section_length + 3 - 4, 0xffffffff); if (crc32 != calc_crc32) { LOGMSG("parse_pmt: invalid CRC32"); return 0; } if (crc32 == pmt->crc32 && version_number == pmt->version_number) { LOGPMT("parse_pmt: PMT with CRC32=%d already parsed. Skipping.", crc32); return 0; } LOGPMT("parse_pmt: new PMT, parsing..."); pmt->crc32 = crc32; pmt->version_number = version_number; /* reset PIDs */ pmt->audio_tracks_count = 0; pmt->spu_tracks_count = 0; pmt->video_pid = INVALID_PID; /* ES definitions start here */ program_info_length = ((pmt->pmt[10] << 8) | pmt->pmt[11]) & 0x0fff; ts_get_reg_desc(&program_info_format_identifier, &pmt->pmt[12], program_info_length); if (program_info_format_identifier) { if ((program_info_format_identifier == (('H' << 24) | ('D' << 16) | ('M' << 8) | 'V'))) { LOGMSG("PMT program info has tag 0x05 (format_identifier), content HDMV (0x%x)\n", program_info_format_identifier); hdmv_pmt = 1; } else { LOGMSG("PMT program info has tag 0x05 (format_identifier), content 0x%x\n", program_info_format_identifier); } } stream = &pmt->pmt[12] + program_info_length; coded_length = 13 + program_info_length; if (coded_length > section_length) { LOGMSG("parse_pmt: PMT with inconsistent progInfo length"); return 0; } section_length -= coded_length; /* * Extract the elementary streams. */ while (section_length > 0) { unsigned int stream_info_length; pid = ((stream[1] << 8) | stream[2]) & 0x1fff; stream_info_length = ((stream[3] << 8) | stream[4]) & 0x0fff; coded_length = 5 + stream_info_length; if (coded_length > section_length) { LOGMSG("parse_pmt: PMT with inconsistent streamInfo length"); return 0; } switch (stream[0]) { case ISO_11172_VIDEO: case ISO_13818_VIDEO: case ISO_14496_PART2_VIDEO: case ISO_14496_PART10_VIDEO: case STREAM_VIDEO_VC1: case STREAM_VIDEO_HEVC: LOGPMT("parse_pmt: video pid 0x%.4x type %2.2x", pid, stream[0]); if (pmt->video_pid == INVALID_PID) { pmt->video_pid = pid; pmt->video_type = (ts_stream_type)stream[0]; } break; case ISO_11172_AUDIO: case ISO_13818_AUDIO: case ISO_13818_PART7_AUDIO: case ISO_14496_PART3_AUDIO: if (pmt->audio_tracks_count < TS_MAX_AUDIO_TRACKS) { if (find_audio_track(pmt, pid) < 0) { LOGPMT("parse_pmt: audio pid 0x%.4x type %2.2x", pid, stream[0]); pmt->audio_tracks[pmt->audio_tracks_count].pid = pid; pmt->audio_tracks[pmt->audio_tracks_count].type = (ts_stream_type)stream[0]; /* ts_get_lang_desc(pmt->audio_tracks[pmt->audio_tracks_count].lang, */ /* stream + 5, stream_info_length); */ pmt->audio_tracks_count++; } } break; case ISO_13818_PRIVATE: case ISO_13818_TYPE_C: break; case ISO_13818_PES_PRIVATE: for (i = 5; i < coded_length; i += stream[i+1] + 2) { if (is_audio_descriptor(stream[i])) { if (pmt->audio_tracks_count < TS_MAX_AUDIO_TRACKS && find_audio_track(pmt, pid) < 0) { LOGPMT("parse_pmt: PS1 audio pid 0x%.4x type %2.2x descriptor %2.2x", pid, stream[0], stream[i]); pmt->audio_tracks[pmt->audio_tracks_count].pid = pid; pmt->audio_tracks[pmt->audio_tracks_count].type = descriptor_to_stream_type(stream[i]); /* demux_ts_get_lang_desc(pmt->audio_tracks[pmt->audio_tracks_count].lang, */ /* stream + 5, stream_info_length); */ pmt->audio_tracks_count++; break; } } /* DVBSUB */ else if (stream[i] == STREAM_DESCR_DVBSUB) { unsigned pos; for (pos = i + 2; pos + 8 <= i + 2 + stream[i + 1] && pmt->spu_tracks_count < TS_MAX_SPU_TRACKS; pos += 8) { int no = pmt->spu_tracks_count; pmt->spu_tracks_count++; memcpy(pmt->spu_tracks[no].lang, &stream[pos], 3); pmt->spu_tracks[no].lang[3] = 0; pmt->spu_tracks[no].comp_page_id = (stream[pos + 4] << 8) | stream[pos + 5]; pmt->spu_tracks[no].aux_page_id = (stream[pos + 6] << 8) | stream[pos + 7]; pmt->spu_tracks[no].pid = pid; LOGPMT("parse_pmt: DVBSUB pid 0x%.4x: %s page %d %d type %2.2x", pid, pmt->spu_tracks[no].lang, pmt->spu_tracks[no].comp_page_id, pmt->spu_tracks[no].aux_page_id, STREAM_DVBSUB); } } } break; case HDMV_SPU_90_BITMAP & 0xff: case HDMV_SPU_91_INTERACTIVE & 0xff: case HDMV_SPU_92_TEXT & 0xff: if (hdmv_pmt) break; // ignore BluRay PG/IG/TextST streams /* fall thru */ case HDMV_AUDIO_80_PCM & 0xff: case HDMV_AUDIO_82_DTS & 0xff: case HDMV_AUDIO_83_TRUEHD & 0xff: case HDMV_AUDIO_84_EAC3 & 0xff: case HDMV_AUDIO_85_DTS_HRA & 0xff: case HDMV_AUDIO_86_DTS_HD_MA & 0xff: if (hdmv_pmt) { if ((pmt->audio_tracks_count < TS_MAX_AUDIO_TRACKS)) { if (find_audio_track(pmt, pid) < 0) { pmt->audio_tracks[pmt->audio_tracks_count].pid = pid; pmt->audio_tracks[pmt->audio_tracks_count].type = (ts_stream_type)(stream[0] | STREAM_HDMV); pmt->audio_tracks_count++; break; } } break; } /* fall thru */ default: /* This following section handles all the cases where the audio track info is stored * in PMT user info with stream id >= 0x80 * We first check that the stream id >= 0x80, because all values below that are * invalid if not handled above, then we check the registration format identifier * to see if it holds "AC-3" (0x41432d33) and if is does, we tag this as an audio stream. */ if ((pmt->audio_tracks_count < TS_MAX_AUDIO_TRACKS) && (stream[0] >= 0x80) ) { if (find_audio_track(pmt, pid) < 0) { uint32_t format_identifier = 0; ts_get_reg_desc(&format_identifier, stream + 5, stream_info_length); /* If no format identifier, assume A52 */ if ((format_identifier == 0x41432d33) || (format_identifier == 0)) { pmt->audio_tracks[pmt->audio_tracks_count].pid = pid; pmt->audio_tracks[pmt->audio_tracks_count].type = (ts_stream_type)stream[0]; /* ts_get_lang_desc(pmt->audio_tracks[pmt->audio_tracks_count].lang, */ /* stream + 5, stream_info_length); */ pmt->audio_tracks_count++; break; } } } else { LOGPMT("parse_pmt: unknown stream_type: 0x%.2x pid: 0x%.4x", stream[0], pid); } break; } stream += coded_length; section_length -= coded_length; } /* * Get the current PCR PID. */ pid = ((pmt->pmt[8] << 8) | pmt->pmt[9]) & 0x1fff; if (pmt->pcr_pid != pid) { if (pmt->pcr_pid == INVALID_PID) { LOGPMT("parse_pmt: pcr pid 0x%.4x", pid); } else { LOGPMT("parse_pmt: pcr pid changed 0x%.4x", pid); } pmt->pcr_pid = pid; } return 1; } /* * ts_get_pcr() */ static int ts_get_pcr_1(const uint8_t *pkt, int64_t *ppcr) { if (!ts_ADAPT_FIELD_EXISTS(pkt)) { return 0; } if (ts_HAS_ERROR(pkt)) { LOGMSG("ts_get_pcr: transport error"); return 0; } /* pcr flag ? */ if (! (pkt[5] & 0x10)) return 0; int64_t pcr; pcr = ((int64_t) pkt[6]) << 25; pcr += (int64_t) (pkt[7] << 17); pcr += (int64_t) (pkt[8] << 9); pcr += (int64_t) (pkt[9] << 1); pcr += (int64_t) ((pkt[10] & 0x80) >> 7); #ifdef LOG_PCR unsigned epcr = ((pkt[10] & 0x1) << 8) | pkt[11]; LOGPCR("ts_get_pcr: PCR: %"PRId64", EPCR: %u", pcr, epcr); #endif *ppcr = pcr; return 1; } int64_t ts_get_pcr(const uint8_t *pkt) { int64_t pcr = NO_PTS; ts_get_pcr_1(pkt, &pcr); return pcr; } int ts_get_pcr_n(const uint8_t *pkt, int npkt, int64_t *pcr) { pkt += TS_SIZE * npkt; while (npkt > 0) { npkt--; pkt -= TS_SIZE; if (ts_get_pcr_1(pkt, pcr)) return 1; } return 0; } /* * ts_state_t */ struct ts_state_s { uint8_t pusi_seen; uint8_t inside_pes; /* Scanning ES (PES start code seen and skipped) */ size_t buf_len; /* bytes queued */ size_t buf_size; /* buffer size */ uint8_t buf[0]; /* payload: partial PES / video stream header etc. */ }; ts_state_t *ts_state_init(size_t buffer_size) { if (buffer_size < 8 * TS_SIZE) buffer_size = 8 * TS_SIZE; if (buffer_size > 4*1024*1024) { LOGMSG("ERROR: ts_state_init(%zd)", buffer_size); buffer_size = 4*1024*1024; } ts_state_t *ts = (ts_state_t*)calloc(1, sizeof(ts_state_t) + buffer_size); if (ts) { ts->buf_size = buffer_size; } return ts; } void ts_state_reset(ts_state_t *ts) { if (ts) { size_t buf_size = ts->buf_size; memset(ts, 0, sizeof(ts_state_t)); ts->buf_size = buf_size; } } void ts_state_dispose(ts_state_t *ts) { free(ts); } /* * ts_add_payload() * * Add TS packet payload to buffer. * - PUSI resets the buffer * - all data before first PUSI is discarded */ static size_t ts_add_payload(ts_state_t *ts, const uint8_t *data) { /* start from PUSI */ if (!ts->pusi_seen) { if (!ts_PAYLOAD_START(data)) return 0; ts->pusi_seen = 1; ts->buf_len = 0; } if (ts->buf_len >= ts->buf_size - TS_SIZE) { LOGDBG("ts_add_payload: buffer full"); ts->buf_len -= TS_SIZE; memmove(ts->buf, ts->buf+TS_SIZE, ts->buf_len); } ssize_t len = ts_PAYLOAD_SIZE(data); if (len > 0) { memcpy(ts->buf + ts->buf_len, ts_GET_PAYLOAD(data), len); ts->buf_len += len; } return ts->buf_len; } /* * ts_skip_payload() */ static void ts_skip_payload(ts_state_t *ts, size_t n) { if (n < ts->buf_len) { ts->buf_len -= n; memmove(ts->buf, ts->buf + n, ts->buf_len); } else { ts->buf_len = 0; } } /* * ts_scan_startcode() * * - discard all data until startcode (00 00 01) is found * - returns number of bytes left */ static size_t ts_scan_startcode(ts_state_t *ts) { if (ts->buf_len > 2) { /* scan for PES or MPEG 00 00 01 */ size_t i = 0, n = ts->buf_len - 2; while (i < n) { if (ts->buf[i+1]) i += 2; else if (ts->buf[i]) i++; else if (ts->buf[i+2] != 1) i++; else break; } /* skip data until start code */ ts_skip_payload(ts, i); } return ts->buf_len; } /* * ts_get_pes() * * - scan for PES start * - return (PES) bytes queued */ static int ts_get_pes(ts_state_t *ts, const uint8_t *data) { if (ts_add_payload(ts, data) > 0) return ts_scan_startcode(ts); return 0; } /* * ts_get_pts() */ int64_t ts_get_pts(ts_state_t *ts, const uint8_t *data) { int64_t pts = NO_PTS; size_t cnt = ts_get_pes(ts, data); if (cnt > 14) { pts = pes_get_pts(ts->buf, ts->buf_len); if (pts < 0 && cnt > 2*TS_SIZE) ts_state_reset(ts); } return pts; } /* * ts_get_picture_type() */ int ts_get_picture_type(ts_state_t *ts, const uint8_t *data, int h264) { int pic = NO_PICTURE; return pic; } /* * ts_get_video_size() */ #include "h264.h" #include "mpeg.h" #include "h265.h" int ts_get_video_size(ts_state_t *ts, const uint8_t *data, video_size_t *size, ts_stream_type vid_type) { int h264 = (vid_type == ISO_14496_PART10_VIDEO); int h265 = (vid_type == STREAM_VIDEO_HEVC); /* Accumulate data. Skip all data until start code. */ if (ts_get_pes(ts, data) < 9) return 0; /* start scanning PES payload */ if (!ts->inside_pes) { /* Skip PES header */ ts_skip_payload(ts, PES_HEADER_LEN(ts->buf)); ts->inside_pes = 1; /* move to first ES header */ ts_scan_startcode(ts); } /* scan for start code */ while (ts->buf_len > 9) { uint8_t *buf = ts->buf; if (h265) { /* HEVC NAL AUD */ if (IS_H265_NAL_AUD(buf)) { if (h265_get_video_size(ts->buf, ts->buf_len, size)) { ts_state_reset(ts); return 1; } if (ts->buf_len < ts->buf_size - TS_SIZE) return 0; } } else if (h264) { /* H.264 NAL AUD */ if (IS_NAL_AUD(buf)) { if (h264_get_video_size(ts->buf, ts->buf_len, size)) { ts_state_reset(ts); return 1; } if (ts->buf_len < ts->buf_size - TS_SIZE) return 0; } } else { /* MPEG2 sequence start code */ if (IS_SC_SEQUENCE(buf)) { if (mpeg2_get_video_size(ts->buf, ts->buf_len, size)) { ts_state_reset(ts); return 1; } if (ts->buf_len < ts->buf_size - TS_SIZE) return 0; } } /* find next start code */ ts_skip_payload(ts, 4); ts_scan_startcode(ts); } return 0; } xineliboutput-2.0.0/tools/timer.h0000644000175000017500000001611113061253352014655 0ustar phph/* * timer.h: Threaded timer class * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIBOUTPUT_TIMER_H #define __XINELIBOUTPUT_TIMER_H // // cTimerCallback : timer callback handler interface // class cTimerCallback { protected: virtual bool TimerEvent() = 0; // return false to cancel timer virtual void *TargetId() { return (void*)this; } virtual int size() { return sizeof(*this); } virtual bool is(void *data, int len) { return len==sizeof(*this) && TargetId()==data; } friend class cTimerThread; public: static void Set(cTimerCallback *, unsigned int TimeoutMs); static void Cancel(cTimerCallback *); virtual ~cTimerCallback(); }; // // cTimerEvent : base class for timer events // class cTimerEvent : protected cTimerCallback { private: cTimerEvent(cTimerEvent&); protected: cTimerEvent() {}; virtual void AddEvent(unsigned int TimeoutMs); static void CancelAll(void *Target); template friend void CancelTimerEvents(TCLASS*); friend class cTimerThread; public: static void Cancel(cTimerEvent *&); }; // // make gcc 3.4.5 happy // template cTimerEvent *CreateTimerEvent(TCLASS *c, TRESULT (TCLASS::*fp)(void), unsigned int TimeoutMs); template cTimerEvent *CreateTimerEvent(TCLASS *c, TRESULT (TCLASS::*fp)(TARG1), TARG1 arg1, unsigned int TimeoutMs); template cTimerEvent *CreateTimerEvent(TCLASS *c, void (TCLASS::*fp)(void), unsigned int TimeoutMs, bool runOnce = true); template cTimerEvent *CreateTimerEvent(TCLASS *c, void (TCLASS::*fp)(TARG1), TARG1 arg1, unsigned int TimeoutMs, bool runOnce = true); // // Timer event templates // template class cTimerFunctorR0 : public cTimerEvent { public: protected: typedef TRESULT (TCLASS::*TFUNC)(void); cTimerFunctorR0(TCLASS *obj, TFUNC f, unsigned int TimeoutMs) : m_obj(obj), m_f(f) { AddEvent(TimeoutMs); } virtual ~cTimerFunctorR0() {}; virtual bool TimerEvent(void) { return (*m_obj.*m_f)(); } virtual void *TargetId() { return (void*)m_obj; } virtual int size() { return sizeof(*this); } virtual bool is(void *data, int len) { return sizeof(*this)==len && !memcmp(this,data,len); } private: TCLASS *m_obj; TFUNC m_f; friend cTimerEvent *CreateTimerEvent(TCLASS*,TFUNC,unsigned int); }; template class cTimerFunctorR1 : public cTimerEvent { public: protected: typedef TRESULT (TCLASS::*TFUNC)(TARG1); cTimerFunctorR1(TCLASS *obj, TFUNC f, TARG1 arg1, unsigned int TimeoutMs) : m_obj(obj), m_f(f), m_arg1(arg1) { AddEvent(TimeoutMs); } virtual ~cTimerFunctorR1() {}; virtual bool TimerEvent(void) { return (*m_obj.*m_f)(m_arg1); } virtual void *TargetId() { return (void*)m_obj; } virtual int size() { return sizeof(*this); } virtual bool is(void *data, int len) { return sizeof(*this)==len && !memcmp(this,data,len); } private: TCLASS *m_obj; TFUNC m_f; TARG1 m_arg1; friend cTimerEvent *CreateTimerEvent(TCLASS*,TFUNC,TARG1,unsigned int); }; template class cTimerFunctor0 : public cTimerEvent { public: protected: typedef void (TCLASS::*TFUNC)(void); cTimerFunctor0(TCLASS *obj, TFUNC f, unsigned int TimeoutMs, bool runOnce) : m_obj(obj), m_f(f), m_runAgain(!runOnce) { AddEvent(TimeoutMs); } virtual ~cTimerFunctor0() {}; virtual bool TimerEvent(void) { (*m_obj.*m_f)(); return m_runAgain; } virtual void *TargetId() { return (void*)m_obj; } virtual int size() { return sizeof(*this); } virtual bool is(void *data, int len) { return sizeof(*this)==len && !memcmp(this,data,len); } private: TCLASS *m_obj; TFUNC m_f; bool m_runAgain; friend cTimerEvent *CreateTimerEvent(TCLASS*,TFUNC,unsigned int,bool); }; template class cTimerFunctor1 : public cTimerEvent { public: protected: typedef void (TCLASS::*TFUNC)(TARG1); cTimerFunctor1(TCLASS *obj, TFUNC f, TARG1 arg1, unsigned int TimeoutMs, bool runOnce) : m_obj(obj), m_f(f), m_arg1(arg1), m_runAgain(!runOnce) { AddEvent(TimeoutMs); } virtual ~cTimerFunctor1() {}; virtual bool TimerEvent(void) { (*m_obj.*m_f)(m_arg1); return m_runAgain; } virtual void *TargetId() { return (void*)m_obj; } virtual int size() { return sizeof(*this); } virtual bool is(void *data, int len) { return sizeof(*this)==len && !memcmp(this,data,len); } private: TCLASS *m_obj; TFUNC m_f; TARG1 m_arg1; bool m_runAgain; friend cTimerEvent *CreateTimerEvent(TCLASS*,TFUNC,TARG1,unsigned int,bool); }; // // Function templates for timer event creation and cancellation // template cTimerEvent *CreateTimerEvent(TCLASS *c, TRESULT (TCLASS::*fp)(void), unsigned int TimeoutMs) { return new cTimerFunctorR0(c,fp,TimeoutMs); } template cTimerEvent *CreateTimerEvent(TCLASS *c, TRESULT (TCLASS::*fp)(TARG1), TARG1 arg1, unsigned int TimeoutMs) { return new cTimerFunctorR1(c,fp,arg1,TimeoutMs); } template cTimerEvent *CreateTimerEvent(TCLASS *c, void (TCLASS::*fp)(void), unsigned int TimeoutMs, bool runOnce = true) { return new cTimerFunctor0(c,fp,TimeoutMs,runOnce); } template cTimerEvent *CreateTimerEvent(TCLASS *c, void (TCLASS::*fp)(TARG1), TARG1 arg1, unsigned int TimeoutMs, bool runOnce = true) { return new cTimerFunctor1(c,fp,arg1,TimeoutMs,runOnce); } template void CancelTimerEvents(TCLASS *c) { cTimerEvent::CancelAll((void*)c); } // usage: // // 'this' derived from cTimerHandler: // Set timer: // cTimerCallback::Set(this, TimeoutMs); // Cancel timer: // - return false from handler or // - call cTimerCallback::Cancel(this); or // - delete 'this' object // // any function of any class: // Set timer: // - cTimerEvent *event = CreateTimerEvent(...); // example: // CreateTimerEvent(this, &cXinelibDevice::TimerEvent, 1, 1000); // -> calls this->cXinelibDevice::TimerEvent(1) every second until stopped. // Cancel timer: // - if handler returns bool: return false from handler // - handler is type of void: timer runs only once // - call cTimerEvent::Cancel(event) // Cancel all timers for object: // - Call CancelTimerEvents(object) // - Call CancelTimerEvents(this) #endif // __XINELIBOUTPUT_TIMER_H xineliboutput-2.0.0/tools/timer.c0000644000175000017500000001652113061253352014655 0ustar phph/* * timer.c: Threaded timer class * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #include #include #include "timer.h" //#define XINELIBOUTPUT_DEBUG //#define XINELIBOUTPUT_DEBUG_STDOUT #ifdef XINELIBOUTPUT_DEBUG # include "logdefs.h" #else # define TRACE(x) # define TRACEF(x) #endif // ---------------------------- cTimerThreadEvent ---------------------------- class cTimerThreadEvent : public cListObject { public: cTimerThreadEvent(cTimerCallback *Handler, unsigned int TimeoutMs, bool DeleteOnCancel = false) : m_Handler(Handler), m_DeleteOnCancel(DeleteOnCancel), m_TimeoutMs(TimeoutMs) { m_NextEventTime = cTimeMs::Now(); UpdateEventTime(); } ~cTimerThreadEvent() { if(m_DeleteOnCancel && m_Handler) delete m_Handler; } void UpdateEventTime() { m_NextEventTime += m_TimeoutMs; } int TimeToNextEvent(void) { return m_NextEventTime - cTimeMs::Now(); } virtual bool operator< (const cListObject &ListObject) { const cTimerThreadEvent *o = (cTimerThreadEvent *)&ListObject; return m_NextEventTimem_NextEventTime; } virtual int Compare(const cListObject &ListObject) const { const cTimerThreadEvent *o = (cTimerThreadEvent *)&ListObject; if(m_NextEventTimem_NextEventTime) return -1; else if(m_NextEventTime>o->m_NextEventTime) return 1; return 0; } cTimerCallback *m_Handler; protected: bool m_DeleteOnCancel; unsigned int m_TimeoutMs; int64_t m_NextEventTime; }; // ------------------------------- cTimerThread ------------------------------ class cTimerThread : public cThread { private: cTimerThread(cTimerThread&); // copy not allowed static cMutex m_InstanceLock; static cTimerThread *m_Instance; // singleton cMutex m_Lock; cCondVar m_Signal; cList m_Events; cTimerThreadEvent *m_RunningEvent; bool m_Finished; bool m_HandlerRunning; cTimerThread() : m_RunningEvent(NULL), m_Finished(false), m_HandlerRunning(false) { } virtual ~cTimerThread() { m_Lock.Lock(); cTimerThreadEvent *ev; while(NULL != (ev = m_Events.First())) { m_Events.Del(ev,true); } m_Lock.Unlock(); m_Signal.Broadcast(); Cancel(1); } protected: virtual void Action() { TRACEF("cTimerThread::Action"); m_Lock.Lock(); while(m_Events.First()) { m_Signal.TimedWait(m_Lock, max(1, m_Events.First()->TimeToNextEvent())); TRACE("cTimerThread::Action waked up"); while(NULL != (m_RunningEvent = m_Events.First()) && m_RunningEvent->TimeToNextEvent() <= 0) { TRACE("cTimerThread::Action calling handler"); m_HandlerRunning=true; // m_Lock.Unlock(); // - can't unlock or running timer handler may be deleted while // executing (or thread may be killed by Delete) bool result = m_RunningEvent->m_Handler->TimerEvent(); // m_Lock.Lock(); m_HandlerRunning=false; if(!result) { if(m_RunningEvent) { // check if event was cancelled in handler... TRACE("cTimerThread::Action handler cancelled timer"); m_Events.Del(m_RunningEvent, true); } } else { if(m_RunningEvent) { TRACE("cTimerThread::Action timer re-scheduled"); m_RunningEvent->UpdateEventTime(); m_Events.Sort(); } } m_RunningEvent = NULL; } } m_Finished = true; m_Lock.Unlock(); } void Add(cTimerThreadEvent *Event) { TRACEF("cTimerThread::Add"); //m_Events.Del(Event, false); Event->Unlink(); Del(Event->m_Handler); m_Events.Add(Event); m_Events.Sort(); } bool Del(cTimerCallback *Handler, void *TargetId=NULL, bool inDestructor=false) { TRACEF("cTimerThread::Del"); cTimerThreadEvent *ev = m_Events.First(); while(ev) { if(ev->m_Handler == Handler || (TargetId && ev->m_Handler->TargetId() == TargetId) || (Handler && ev->m_Handler->is(Handler,Handler->size()))) { cTimerThreadEvent *nev = m_Events.Next(ev); if(inDestructor) ev->m_Handler=NULL; m_Events.Del(ev, true); ev = nev; } else ev = m_Events.Next(ev); } if(m_RunningEvent && (m_RunningEvent->m_Handler == Handler || m_RunningEvent->m_Handler->TargetId() == TargetId)) m_RunningEvent = NULL; return !m_HandlerRunning && !m_RunningEvent && !m_Events.First(); } public: static void AddEvent(cTimerCallback *Handler, unsigned int TimeoutMs, bool DeleteOnCancel=false) { TRACEF("cTimerThread::AddEvent"); m_InstanceLock.Lock(); if(m_Instance && m_Instance->m_Finished) { delete m_Instance; m_Instance = NULL; } if(!m_Instance) { m_Instance = new cTimerThread; m_Instance->m_Lock.Lock(); m_Instance->Start(); } else { m_Instance->m_Lock.Lock(); m_Instance->m_Signal.Broadcast(); } m_Instance->Add(new cTimerThreadEvent(Handler, max(1U,TimeoutMs), DeleteOnCancel)); m_Instance->m_Lock.Unlock(); m_InstanceLock.Unlock(); } static void CancelEvent(cTimerCallback *Handler, void *TargetId = NULL, bool inDestructor=false) { TRACEF("cTimerThread::CancelEvent"); m_InstanceLock.Lock(); if(m_Instance && !m_Instance->m_Finished) { m_Instance->m_Lock.Lock(); if(m_Instance->Del(Handler, TargetId, inDestructor) && !inDestructor) { m_Instance->m_Lock.Unlock(); delete m_Instance; m_Instance = NULL; } else m_Instance->m_Lock.Unlock(); } m_InstanceLock.Unlock(); } }; cMutex cTimerThread::m_InstanceLock; cTimerThread *cTimerThread::m_Instance = NULL; // ------------------------------ cTimerCallback ----------------------------- cTimerCallback::~cTimerCallback() { TRACEF("cTimerCallback::~cTimerCallback"); cTimerThread::CancelEvent(this, NULL, true); } void cTimerCallback::Set(cTimerCallback *handler, unsigned int TimeoutMs) { TRACEF("cTimerCallback::Set"); cTimerThread::AddEvent(handler, TimeoutMs); } void cTimerCallback::Cancel(cTimerCallback *handler) { TRACEF("cTimerCallback::Cancel"); cTimerThread::CancelEvent(handler); } // ------------------------------- cTimerEvent ------------------------------- //cTimerEvent::cTimerEvent(unsigned int TimeoutMs) //{ // TRACEF("cTimerEvent::cTimerEvent"); //// cTimerThread::AddEvent(this, TimeoutMs, true); //} void cTimerEvent::AddEvent(unsigned int TimeoutMs) { TRACEF("cTimerEvent::AddEvent"); cTimerThread::AddEvent(this, TimeoutMs, true); } void cTimerEvent::Cancel(cTimerEvent *&event) { TRACEF("cTimerEvent::Cancel"); cTimerThread::CancelEvent(event); event = NULL; } void cTimerEvent::CancelAll(void *Target) { TRACEF("cTimerEvent::CancelAll"); cTimerThread::CancelEvent(NULL, Target); } xineliboutput-2.0.0/tools/time_pts.h0000644000175000017500000000145513061253352015366 0ustar phph/* * time_pts.h: Adjustable clock in PTS units * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __TIME_PTS_H #define __TIME_PTS_H #include // int64_t #include // struct timeval class cTimePts { private: int64_t begin; /* Start time (PTS) */ struct timeval tbegin; /* Start time (real time) */ bool m_Paused; int m_Multiplier; int m_ScrSpeed; static int m_Monotonic; static void Init(void); public: cTimePts(void); int64_t Now(void) const; void Set(int64_t Pts = 0LL); void Pause(void); void Resume(void); void TrickSpeed(const int Multiplier); void SetScrSpeed(const int ScrSpeed = 90000); }; #endif // __TIME_PTS_H xineliboutput-2.0.0/tools/time_pts.c0000644000175000017500000000643113061253352015360 0ustar phph/* * time_pts.c: Adjustable clock in PTS units * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_FORMAT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include "../logdefs.h" // logging #include "time_pts.h" #define MAX_SCR ((int64_t)0x1ffffffffLL) #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) #else # warning Posix monotonic clock not available #endif int cTimePts::m_Monotonic = -1; void cTimePts::Init(void) { #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) if(m_Monotonic >= 0) return; m_Monotonic = 0; struct timespec resolution; if(clock_getres(CLOCK_MONOTONIC, &resolution)) { LOGERR("cTimePts: clock_getres(CLOCK_MONOTONIC) failed"); return; } LOGDBG("cTimePts: clock_gettime(CLOCK_MONOTONIC): clock resolution %d us", ((int)resolution.tv_nsec) / 1000); if( resolution.tv_sec == 0 && resolution.tv_nsec <= 1000000 ) { struct timespec tp; if(clock_gettime(CLOCK_MONOTONIC, &tp)) { LOGERR("cTimePts: clock_gettime(CLOCK_MONOTONIC) failed"); } else { LOGDBG("cTimePts: using monotonic clock"); m_Monotonic = 1; } } #endif } cTimePts::cTimePts(void) { m_Paused = false; m_ScrSpeed = 90000; m_Multiplier = 90000; Init(); Set(); } int64_t cTimePts::Now(void) const { if(m_Paused) return begin; struct timeval t; #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) if(m_Monotonic) { struct timespec tp; if(clock_gettime(CLOCK_MONOTONIC, &tp)) { LOGERR("cTimePts: clock_gettime(CLOCK_MONOTONIC) failed"); return -1; } t.tv_sec = tp.tv_sec; t.tv_usec = tp.tv_nsec/1000; } else if (gettimeofday(&t, NULL)) { LOGERR("cTimePts: gettimeofday() failed"); return -1; } #else if (gettimeofday(&t, NULL)) { LOGERR("cTimePts: gettimeofday() failed"); return -1; } #endif t.tv_sec -= tbegin.tv_sec; if(t.tv_usec < tbegin.tv_usec) { t.tv_sec--; t.tv_usec += 1000000; } t.tv_usec -= tbegin.tv_usec; int64_t pts = 0; pts += (int64_t)t.tv_sec * (int64_t)m_ScrSpeed; pts += (int64_t)t.tv_usec * (int64_t)m_ScrSpeed / INT64_C(1000000); if(m_Multiplier != 90000) pts = pts * m_Multiplier / INT64_C(90000); return ( pts + begin ) & MAX_SCR; } void cTimePts::Set(int64_t Pts) { begin = Pts; #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) if(m_Monotonic) { struct timespec tp; if(!clock_gettime(CLOCK_MONOTONIC, &tp)) { tbegin.tv_sec = tp.tv_sec; tbegin.tv_usec = tp.tv_nsec/1000; return; } LOGERR("cTimePts: clock_gettime(CLOCL_MONOTONIC) failed"); m_Monotonic = 0; } #endif gettimeofday(&tbegin, NULL); } void cTimePts::Pause(void) { Set(Now()); m_Paused = true; } void cTimePts::Resume(void) { if(m_Paused) { Set(begin); m_Paused = false; } } void cTimePts::TrickSpeed(const int Multiplier) { Set(Now()); if(Multiplier < 0) m_Multiplier = 90000 * (-Multiplier); else if(Multiplier > 0) m_Multiplier = 90000 / Multiplier; else LOGERR("cTimePts::SetSpeed: Multiplier=%d", Multiplier); } void cTimePts::SetScrSpeed(const int ScrSpeed) { Set(Now()); m_ScrSpeed = ScrSpeed; } xineliboutput-2.0.0/tools/time_ms.h0000644000175000017500000000114113061253352015167 0ustar phph/* * time_ms.h: simple current time interface * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_TIME_MS_H_ #define XINELIBOUTPUT_TIME_MS_H_ #include static uint64_t time_ms(void) { struct timeval t; #ifdef XINEUTILS_H if (xine_monotonic_clock(&t, NULL) == 0) #else if (gettimeofday(&t, NULL) == 0) #endif return ((uint64_t)t.tv_sec) * 1000ULL + t.tv_usec / 1000ULL; return 0; } static uint64_t elapsed(uint64_t t) { return time_ms() - t; } #endif /* XINELIBOUTPUT_TIME_MS_H_ */ xineliboutput-2.0.0/tools/sys_cap.h0000644000175000017500000000175213061253352015203 0ustar phph/* * sys_cap.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_SYS_CAP_H_ #define _XINELIBOUTPUT_SYS_CAP_H_ #include "../features.h" #include #include #ifdef HAVE_LIBCAP # include #endif #ifdef HAVE_LIBCAP static inline cap_flag_value_t get_cap_flag(cap_value_t cap) { cap_flag_value_t val = CAP_CLEAR; cap_t caps = cap_get_proc(); if (!caps) { LOGDBG("cap_get_proc() failed"); return val; } if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val)) { LOGDBG("cap_get_flag(%d) failed", cap); } cap_free(caps); return val; } #endif static inline int have_cap_sys_nice(void) { #ifdef HAVE_LIBCAP if (get_cap_flag(CAP_SYS_NICE) == CAP_SET) { LOGDBG("Have CAP_SYS_NICE capability"); return 1; } LOGDBG("No CAP_SYS_NICE capability"); #endif return geteuid() == 0; } #endif // _XINELIBOUTPUT_SYS_CAP_H_ xineliboutput-2.0.0/tools/section_lock.h0000644000175000017500000000064713061253352016220 0ustar phph/* * section_lock.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __SECTION_LOCK_H #define __SECTION_LOCK_H #include class cSectionLock { private: cMutex& mutex; public: cSectionLock(cMutex& Mutex) : mutex(Mutex) { mutex.Lock(); }; ~cSectionLock() { mutex.Unlock(); }; }; #endif // __SECTION_LOCK_H xineliboutput-2.0.0/tools/sdp.h0000644000175000017500000000657313061253352014336 0ustar phph/* * sdp.h: RFC2974 Session Description Protocol (SDP) * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_SDP_H_ #define XINELIBOUTPUT_SDP_H_ #define SDP_MIME_TYPE "application/sdp" #define SDP_PAYLOAD_MPEG_PES 96 #define SDP_PAYLOAD_MPEG_TS 33 static const char *vdr_sdp_description(const char *vdr_ip, int vdr_svdrp_port, int vdr_xineliboutput_port, const char *rtp_ip, uint32_t rtp_ssrc, uint32_t payload_type, int rtp_port, int rtp_ttl) { static uint8_t s_serial = 0; static cString s_data; static char s_hostname[257] = {0}; uint64_t serial = (time(NULL) << 2) + ((s_serial++) & 0x03); cString payload; if (!s_hostname[0]) gethostname(s_hostname, 256); if (payload_type == SDP_PAYLOAD_MPEG_PES) { payload = cString::sprintf( /* video/mp2p udp/rtp */ /* media */ "m=video %d RTP/AVP 96" /* */ "\r\n" "a=rtpmap:96 MP2P/90000" , rtp_port ); } else { payload = cString::sprintf( /* video/mp2t udp/rtp */ /* media */ "m=video %d RTP/AVP 33" , rtp_port ); } s_data = cString::sprintf( /*** session ***/ /* version */ "v=0" /* origin */ "\r\n" "o=%s %u %" PRIu64 " IN IP4 %s" /* name */ "\r\n" "s=%s@%s (multicast %s:%d)" /* opt:info */ /*"\r\n" "i=vdr-xineliboutput primary device output"*/ /* time */ "\r\n" "t=0 0" /*** data stream(s) ***/ /* connection */ "\r\n" "c=IN IP4 %s/%d" /* */ "\r\n" "a=recvonly" /* */ "\r\n" "a=type:broadcast" /* */ "\r\n" "a=x-plgroup:vdr" /* *media */ "\r\n" "%s" /* media */ /*"\r\n" "m=video %d udp MP2P"*/ /* */ /*"\r\n" "a=mux:ps"*/ /* */ /*"\r\n" "a=packetformat:RAW"*/ #if 0 /*** rtsp control port ***/ /* connection */ "\r\n" "c=IN IP4 %s" /* media */ "\r\n" "m=control %d tcp/http rtsp" #endif /*** xineliboutput control port ***/ /* connection */ "\r\n" "c=IN IP4 %s" /* media */ "\r\n" "m=control %d tcp x-vdr-xineliboutput" /*** SVDRP control port ***/ /* connection */ "\r\n" "c=IN IP4 %s" /* media */ "\r\n" "m=control %d tcp x-svdrp" /* origin */ , "vdr", rtp_ssrc, serial, vdr_ip /* name */ , "vdr", s_hostname, rtp_ip, rtp_port /* media */ , rtp_ip, rtp_ttl , *payload #if 0 /* tcp/http control/rtsp */ , vdr_ip , vdr_xineliboutput_port #endif /* tcp control/x-vdr-xineliboutput */ , vdr_ip , vdr_xineliboutput_port /* tcp control/x-svdrp */ , vdr_ip , vdr_svdrp_port ); return s_data; } #endif /* XINELIBOUTPUT_SDP_H_ */ xineliboutput-2.0.0/tools/sap.h0000644000175000017500000001124713061253352014325 0ustar phph/* * sap.h: RFC2974 Session Announcement Protocol (SAP) version 2 * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_SAP_H_ #define XINELIBOUTPUT_SAP_H_ #include #if defined(__APPLE__) || defined(__FreeBSD__) # include #else # include #endif #ifndef PACKED # define PACKED __attribute__((packed)) #endif /*#define LOG_SAP*/ /* SAP IPv4 multicast addresses */ #define SAP_IP_ADDRESS_GLOBAL "224.2.127.254" /* SAPv1 IP4 global scope multicast address */ #define SAP_IP_ADDRESS_ORG "239.195.255.255" /* organization-local */ #define SAP_IP_ADDRESS_LOCAL "239.255.255.255" /* local */ #define SAP_IP_ADDRESS_LINK "224.0.0.255" /* link-local */ #define SAP_IP_TTL 255 #define SAP_UDP_PORT 9875 typedef struct { /* RFC2974: SAP (Session Announcement Protocol) version 2 PDU */ union { uint8_t raw0; struct { #if __BYTE_ORDER == __BIG_ENDIAN uint8_t version : 3; uint8_t addr_type : 1; uint8_t reserved : 1; uint8_t msg_type : 1; uint8_t encrypted : 1; uint8_t compressed : 1; #else uint8_t compressed : 1; uint8_t encrypted : 1; uint8_t msg_type : 1; uint8_t reserved : 1; uint8_t addr_type : 1; uint8_t version : 3; #endif } PACKED; } PACKED; uint8_t auth_len; uint16_t msgid_hash; union { uint8_t u8[4]; uint32_t u32; } PACKED ip4_source; char payload[0]; } PACKED sap_pdu_t; static inline sap_pdu_t *sap_create_pdu(uint32_t src_ip, uint16_t msgid, int announce, const char *payload_type, const char *payload) { sap_pdu_t *pdu; int length = sizeof(sap_pdu_t) + strlen(payload) + 3; if(payload_type) length += strlen(payload_type); if(! (pdu = (sap_pdu_t*)malloc(length))) return NULL; memset(pdu, 0, sizeof(sap_pdu_t)); pdu->version = 1; /* SAP v1 / v2 */ pdu->msg_type = announce ? 0 : 1; pdu->msgid_hash = msgid; pdu->ip4_source.u32 = src_ip; if(payload_type) { char *tmp = &pdu->payload[0]; strcpy(tmp, payload_type); tmp += strlen(tmp) + 1; strcpy(tmp, payload); } else { /* payload type defaults to application/sdp */ sprintf(&pdu->payload[0], "%s%c%c", payload, 0, 0); } return pdu; } static inline int sap_compress_pdu(sap_pdu_t *pdu) { #ifdef HAVE_ZLIB_H /* zlib compression */ Compress(); /*pdu->compressed = 1;*/ #endif /* not implemented */ pdu->compressed = 0; return -1; } static inline int sap_send_pdu(int *pfd, sap_pdu_t *pdu, uint32_t dst_ip) { int len = 0, r; int iReuse = 1, iLoop = 1, iTtl = SAP_IP_TTL; int fd; if(!pfd || *pfd < 0) { fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd < 0) { LOGERR("socket() failed (UDP/SAP multicast)"); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &iReuse, sizeof(int)) < 0 || setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &iTtl, sizeof(int)) < 0 || setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &iLoop, sizeof(int)) < 0) { LOGERR("UDP/SAP multicast setsockopt() failed."); } // Connect to multicast address struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SAP_UDP_PORT); sin.sin_addr.s_addr = dst_ip ? dst_ip : inet_addr(SAP_IP_ADDRESS_GLOBAL); if(connect(fd, (struct sockaddr *)&sin, sizeof(sin))==-1) LOGERR("UDP/SAP multicast connect() failed."); // Set to non-blocking mode if (fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK) < 0) { LOGERR("UDP/SAP multicast: error setting non-blocking mode"); } if(pfd) *pfd = fd; } else { fd = *pfd; } // size of PDU len += strlen(&pdu->payload[0]); if(!strstr(&pdu->payload[0], "\r\n")) { /* assume mime content type is present */ len += 1; len += strlen(&pdu->payload[len]); len += sizeof(sap_pdu_t); } // network order pdu->msgid_hash = htons(pdu->msgid_hash); // send r = send(fd, pdu, len, 0); if(r < 0) LOGERR("UDP/SAP multicast send() failed."); if(!pfd) close(fd); #ifdef LOG_SAP /* log PDU */ for(int i=0; i=32 && ch<127) ? ch : '.'); strcat(a, t); } LOGMSG("SAP: 0x%02x: %-50s%-18s", i/16-1, x, a); } #endif // back to host order pdu->msgid_hash = ntohs(pdu->msgid_hash); return r == len ? len : -1; } #endif /* XINELIBOUTPUT_SAP_H_ */ xineliboutput-2.0.0/tools/rtp.h0000644000175000017500000000416213061253352014345 0ustar phph/* * rtp.h: RFC1889: RTP - A Transport Protocol for Real-Time Applications * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_RTP_H_ #define XINELIBOUTPUT_RTP_H_ #if defined(__APPLE__) || defined (__FreeBSD__) # include #else # include #endif #ifndef PACKED # define PACKED __attribute__((packed)) #endif #if __BYTE_ORDER == __BIG_ENDIAN #elif __BYTE_ORDER == __LITTLE_ENDIAN #else # error __BYTE_ORDER not defined #endif #if defined __cplusplus extern "C" { #endif /* Generic RTP header extension */ typedef struct stream_rtp_header_ext { union { uint8_t raw[4]; uint32_t rawd; struct { uint16_t type; uint16_t size; /* Size of ext_data field in DWORDS */ } PACKED; } PACKED; uint8_t ext_data[0]; } PACKED stream_rtp_header_ext_t; /* Common RTP data header */ typedef struct stream_rtp_header { union { uint8_t raw[12]; struct { #if __BYTE_ORDER == __BIG_ENDIAN unsigned int version:2; /* protocol version */ unsigned int padding:1; /* padding flag */ unsigned int ext:1; /* header extension flag */ unsigned int cc:4; /* CSRC count */ unsigned int marker:1; /* marker bit */ unsigned int paytype:7; /* payload type */ #else unsigned int cc:4; /* CSRC count */ unsigned int ext:1; /* header extension flag */ unsigned int padding:1; /* padding flag */ unsigned int version:2; /* protocol version */ unsigned int paytype:7; /* payload type */ unsigned int marker:1; /* marker bit */ #endif uint16_t seq; /* sequence number */ uint32_t ts; /* timestamp */ uint32_t ssrc; /* synchronization source */ /*uint32_t csrc[0];*/ /* optional CSRC list */ } PACKED; } PACKED; union { stream_rtp_header_ext_t hdr_ext[0]; uint8_t payload[0]; } PACKED; } PACKED stream_rtp_header_t; #if defined __cplusplus }; #endif #endif /* XINELIBOUTPUT_RTP_H_ */ xineliboutput-2.0.0/tools/rtcp.h0000644000175000017500000000625313061253352014513 0ustar phph/* * rtcp.h: RFC1889: RTCP * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_RTCP_H_ #define XINELIBOUTPUT_RTCP_H_ #if defined(__APPLE__) || defined(__FreeBSD__) # include #else # include #endif #ifndef PACKED # define PACKED __attribute__((packed)) #endif #if __BYTE_ORDER == __BIG_ENDIAN #elif __BYTE_ORDER == __LITTLE_ENDIAN #else # error __BYTE_ORDER not defined #endif #if defined __cplusplus extern "C" { #endif /* RTCP packet types */ typedef enum { RTCP_SR = 200, RTCP_RR = 201, RTCP_SDES = 202, RTCP_BYE = 203, RTCP_APP = 204 } rtcp_type_t; /* RTCP SDES types */ typedef enum { RTCP_SDES_END = 0, RTCP_SDES_CNAME = 1, RTCP_SDES_NAME = 2, RTCP_SDES_EMAIL = 3, RTCP_SDES_PHONE = 4, RTCP_SDES_LOC = 5, RTCP_SDES_TOOL = 6, RTCP_SDES_NOTE = 7, RTCP_SDES_PRIV = 8 } rtcp_sdes_type_t; /* RTCP common header word */ typedef struct { union { uint8_t raw[4]; struct { #if __BYTE_ORDER == __BIG_ENDIAN unsigned int version:2; /* protocol version */ unsigned int padding:1; /* padding flag */ unsigned int count:5; /* varies by packet type */ #else unsigned int count:5; /* varies by packet type */ unsigned int padding:1; /* padding flag */ unsigned int version:2; /* protocol version */ #endif unsigned int ptype:8; /* RTCP packet type */ uint16_t length; /* pkt len in words, w/o this word */ } PACKED; } PACKED; } PACKED rtcp_common_t; /* RTCP RR (Reception report) */ typedef struct { uint32_t ssrc; /* data source being reported */ unsigned int fraction:8; /* fraction lost since last SR/RR */ int lost:24; /* cumul. no. pkts lost (signed!) */ uint32_t last_seq; /* extended last seq. no. received */ uint32_t jitter; /* interarrival jitter */ uint32_t lsr; /* last SR packet from this source */ uint32_t dlsr; /* delay since last SR packet */ } PACKED rtcp_rr_t; /* RTCP SR (Sender report) */ typedef struct { uint32_t ssrc; uint32_t ntp_sec; /* NTP timestamp, most significant word / seconds */ uint32_t ntp_frac; uint32_t rtp_ts; uint32_t psent; /* packets sent */ uint32_t osent; /* octets sent */ rtcp_rr_t rr[0]; /* variable-length list */ } PACKED rtcp_sr_t; /* RTCP SDES item */ typedef struct { uint8_t type; /* type of item (rtcp_sdes_type_t) */ uint8_t length; /* length of item (in octets) */ char data[0]; /* text, not null-terminated */ } PACKED rtcp_sdes_item_t; /* RTCP packet */ typedef struct { rtcp_common_t hdr; union { rtcp_sr_t sr; struct { uint32_t ssrc; rtcp_rr_t rr[0]; } PACKED rr; struct { uint32_t ssrc; /* first SSRC/CSRC */ rtcp_sdes_item_t item[0]; /* list of SDES items */ } PACKED sdes; struct { uint32_t src[0]; /* list of sources */ /* can't express trailing text for reason */ } PACKED bye; } PACKED; } PACKED rtcp_packet_t; #if defined __cplusplus }; #endif #endif /* XINELIBOUTPUT_RTCP_H_ */ xineliboutput-2.0.0/tools/rle.h0000644000175000017500000000452413061253352014324 0ustar phph/* * rle.h: RLE utils * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_RLE_H_ #define XINELIBOUTPUT_RLE_H_ #if defined __cplusplus extern "C" { #endif typedef enum { scale_fast = 0, /* simple pixel doubling/dropping */ scale_good_BW = 1, /* linear interpolation, palette re-generation */ } scale_mode_t; struct osd_rle_elem_s; struct osd_clut_s; unsigned rle_compress(struct osd_rle_elem_s **rle_data, const uint8_t *data, unsigned w, unsigned h); unsigned rle_recompress_net(uint8_t *raw, osd_rle_elem_t *data, unsigned elems); void rle_palette_to_argb(uint32_t *argb, const struct osd_clut_s *palette, unsigned entries); void rle_palette_to_rgba(uint32_t *rgba, const struct osd_clut_s *palette, unsigned entries); void rle_uncompress_lut8(uint8_t *dst, unsigned w, unsigned h, unsigned stride, const struct osd_rle_elem_s *rle_data, unsigned num_rle); void rle_uncompress_argb(uint32_t *dst, unsigned w, unsigned h, unsigned stride, const struct osd_rle_elem_s *rle_data, unsigned num_rle, const struct osd_clut_s *palette, unsigned palette_entries); void rle_uncompress_rgba(uint32_t *dst, unsigned w, unsigned h, unsigned stride, const struct osd_rle_elem_s *rle_data, unsigned num_rle, const struct osd_clut_s *palette, unsigned palette_entries); /* * rle_scale_nearest() * * - Simple nearest-neighbour scaling for RLE-compressed image * - fast scaling in compressed form without decompression */ struct osd_rle_elem_s *rle_scale_nearest(const struct osd_rle_elem_s *old_rle, int *rle_elems, unsigned w, unsigned h, unsigned new_w, unsigned new_h); /* * HDMV (BluRay) presentation graphics format */ size_t rle_compress_hdmv(uint8_t **rle_data, const uint8_t *data, unsigned w, unsigned h, int *num_rle); int rle_uncompress_hdmv(struct osd_rle_elem_s **data, unsigned w, unsigned h, const uint8_t *rle_data, unsigned num_rle, size_t rle_size); #if defined __cplusplus } #endif #endif /* XINELIBOUTPUT_RLE_H_ */ xineliboutput-2.0.0/tools/rle.c0000644000175000017500000002625013061253352014317 0ustar phph/* * rle.c: RLE utils * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include #include #ifdef __FreeBSD__ #include #endif #include "osd_command.h" #include "rle.h" #undef MAX #define MAX(a,b) ((a) > (b) ? (a) : (b)) /* * rle_compress() * */ unsigned rle_compress(osd_rle_elem_t **rle_data, const uint8_t *data, unsigned w, unsigned h) { osd_rle_elem_t rle, *rle_p = 0, *rle_base; unsigned x, y, num_rle = 0, rle_size = 8128; const uint8_t *c; rle_p = (osd_rle_elem_t*)malloc(4*rle_size); rle_base = rle_p; for (y = 0; y < h; y++) { rle.len = 0; rle.color = 0; c = data + y * w; for (x = 0; x < w; x++, c++) { if (rle.color != *c) { if (rle.len) { if ( (num_rle + h-y+1) > rle_size ) { rle_size *= 2; rle_base = (osd_rle_elem_t*)realloc( rle_base, 4*rle_size ); rle_p = rle_base + num_rle; } *rle_p++ = rle; num_rle++; } rle.color = *c; rle.len = 1; } else { rle.len++; } } *rle_p++ = rle; num_rle++; } *rle_data = rle_base; return num_rle; } /* * rle_recompress_net() * * recompress RLE-compressed OSD using variable sized RLE codewords */ unsigned rle_recompress_net(uint8_t *raw, osd_rle_elem_t *data, unsigned elems) { uint8_t *raw0 = raw; unsigned i; for (i = 0; i < elems; i++) { uint16_t len = data[i].len; uint16_t color = data[i].color; if (len >= 0x80) { *(raw++) = (len>>8) | 0x80; *(raw++) = (len & 0xff); } else { *(raw++) = (len & 0x7f); } *(raw++) = color; } return (raw - raw0); } /* * rle_scale_nearest() * * - Simple nearest-neighbour scaling for RLE-compressed image * - fast scaling in compressed form without decompression */ osd_rle_elem_t *rle_scale_nearest(const osd_rle_elem_t *old_rle, int *rle_elems, unsigned w, unsigned h, unsigned new_w, unsigned new_h) { #define FACTORBASE 0x100 #define FACTOR2PIXEL(f) ((f)>>8) #define SCALEX(x) FACTOR2PIXEL(factor_x*(x)) #define SCALEY(y) FACTOR2PIXEL(factor_y*(y)) unsigned old_w = w, old_h = h; unsigned old_y = 0, new_y = 0; unsigned factor_x = FACTORBASE*new_w/old_w; unsigned factor_y = FACTORBASE*new_h/old_h; unsigned rle_size = MAX(8128, *rle_elems * new_h/h ); /* guess ... */ unsigned num_rle = 0; osd_rle_elem_t *new_rle = (osd_rle_elem_t*)malloc(sizeof(osd_rle_elem_t)*rle_size); osd_rle_elem_t *new_rle_start = new_rle; /* we assume rle elements are breaked at end of line */ while (old_y < old_h) { unsigned elems_current_line = 0; unsigned old_x = 0, new_x = 0; while (old_x < old_w) { unsigned new_x_end = SCALEX(old_x + old_rle->len); if (new_x_end > new_w) { new_x_end = new_w; } new_rle->len = new_x_end - new_x; new_rle->color = old_rle->color; old_x += old_rle->len; old_rle++; /* may be incremented to last element + 1 (element is not accessed anymore) */ if (new_rle->len > 0) { new_x += new_rle->len; new_rle++; num_rle++; elems_current_line++; if ( (num_rle + 1) >= rle_size ) { rle_size *= 2; new_rle_start = (osd_rle_elem_t*)realloc( new_rle_start, 4*rle_size); new_rle = new_rle_start + num_rle; } } } if (new_x < new_w) (new_rle-1)->len += new_w - new_x; old_y++; new_y++; if (factor_y > FACTORBASE) { /* scale up -- duplicate current line ? */ int dup = SCALEY(old_y) - new_y; /* if no lines left in (old) rle, copy all lines still missing from new */ if (old_y == old_h) dup = new_h - new_y - 1; while (dup-- && (new_y+1= rle_size ) { rle_size *= 2; new_rle_start = (osd_rle_elem_t*)realloc( new_rle_start, 4*rle_size); new_rle = new_rle_start + num_rle; } /* duplicate previous line */ prevline = new_rle - elems_current_line; for (n = 0; n < elems_current_line; n++) { *new_rle++ = *prevline++; num_rle++; } new_y++; } } else if (factor_y < FACTORBASE) { /* scale down -- drop next line ? */ unsigned skip = new_y - SCALEY(old_y); if (old_y == old_h-1) { /* one (old) line left ; don't skip it if new rle is not complete */ if (new_y < new_h) skip = 0; } while (skip-- && old_ylen; old_rle++; } old_y++; } } } *rle_elems = num_rle; return new_rle_start; } /* * encode single HDMV PG rle element */ static uint8_t *write_rle_hdmv(uint8_t *rle_data, unsigned color, unsigned len) { /* short non-transparent sequences are uncompressed */ if (color && len < 4) { unsigned i; for (i = 0; i < len; i++) { *rle_data++ = color; } return rle_data; } /* rle code marker */ *rle_data++ = 0; if (!color) { /* transparent */ if (len < 64) { *rle_data++ = len; } else { *rle_data++ = 0x40 | ((len >> 8) & 0x3f); *rle_data++ = len & 0xff; } } else { if (len < 64) { *rle_data++ = 0x80 | len; } else { *rle_data++ = 0x80 | 0x40 | ((len >> 8) & 0x3f); *rle_data++ = len & 0xff; } *rle_data++ = color; } return rle_data; } /* * compress LUT8 image using HDMV PG compression algorithm */ size_t rle_compress_hdmv(uint8_t **rle_data, const uint8_t *data, unsigned w, unsigned h, int *num_rle) { unsigned y; size_t rle_size = 0; uint8_t *rle = NULL; *rle_data = NULL; *num_rle = 0; for (y = 0; y < h; y++) { /* grow buffer ? */ if ((ssize_t)(rle_size - (rle - *rle_data)) < w * 4) { size_t used = rle - *rle_data; rle_size = rle_size < 1 ? w*h/16 : rle_size*2; *rle_data = realloc(*rle_data, rle_size); rle = *rle_data + used; } /* compress line */ unsigned color = *data; unsigned len = 1; unsigned x = 1; for (x = 1; x < w; x++) { if (data[x] == color) { len++; } else { rle = write_rle_hdmv(rle, color, len); (*num_rle)++; color = data[x]; len = 1; } } if (len) { rle = write_rle_hdmv(rle, color, len); (*num_rle)++; } /* end of line marker */ rle = write_rle_hdmv(rle, 0, 0); (*num_rle)++; data += w; } return rle - *rle_data; } int rle_uncompress_hdmv(osd_rle_elem_t **data, unsigned w, unsigned h, const uint8_t *rle_data, unsigned num_rle, size_t rle_size) { unsigned rle_count = 0, x = 0, y = 0; osd_rle_elem_t *rlep = calloc(2*num_rle, sizeof(osd_rle_elem_t)); const uint8_t *end = rle_data + rle_size; *data = rlep; /* convert to xine-lib rle format */ while (y < h) { if (rle_data >= end || rle_count >= 2*num_rle) { free(*data); *data = NULL; return -1 - (rle_data >= end); } /* decode RLE element */ unsigned byte = *rle_data++; if (byte) { rlep->color = byte; rlep->len = 1; } else { byte = *rle_data++; if (!(byte & 0x80)) { rlep->color = 0; if (!(byte & 0x40)) rlep->len = byte & 0x3f; else rlep->len = ((byte & 0x3f) << 8) | *rle_data++; } else { if (!(byte & 0x40)) rlep->len = byte & 0x3f; else rlep->len = ((byte & 0x3f) << 8) | *rle_data++; rlep->color = *rle_data++; } } /* move to next element */ if (rlep->len > 0) { if (rlep->len == 1 && x && rlep[-1].color == rlep->color) { rlep[-1].len++; x++; } else { x += rlep->len; rlep++; rle_count++; } if (x > w) { return -9999; } } else { /* end of line marker (00 00) */ if (x < w-1) { //return -1-rlep->color - (w-x); rlep->len = w - x; rlep->color = 0xff; rlep++; rle_count++; } x = 0; y++; } } return rle_count; } void rle_uncompress_lut8(uint8_t *dst, unsigned w, unsigned h, unsigned stride, const struct osd_rle_elem_s *rle_data, unsigned num_rle) { unsigned i, pixelcounter = 0; unsigned idx = 0, line = 0; for(i = 0; i < num_rle; ++i) { uint8_t color = (rle_data + i)->color; unsigned len = (rle_data + i)->len; unsigned j; for (j = 0; j < len; ++j) { if (pixelcounter >= w) { idx += stride - pixelcounter; pixelcounter = 0; if (++line >= h) return; } dst[idx] = color; ++idx; ++pixelcounter; } } } void rle_palette_to_argb(uint32_t *argb, const struct osd_clut_s *palette, unsigned entries) { unsigned i; for (i = 0; i < entries; i++) { argb[i] = (palette[i].alpha << 24) | (palette[i].r << 16) | (palette[i].g << 8 ) | (palette[i].b ); } } void rle_palette_to_rgba(uint32_t *rgba, const struct osd_clut_s *palette, unsigned entries) { unsigned i; for (i = 0; i < entries; i++) { rgba[i] = (palette[i].r << 24) | (palette[i].g << 16) | (palette[i].b << 8 ) | (palette[i].alpha ); } } static void rle_uncompress_u32(uint32_t *dst, unsigned w, unsigned h, unsigned stride, const struct osd_rle_elem_s *rle_data, unsigned num_rle, uint32_t *lut) { unsigned i, pixelcounter = 0; unsigned idx = 0, line = 0; for(i = 0; i < num_rle; ++i) { uint32_t color = lut[(rle_data + i)->color]; unsigned len = (rle_data + i)->len; unsigned j; for (j = 0; j < len; ++j) { if (pixelcounter >= w) { idx += stride - pixelcounter; pixelcounter = 0; if (++line >= h) return; } dst[idx] = color; ++idx; ++pixelcounter; } } } void rle_uncompress_argb(uint32_t *dst, unsigned w, unsigned h, unsigned stride, const struct osd_rle_elem_s *rle_data, unsigned num_rle, const struct osd_clut_s *palette, unsigned palette_entries) { uint32_t lut[256] = {0}; if (palette_entries > 256) return; rle_palette_to_argb(lut, palette, palette_entries); rle_uncompress_u32(dst, w, h, stride, rle_data, num_rle, lut); } void rle_uncompress_rgba(uint32_t *dst, unsigned w, unsigned h, unsigned stride, const struct osd_rle_elem_s *rle_data, unsigned num_rle, const struct osd_clut_s *palette, unsigned palette_entries) { uint32_t lut[256] = {0}; if (palette_entries > 256) return; rle_palette_to_rgba(lut, palette, palette_entries); rle_uncompress_u32(dst, w, h, stride, rle_data, num_rle, lut); } xineliboutput-2.0.0/tools/playlist.h0000644000175000017500000000600213061253352015374 0ustar phph/* * playlist.h: Media player playlist * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIBOUTPUT_PLAYLIST_H #define __XINELIBOUTPUT_PLAYLIST_H #include // cString, cListObject, cList<> #include // cMutex // // cPlaylistItem // class cPlaylistItem : public cListObject { private: cPlaylistItem(); virtual int Compare(const cListObject &ListObject) const; public: cPlaylistItem(const char *filename); /* file name with full path */ cPlaylistItem(const char *filename, /* file name without path */ const char *path, const char *title = NULL, int position = -1); cString Filename; /* file name and full path */ // Metainfo (ID3 etc.) cString Title; cString Tracknumber; cString Artist; cString Album; // position in playlist (if given in playlist file) int Position; }; // // cPlaylistChangeNotify interface // class cPlaylistChangeNotify { public: virtual void PlaylistChanged(const cPlaylistItem *Item) = 0; virtual ~cPlaylistChangeNotify() {} }; // // cPlaylist // class cID3Scanner; class cPlaylist : protected cList { private: cMutex m_Lock; cString m_Name; // playlist (or folder) name cString m_Folder; // path to "root" of playlist cPlaylistItem *m_Current; // now playing unsigned int m_Version; enum { ePlaylist, eImplicit } m_Origin; cPlaylistChangeNotify *m_Menu; cID3Scanner *m_Scanner; protected: bool StoreCache(void); bool ReadCache(void); int ReadPlaylist(const char *PlaylistFile); int ScanFolder(const char *FolderName, bool Recursive = false, bool (*Filter)(const char *) = &config_t::IsAudioFile); friend class cID3Scanner; friend class cPlaylistReader; void PlaylistChanged(const cPlaylistItem *Item); public: cPlaylist(); virtual ~cPlaylist(); const cString& Name(void) const { return m_Name; } // listen for changes in playlist void Listen(cPlaylistChangeNotify *Menu = NULL); // read playlist from file or create playlist from directory tree bool Read(const char *PlaylistFile, bool Recursive = false); void StartScanner(void); void Del(cPlaylistItem *it); void Move(int From, int To); void Sort(void); int Count(void) const; // access/iterate playlist items cPlaylistItem *First(void) { return Next(NULL); } cPlaylistItem *Next(const cPlaylistItem *i); cPlaylistItem *Last(void) { return cList::Last(); } // get/set current (now playing) item cPlaylistItem *Current(void); void SetCurrent(cPlaylistItem *current); cPlaylistItem *Next(void); cPlaylistItem *Prev(void); cPlaylistItem *Seek(int Rel); static cString BuildMrl(const char *proto, const char *s1, const char *s2 = NULL, const char *s3 = NULL, const char *s4 = NULL); static cString GetEntry(cPlaylistItem *i, bool isPlaylist = false, bool isCurrent = false); }; #endif // __XINELIBOUTPUT_PLAYLIST_H xineliboutput-2.0.0/tools/playlist.c0000644000175000017500000006424513061253352015404 0ustar phph/* * playlist.c: Media player playlist * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "../features.h" #include #ifdef HAVE_LIBEXTRACTOR # include // libextractor 0.5.20 (2008-03-20) adds support for track numbers # if EXTRACTOR_VERSION < 0x00052000 # warning libextractor version too old (0.5.20 required for track numbers) # undef HAVE_LIBEXTRACTOR # endif #endif #include #include #include #include "../config.h" #include "playlist.h" #include "../logdefs.h" #ifndef PLAYLIST_CACHE # define PLAYLIST_CACHE ".xineliboutput-playlist.pls" #endif #define MAX_PLAYLIST_FILES 1024 static void strip_extension(cString& fname) { const char *ext = strrchr(fname, '.'); if (ext) fname.Truncate(ext - fname); } // // cPlaylistItem // cPlaylistItem::cPlaylistItem(const char *filename) { const char *pt; Filename = filename; Position = -1; if(NULL != (pt = strrchr(filename, '/'))) Title = pt + 1; else Title = filename; strip_extension(Title); } cPlaylistItem::cPlaylistItem(const char *filename, const char *path, const char *title, int position) { if(path[strlen(path)-1] != '/') Filename = cString::sprintf("%s/%s", path, filename); else Filename = cString::sprintf("%s%s", path, filename); Position = position; Title = title ?: filename; if (!title) strip_extension(Title); } int cPlaylistItem::Compare(const cListObject &ListObject) const { ///< Must return 0 if this object is equal to ListObject, a positive value ///< if it is "greater", and a negative value if it is "smaller". const cPlaylistItem *o = (cPlaylistItem *)&ListObject; // Use Position (if defined in playlist file) // compare as unsigned --> -1 goes to last position if(Position != o->Position) return ((unsigned int)Position) > ((unsigned int)o->Position) ? 1 : -1; // same position (or no positions definend) -> alphabetical order #if 0 return strcmp(Title, o->Title); #else // use filename, because: // - implicit playlist has no track names available when sorting // (track names are read during playback), so track name is // just file name without path. // using full path allows sorting of each album and tracks inside albums... // - "normal" playlist is ordered using Position, // so track names are never compared anyway ... return strcmp(Filename, o->Filename); #endif } // // cID3Scanner // #ifndef HAVE_LIBEXTRACTOR static const char *shell_escape(char *buf, int buflen, const cString& src, char ch) { const char *pt = *src; int n = 0; if(pt) { while(*pt && n < buflen-2) { if(*pt == ch || *pt == '\\' /*|| *pt == '\"' || *pt == '\''*/) { buf[n++] = '\\'; } buf[n++] = *pt++; } buf[n] = 0; return buf; } return ""; } #endif #if defined(HAVE_LIBEXTRACTOR) && EXTRACTOR_VERSION >= 0x00060000 static int extractor_callback_id3(void *priv, const char *plugin_name, enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format, const char *data_mime_type, const char *data, size_t data_len) { if (format == EXTRACTOR_METAFORMAT_UTF8) { cPlaylistItem *Item = (cPlaylistItem *)priv; switch (type) { case EXTRACTOR_METATYPE_TITLE: Item->Title = data; break; case EXTRACTOR_METATYPE_ARTIST: Item->Artist = data; break; case EXTRACTOR_METATYPE_ALBUM: Item->Album = data; break; case EXTRACTOR_METATYPE_TRACK_NUMBER: Item->Tracknumber = strlen(data) == 1 ? *cString::sprintf("0%s", data) : data; break; default: break; } } return 0; } #endif // defined(HAVE_LIBEXTRACTOR) && EXTRACTOR_VERSION >= 0x00060000 class cID3Scanner : public cThread { public: cPlaylist& m_List; cID3Scanner(cPlaylist& List) : cThread("Metadata scanner"), m_List(List), m_Done(false) {}; void CancelScanner(void) { Cancel(3); } private: bool m_Done; virtual void Action(void) { cPlaylistItem *Item = NULL; unsigned int Version = 0; const int priority = 10; errno = 0; if((nice(priority) == -1) && errno) LOGDBG("ID3Scanner: Can't nice to value: %d", priority); LOGDBG("ID3Scanner Started"); while(Running()) { cMutexLock ml(&m_List.m_Lock); if(Version < m_List.m_Version) { // restart after sort, add, del Item = NULL; Version = m_List.m_Version; } if(!(Item = m_List.Next(Item))) break; if(xc.IsAudioFile(Item->Filename)) { LOGDBG("Scanning metainfo for file %s", *Item->Filename); #ifdef HAVE_LIBEXTRACTOR # if EXTRACTOR_VERSION >= 0x00060000 EXTRACTOR_PluginList * plugins; plugins = EXTRACTOR_plugin_add_defaults(EXTRACTOR_OPTION_DEFAULT_POLICY); EXTRACTOR_extract(plugins, *Item->Filename, NULL, 0, (EXTRACTOR_MetaDataProcessor)&extractor_callback_id3, Item); EXTRACTOR_plugin_remove_all(plugins); /* unload plugins */ # else // EXTRACTOR_VERSION >= 0x00060000 EXTRACTOR_ExtractorList * plugins; EXTRACTOR_KeywordList * md_list; plugins = EXTRACTOR_loadDefaultLibraries(); md_list = EXTRACTOR_getKeywords(plugins, *Item->Filename); const char *key; while(md_list) { if ((key=EXTRACTOR_getKeywordTypeAsString(md_list->keywordType))) { if (!strcasecmp(key,"title")) Item->Title = md_list->keyword; else if (!strcasecmp(key,"artist")) Item->Artist = md_list->keyword; else if (!strcasecmp(key,"album")) Item->Album = md_list->keyword; else if (!strcasecmp(key,"track number")) Item->Tracknumber = cString::sprintf("%s%s", strlen(md_list->keyword) == 1 ? "0" : "", md_list->keyword); md_list=md_list->next; } } EXTRACTOR_freeKeywords(md_list); EXTRACTOR_removeAll(plugins); /* unload plugins */ # endif // EXTRACTOR_VERSION >= 0x00060000 #else // HAVE_LIBEXTRACTOR char buf[4096]; cString Cmd = ""; if(!strcasecmp((Item->Filename) + strlen(Item->Filename) - 5, ".flac")) Cmd = cString::sprintf("metaflac " " --show-tag=TITLE " " --show-tag=ALBUM " " --show-tag=ARTIST " " --show-tag=TRACKNUMBER " " \"%s\"", shell_escape(buf, sizeof(buf)-1, Item->Filename, '\"')); else Cmd = cString::sprintf("mp3info -p \"" "ARTIST=%%a\\r\\n" "ALBUM=%%l\\r\\n" "TITLE=%%t\\r\\n" "TRACKNUMBER=%%n\\r\\n\"" " \"%s\"", shell_escape(buf, sizeof(buf)-1, Item->Filename, '\"')); cPipe p; if(p.Open(*Cmd, "r")) { cReadLine r; char *pt; while(NULL != (pt = r.Read(p))) { if(!strncasecmp(pt, "ARTIST=", 7) && strlen(pt) > 8) Item->Artist = (pt+7); else if(!strncasecmp(pt, "ALBUM=", 6) && strlen(pt) > 7) Item->Album = (pt+6); else if(!strncasecmp(pt, "TITLE=", 6) && strlen(pt) > 7) Item->Title = (pt+6); else if(!strncasecmp(pt, "TRACKNUMBER=", 12) && strlen(pt) > 12) Item->Tracknumber = cString::sprintf("%s%s", strlen(pt) == 13 ? "0" : "", (pt+12)); } } #endif // HAVE_LIBEXTRACTOR } } LOGDBG("ID3Scanner Done."); m_List.PlaylistChanged(Item); m_Done = true; } }; // // cPlaylistReader // class cPlaylistReader { private: cPlaylist& m_Playlist; protected: cString m_Title; int m_Position; cPlaylistItem *Prev(void) { return m_Playlist.Last(); } public: cPlaylistReader(cPlaylist& Playlist) : m_Playlist(Playlist), m_Position(-1) {} virtual ~cPlaylistReader() {} virtual char *Parse(char *line) = 0; void ResetCache(void) { m_Title = NULL; m_Position = -1; } const char *Title(void) { return m_Title; } int Position(void) { return m_Position; } }; class cM3uReader : public cPlaylistReader { public: cM3uReader(cPlaylist& Playlist) : cPlaylistReader(Playlist), m_Next(1) {} protected: int m_Next; virtual char *Parse(char *line) { if(!*line) return NULL; if(*line == '#') { if(!strncmp(line, "#EXTINF:", 8)) { int len = -1; sscanf(line+8,"%d", &len); while(*line && *line != ',') line++; m_Title = *line ? (line+1) : NULL; m_Position = m_Next++; } return NULL; } return *line ? line : NULL; } }; class cPlsReader : public cPlaylistReader { public: cPlsReader(cPlaylist& Playlist) : cPlaylistReader(Playlist), m_Current(0) {} protected: int m_Current; virtual char *Parse(char *line) { char *t = strchr(line, '='); if(t) { int n; if(!strncasecmp(line, "file", 4) && 1 == sscanf(line + 4, "%d=", &n)) { m_Current = n; m_Position = n; if(*(t+1)) return t+1; } else if(!strncasecmp(line, "title", 5) && 1 == sscanf(line + 5, "%d=", &n)) { if(*(t+1)) { if(n == m_Current) Prev()->Title = t+1; else m_Title = t+1; } } //else if(!strncasecmp(line, "length", 6) && // 1 == sscanf(line + 4, "%d=", &n)) { //} } return NULL; } }; class cAsxReader : public cPlaylistReader { public: cAsxReader(cPlaylist& Playlist) : cPlaylistReader(Playlist) {} protected: virtual char *Parse(char *line) { char *pt = strstr(line, ""); if(!pt) pt = strstr(line, ""); if(pt) { pt += 7; if(strstr(line, "</")) *strstr(line, "</") = 0; m_Title = pt; } if(*m_Title) { pt = strstr(line, "<ENTRY>"); if(!pt) pt = strstr(line, "<entry>"); if(pt) { if(*m_Title && Prev()) { Prev()->Title = m_Title; m_Title = NULL; } } } return NULL; } }; // // cPlaylist // cPlaylist::cPlaylist() { m_Origin = eImplicit; m_Menu = NULL; m_Scanner = NULL; m_Current = NULL; m_Version = 1; } cPlaylist::~cPlaylist() { if(m_Scanner) { m_Scanner->CancelScanner(); delete m_Scanner; } if(m_Origin == eImplicit) StoreCache(); } void cPlaylist::Listen(cPlaylistChangeNotify *Menu) { cMutexLock ml(&m_Lock); m_Menu = Menu; } void cPlaylist::PlaylistChanged(const cPlaylistItem *Item) { cMutexLock ml(&m_Lock); /*if(m_Origin == eImplicit)*/ Sort(); if(m_Menu) m_Menu->PlaylistChanged(Item); } void cPlaylist::Sort(void) { cMutexLock ml(&m_Lock); cListBase::Sort(); m_Version++; } int cPlaylist::Count(void) const { return cListBase::Count(); } cPlaylistItem *cPlaylist::Next(const cPlaylistItem *i) { cMutexLock ml(&m_Lock); return i ? cList<cPlaylistItem>::Next(i) : cList<cPlaylistItem>::First(); } cPlaylistItem *cPlaylist::Current(void) { cMutexLock ml(&m_Lock); return m_Current ?: First(); } void cPlaylist::Del(cPlaylistItem *it) { cMutexLock ml(&m_Lock); if(!it || Count() < 2) return; if(m_Current == it) m_Current = cList<cPlaylistItem>::Next(Current()) ?: cList<cPlaylistItem>::Prev(Current()); cListBase::Del(it); m_Version++; } void cPlaylist::Move(int From, int To) { cMutexLock ml(&m_Lock); if (Count() < 3) return; cListBase::Move(From, To); m_Version++; } void cPlaylist::SetCurrent(cPlaylistItem *current) { cMutexLock ml(&m_Lock); m_Current = current; } cPlaylistItem *cPlaylist::Next(void) { cMutexLock ml(&m_Lock); if(Current()) return m_Current = (cList<cPlaylistItem>::Next(Current()) ?: First()); return NULL; } cPlaylistItem *cPlaylist::Prev(void) { cMutexLock ml(&m_Lock); if(Current()) return m_Current = (cList<cPlaylistItem>::Prev(Current()) ?: Last()); return NULL; } cPlaylistItem *cPlaylist::Seek(int Rel) { cMutexLock ml(&m_Lock); if (!Current()) return NULL; if (Rel > 0) { while (Rel--) m_Current = (cList<cPlaylistItem>::Next(Current()) ?: Last()); } else if (Rel < 0) { while (Rel++) m_Current = (cList<cPlaylistItem>::Prev(Current()) ?: First()); } return Current(); } bool cPlaylist::StoreCache(void) { if(!xc.cache_implicit_playlists || m_Origin != eImplicit || !*m_Folder) return false; cString Name = cString::sprintf("%s%s", *m_Folder, PLAYLIST_CACHE); int len = strlen(m_Folder), entries = 0; FILE *f = NULL; for(cPlaylistItem *i = First(); i; i=Next(i)) { // store only items in "current" root folder if(!strncmp(i->Filename, m_Folder, len)) { if(/**i->Title ||*/ *i->Artist || *i->Album) { cString Filename = ((*i->Filename) + len); // relative if(entries < 1) { f = fopen(Name, "w"); if(!f) { LOGERR("creation of metadata cache %s%s failed", *m_Folder, PLAYLIST_CACHE); return false; } fprintf(f, "[playlist]\r\n"); } entries++; fprintf(f, "File%d=%s\r\n", entries, *Filename); if(*i->Title && (*i->Title)[0]) fprintf(f, "Title%d=%s\r\n", entries, *i->Title); if(*i->Tracknumber && (*i->Tracknumber)[0]) fprintf(f, "Tracknumber%d=%s\r\n", entries, *i->Tracknumber); if(*i->Artist && (*i->Artist)[0]) fprintf(f, "Artist%d=%s\r\n", entries, *i->Artist); if(*i->Album && (*i->Album)[0]) fprintf(f, "Album%d=%s\r\n", entries, *i->Album); } } } if(entries > 0) { fprintf(f, "NumberOfEntries=%d\r\nVersion=2\r\n", entries); fclose(f); return true; } return false; } static const char *strchrnext(const char *s, char c) { return (s = strchr(s, c)) ? ((*(s+1))?(s+1):NULL) : NULL; } bool cPlaylist::ReadCache(void) { if(xc.cache_implicit_playlists && m_Origin == eImplicit && *m_Folder) { cString Name = cString::sprintf("%s%s", *m_Folder, PLAYLIST_CACHE); FILE *f = fopen(Name, "r"); if(f) { int len = strlen(m_Folder); cPlaylistItem *it = NULL; cReadLine r; const char *pt; while(NULL != (pt = r.Read(f))) { if(!strncmp(pt, "File", 4)) { it = NULL; const char *Filename = strchrnext(pt+4, '='); if(Filename && *Filename) { for(cPlaylistItem *i = First(); i; i=Next(i)) { if(!strncmp(i->Filename, m_Folder, len)) { if(!strcmp(*i->Filename + len, Filename)) { it = i; break; } } } } } else if(it && !strncmp(pt, "Title", 5)) { it->Title = strchrnext(pt, '='); } else if(it && !strncmp(pt, "Tracknumber", 11)) { it->Tracknumber = strchrnext(pt, '='); } else if(it && !strncmp(pt, "Artist", 6)) { it->Artist = strchrnext(pt, '='); } else if(it && !strncmp(pt, "Album", 5)) { it->Album = strchrnext(pt, '='); } else { /*it = NULL;*/ } } fclose(f); return true; } } return false; } #if 0 static FILE *open_http(const char *PlaylistFile) { char file[1024] = "", host[128] = "", pt; int fd, port = 80; strn0cpy(host, PlaylistFile+strlen("http://"), sizeof(host)-1); pt = strchr(host, '/'); if(pt) { strn0cpy(file, pt, sizeof(file)-1); *pt = 0; } pt = strchr(host, ':'); if(pt) { *pt++ = 0; port = atoi(pt); } fd = tcp_connect(host, port); if(fd < 0) { LOGERR("TCP connect failed"); return NULL; } int len = asprintf(&pt, "GET %s HTTP/1.1" "\r\n" "Host: %s" "\r\n" "\r\n", file, host); if(len != write(fd, pt, len)) { LOGERR("HTTP request write failed"); free(pt); close(fd); return NULL; } free(pt); int state = 0; FILE *f = fdopen(fd, "r"); cReadLine r; while(state >= 0 && NULL != (pt = r.Read(f))) { switch(state) { case 0: if(!strncmp(pt, "HTTP/1", 6) || !strstr(pt, " 200 ")) { LOGERR("HTTP error: %s", pt); fclose(f); return NULL; } state = 1; break; case 1: if(strcmp(pt, "\r\n")) break; return f; default: break; } } fclose(f); return NULL; } #endif int cPlaylist::ScanFolder(const char *FolderName, bool Recursive, bool (*Filter)(const char *)) { cMutexLock ml(&m_Lock); static int depth = 0; DIR *d = opendir(FolderName); if (d) { LOGDBG("ScanFolder(%s)", FolderName); struct dirent *e; int n = 0, warn = -1; while ((e = readdir(d)) != NULL) { cString Buffer = cString::sprintf("%s%s", FolderName, e->d_name); struct stat st; if (stat(Buffer, &st) == 0) { if(S_ISDIR(st.st_mode)) { if (Recursive && !S_ISLNK(st.st_mode)) { /* don't want to loop ... */ if(depth > 4) { LOGMSG("ScanFolder: Too deep directory tree"); } else if(e->d_name[0]=='.') { } else { if(n<MAX_PLAYLIST_FILES) { depth++; /* limit depth */ Buffer = cString::sprintf("%s/", *Buffer); n += ScanFolder(Buffer, Recursive, Filter); depth--; } else { if(!++warn) LOGMSG("ScanFolder: Found over %d matching files, list truncated!", n); break; } } } } else /* == if(!S_ISDIR(st.st_mode))*/ { // check symlink destination if (S_ISLNK(st.st_mode)) { Buffer = ReadLink(Buffer); if (!*Buffer) continue; if (stat(Buffer, &st) != 0) continue; } if((*Filter)(Buffer)) { /* TODO: Should ScanDir add contents of playlist files ... ? */ if(Filter == &config_t::IsPlaylistFile || !xc.IsPlaylistFile(Buffer)) { n++; if(n<MAX_PLAYLIST_FILES) { Add(new cPlaylistItem(e->d_name, FolderName)); //LOGDBG("ScanFolder: %s", e->d_name); } else { if(!++warn) LOGMSG("ScanFolder: Found over %d matching files, list truncated!", n); break; } } } } } } LOGDBG("ScanFolder: Found %d matching files from %s", n, FolderName); closedir(d); return n; } LOGERR("ScanFolder: Error opening %s", FolderName); return 0; } void cPlaylist::StartScanner(void) { cMutexLock ml(&m_Lock); if(m_Scanner) { if(m_Scanner->Active()) return; delete m_Scanner; m_Scanner = NULL; } /* check if cache is already up-to-date */ cString CacheName = cString::sprintf("%s%s", *m_Folder, PLAYLIST_CACHE); struct stat stf, stc; if(!stat(m_Folder, &stf)) { if(!stat(CacheName, &stc)) { //LOGDBG("ID3 Cache modified %d, folder modified %d, diff %d", // (unsigned int)stc.st_mtime, (unsigned int)stf.st_mtime, // (unsigned int)(stc.st_mtime - stf.st_mtime)); if(stc.st_mtime >= stf.st_mtime) { if(ReadCache()) { LOGDBG("cPlaylist: using up-to-date ID3 cache"); //LOGMSG(" Cache read OK."); return; } LOGMSG("cPlaylist: ID3 cache read FAILED"); } else { LOGDBG("cPlaylist: ID3 cache not up-to-date, using old cache and scanning for changes"); ReadCache(); } } //else LOGERR("cPlaylist: stat(%s) failed"); } //else LOGERR("cPlaylist: stat(%s) failed"); if(xc.enable_id3_scanner) { m_Scanner = new cID3Scanner(*this); m_Scanner->Start(); } } int cPlaylist::ReadPlaylist(const char *file) { static int depth = 0; /* limit recursion */ cPipe p; cPlaylistReader *parser = NULL; FILE *f; if(strncmp(file, "http:", 5) && strncmp(file, "https:", 6)) { f = fopen(file, "r"); } else { // fetch playlist from server using curl LOGDBG("cPlaylist: fetching remote playlist from %s", file); cString Cmd = cString::sprintf("curl %s", file); if(!p.Open(Cmd, "r")) { LOGERR("cPlaylist: CURL command (%s) failed", *Cmd); return false; } // process as normal file f = p; } if(f) { LOGDBG("cPlaylist: parsing %s", file); const char *ext = strrchr(file, '.'); if(ext && !strcasecmp(ext, ".pls")) parser = new cPlsReader(*this); else if(ext && !strcasecmp(ext, ".asx")) parser = new cAsxReader(*this); else /*if(!strcasecmp(ext, ".m3u"))*/ parser = new cM3uReader(*this); /* parses plain lists (.ram, ...) too ...*/ /* get folder */ cString Folder = file; const char *folder = strrchr(Folder, '/'); if (folder) Folder.Truncate(folder - Folder + 1); int n = 0; cReadLine r; char *pt; while(NULL != (pt = r.Read(f)) && n < MAX_PLAYLIST_FILES) { if(NULL != (pt = parser->Parse(pt))) { if(xc.IsPlaylistFile(pt)) { parser->ResetCache(); LOGDBG("cPlaylist: found playlist inside playlist"); if(depth > 4) LOGMSG("cPlaylist: recursion too deep, skipped %s", pt); else { depth++; if(*pt == '/' || (strstr(pt,"://")+1 == strchr(pt,'/') && strchr(pt,'/') - pt < 8)) n += ReadPlaylist(pt); else n += ReadPlaylist(cString::sprintf("%s%s", *Folder, pt)); depth--; } } else { if(*pt == '/' || (strstr(pt,"://")+1 == strchr(pt,'/') && strchr(pt,'/') - pt < 8)) { // absolute path Add(new cPlaylistItem(pt)); if(parser->Title()) Last()->Title = parser->Title(); } else { // relative path Add(new cPlaylistItem(pt, Folder, parser->Title())); } Last()->Position = parser->Position(); parser->ResetCache(); //LOGDBG("read_playlist: %s", pt); n++; } } } if(! (FILE*) p) fclose(f); if(n >= MAX_PLAYLIST_FILES) LOGMSG("cPlaylist: Found over %d matching files, list truncated!", n); LOGDBG("cPlaylist: Found %d matching files", n); delete parser; return n; } LOGERR("cPlaylist: Error opening %s", file); return 0; } static cString LastDir(cString& path) { cString tmp = path; const char *pt = strrchr(tmp, '/'); if(pt && pt > *tmp) { tmp.Truncate(pt - tmp); pt = strrchr(tmp, '/'); if(pt) return cString(pt+1); } return cString(NULL); } bool cPlaylist::Read(const char *PlaylistFile, bool Recursive) { cMutexLock ml(&m_Lock); bool Result = true; // extract playlist root folder if(!*m_Folder) { const char *pt; m_Folder = PlaylistFile; if (NULL != (pt=strrchr(m_Folder, '/'))) m_Folder.Truncate(pt - m_Folder + 1); } if(xc.IsPlaylistFile(PlaylistFile)) { // Read playlist file Result = ReadPlaylist(PlaylistFile); m_Origin = ePlaylist; cString dir = LastDir(m_Folder); const char *name = strrchr(PlaylistFile, '/'); name = name ? name+1 : NULL; if(*dir && name) m_Name = cString::sprintf("%s - %s", *dir, name); else m_Name = name ?: ""; strip_extension(m_Name); } else if(PlaylistFile[ 0] == '/' && PlaylistFile[strlen(PlaylistFile)-1] == '/') { // Scan folder Result = ScanFolder(PlaylistFile, Recursive) > 0; m_Origin = eImplicit; Sort(); if(!*m_Name) { m_Name = PlaylistFile; m_Name.Truncate( strrchr(m_Name, '/') - m_Name); if(strrchr(m_Name, '/')) { cString dir = LastDir(m_Name); if(*dir) m_Name = cString::sprintf("%s - %s", *dir, strrchr(m_Name, '/')+1); else m_Name = strrchr(m_Name, '/')+1; } } } else { // Single file Add(new cPlaylistItem(PlaylistFile)); m_Origin = eImplicit; if(!*m_Name) { m_Name = LastDir(m_Folder); if(!*m_Name) m_Name = ""; } } if(Count() < 1) { LOGMSG("Empty playlist %s !", PlaylistFile); Add(new cPlaylistItem(PlaylistFile)); } m_Version++; return Result; } static cString EscapeString(const char *s) { static const uint8_t hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; const uint8_t *fn = (const uint8_t*)s; int size = strlen(s) + 16; char *buf = (char *)malloc(size); int i = 0; LOGVERBOSE("EscapeString('%s')", fn); while (*fn) { if(size-7 < i) buf = (char *)realloc(buf, (size=size+16)); switch (*fn) { case 1 ... ' ': case 127 ... 255: case '#': case '%': case '?': case ':': case ';': case '\'': case '\"': case '(': case ')': buf[i++] = '%'; buf[i++] = hex[(*fn & 0xf0)>>4]; buf[i++] = hex[(*fn & 0x0f)]; break; default: buf[i++] = *fn; break; } fn++; } buf[i] = 0; LOGVERBOSE(" --> '%s'", buf); return cString(buf, true); } cString cPlaylist::BuildMrl(const char *proto, const char *s1, const char *s2, const char *s3, const char *s4) { return cString::sprintf("%s:%s%s%s%s", proto, s1 ? *EscapeString(s1) : "", s2 ? *EscapeString(s2) : "", s3 ? *EscapeString(s3) : "", s4 ? *EscapeString(s4) : ""); } cString cPlaylist::GetEntry(cPlaylistItem *i, bool isPlaylist, bool isCurrent) { cString Entry = ""; if ((*i->Artist && xc.playlist_artist) || (*i->Album && xc.playlist_album)) { Entry = cString::sprintf("%s%s%s%s%s%s(%s%s%s)", isPlaylist ? (isCurrent ? "*" : " ") : "", isPlaylist ? "\t" : " ", xc.playlist_tracknumber ? (*i->Tracknumber ?: "") : "", xc.playlist_tracknumber ? (*i->Tracknumber ? " - " : "") : "", *i->Title, isPlaylist ? "\t" : " ", xc.playlist_artist ? (*i->Artist ?: "") : "", xc.playlist_artist && xc.playlist_album ? (*i->Artist && *i->Album ? ":" : "") : "", xc.playlist_album ? (*i->Album ?: "") : ""); } else { Entry = cString::sprintf("%s%s%s%s%s", isPlaylist ? (isCurrent ? "*" : " ") : "", isPlaylist ? "\t" : " ", xc.playlist_tracknumber ? (*i->Tracknumber ?: "") : "", xc.playlist_tracknumber ? (*i->Tracknumber ? " - " : "") : "", *i->Title); } return Entry; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/pip_service_impl.h��������������������������������������������������������0000644�0001750�0001750�00000002720�13061253352�017067� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * pip_service_impl.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef PIP_SERVICE_IMPL_H #define PIP_SERVICE_IMPL_H #include "../include/vdr-xineliboutput/service_pip.h" #ifndef TS_SIZE # define TS_SIZE 188 #endif #define PIP_TS_CHUNK_SIZE (8*TS_SIZE) class cPipServiceImpl : public cXineliboutputPipService { private: cXinelibDevice *m_Dev; int m_Id; uint8_t m_Data[PIP_TS_CHUNK_SIZE + TS_SIZE]; uint m_DataLen; public: cPipServiceImpl(int Index, cXinelibDevice *Device) { m_Dev = Device; m_Id = Index; m_DataLen = 0; int X = 5 + 22 * (Index % 4); int Y = 5 + 22 * (Index / 4); int W = 20; int H = 20; m_Dev->Pip_Config(m_Id, X, Y, W, H); } private: virtual void SetPosition(uint X, uint Y, uint W, uint H) { m_Dev->Pip_Config(m_Id, X, Y, W, H); } virtual int PlayTs(const uint8_t *Data) { if (Data) { /* accumulate data */ memcpy(m_Data + m_DataLen, Data, TS_SIZE); m_DataLen += TS_SIZE; if (m_DataLen < PIP_TS_CHUNK_SIZE) return TS_SIZE; } /* flush to device */ m_Dev->Pip_Play(m_Id, m_Data, m_DataLen); m_DataLen = 0; return TS_SIZE; } virtual ~cPipServiceImpl() { m_Dev->Pip_Close(m_Id); } }; #endif /* PIP_SERVICE_IMPL_H */ ������������������������������������������������xineliboutput-2.0.0/tools/pes.h���������������������������������������������������������������������0000644�0001750�0001750�00000006175�13061253352�014335� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * pes.h: PES header definitions * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_PES_H_ #define _XINELIBOUTPUT_PES_H_ #ifdef __cplusplus extern "C" { #endif /* * Constants */ #define PES_CHUNK_SIZE 2048 #define MAX_SCR ((int64_t)0x1ffffffffLL) #define NO_PTS (INT64_C(-1)) /* PES PIDs */ #define PRIVATE_STREAM1 0xBD #define PADDING_STREAM 0xBE #define PRIVATE_STREAM2 0xBF #define AUDIO_STREAM_S 0xC0 /* 1100 0000 */ #define AUDIO_STREAM_E 0xDF /* 1101 1111 */ #define VIDEO_STREAM_S 0xE0 /* 1110 0000 */ #define VIDEO_STREAM_E 0xEF /* 1110 1111 */ #define AUDIO_STREAM_MASK 0x1F /* 0001 1111 */ #define VIDEO_STREAM_MASK 0x0F /* 0000 1111 */ #define AUDIO_STREAM 0xC0 /* 1100 0000 */ #define VIDEO_STREAM 0xE0 /* 1110 0000 */ #define ECM_STREAM 0xF0 #define EMM_STREAM 0xF1 #define DSM_CC_STREAM 0xF2 #define ISO13522_STREAM 0xF3 #define PROG_STREAM_DIR 0xFF #define IS_VIDEO_PACKET(data) (VIDEO_STREAM == ((data)[3] & ~VIDEO_STREAM_MASK)) #define IS_MPEG_AUDIO_PACKET(data) (AUDIO_STREAM == ((data)[3] & ~AUDIO_STREAM_MASK)) #define IS_PS1_PACKET(data) (PRIVATE_STREAM1 == (data)[3]) #define IS_PADDING_PACKET(data) (PADDING_STREAM == (data)[3]) #define IS_AUDIO_PACKET(data) (IS_MPEG_AUDIO_PACKET(data) || IS_PS1_PACKET(data)) #define PES_HAS_PTS(data) ((data)[7] & 0x80) #define PES_HAS_DTS(data) ((data)[7] & 0x40) #define DATA_IS_PES(data) (!(data)[0] && !(data)[1] && (data)[2] == 1) #define PES_HEADER_LEN(data) (8 + (data)[8] + 1) /* * timestamps */ static inline int pts_to_ms(int64_t pts) { return (int)(pts/INT64_C(90)); } static inline int64_t ms_to_pts(int ms) { return ((int64_t)(ms)) * INT64_C(90); } int64_t pes_get_pts(const uint8_t *buf, int len); int64_t pes_get_dts(const uint8_t *buf, int len); void pes_change_pts(uint8_t *buf, int len, int64_t new_pts); int pes_strip_pts_dts(uint8_t *buf, int len); /* * payload */ struct video_size_s; int pes_is_frame_h264(const uint8_t *buf, int len); uint8_t pes_get_picture_type(const uint8_t *buf, int len); int pes_get_video_size(const uint8_t *buf, int len, struct video_size_s *size, int h264); static inline int pes_is_mpeg1(const uint8_t *header) { if (IS_VIDEO_PACKET(header) || IS_AUDIO_PACKET(header)) return ((header[6] & 0xC0) != 0x80); if (header[3] == 0xBA) return ((header[4] & 0x40) == 0); /* mpeg1 */ return 0; } /* * Extract PES packet length */ static inline int pes_packet_len(const uint8_t *data, const int len) { if (IS_VIDEO_PACKET(data) || IS_AUDIO_PACKET(data)) { return 6 + (data[4] << 8 | data[5]); } else if (data[3] == PADDING_STREAM) { return 6 + (data[4] << 8 | data[5]); } else if (data[3] == 0xBA) { if ((data[4] & 0x40) == 0) /* mpeg1 */ return 12; else /* mpeg 2 */ return 14 + (data[0xD] & 0x07); } else if (data[3] <= 0xB9) { return -3; } return -(6 + (data[4] << 8 | data[5])); } #ifdef __cplusplus } /* extern "C" { */ #endif #endif /* _XINELIBOUTPUT_PES_H_ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/pes.c���������������������������������������������������������������������0000644�0001750�0001750�00000006550�13061253352�014325� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * pes.h: PES header definitions * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include <inttypes.h> #include <string.h> #include "../logdefs.h" #include "mpeg.h" #include "h264.h" #include "pes.h" static int64_t parse_timestamp(const uint8_t *buf) { int64_t ts; ts = ((int64_t)( buf[0] & 0x0E)) << 29 ; ts |= (int64_t)( buf[1] << 22 ); ts |= (int64_t)((buf[2] & 0xFE) << 14 ); ts |= (int64_t)( buf[3] << 7 ); ts |= (int64_t)((buf[4] & 0xFE) >> 1 ); return ts; } int64_t pes_get_pts(const uint8_t *buf, int len) { /* assume mpeg2 pes header ... */ if (IS_VIDEO_PACKET(buf) || IS_AUDIO_PACKET(buf)) { if ((buf[6] & 0xC0) != 0x80) return NO_PTS; if ((buf[6] & 0x30) != 0) return NO_PTS; if ((len > 13) && (buf[7] & 0x80)) { /* pts avail */ return parse_timestamp(buf + 9); } } return NO_PTS; } int64_t pes_get_dts(const uint8_t *buf, int len) { if (IS_VIDEO_PACKET(buf) || IS_AUDIO_PACKET(buf)) { if ((buf[6] & 0xC0) != 0x80) return NO_PTS; if ((buf[6] & 0x30) != 0) return NO_PTS; if (len > 18 && (buf[7] & 0x40)) { /* dts avail */ return parse_timestamp(buf + 14); } } return NO_PTS; } void pes_change_pts(uint8_t *buf, int len, int64_t new_pts) { /* assume mpeg2 pes header ... Assume header already HAS pts */ if (IS_VIDEO_PACKET(buf) || IS_AUDIO_PACKET(buf)) { if ((buf[6] & 0xC0) != 0x80) return; if ((buf[6] & 0x30) != 0) return; if ((len > 13) && (buf[7] & 0x80)) { /* pts avail */ buf[ 9] = ((new_pts >> 29) & 0x0E) | (buf[ 9] & 0xf1); buf[10] = ((new_pts >> 22) & 0xFF); buf[11] = ((new_pts >> 14) & 0xFE) | (buf[11] & 0x01); buf[12] = ((new_pts >> 7 ) & 0xFF); buf[13] = ((new_pts << 1 ) & 0xFE) | (buf[13] & 0x01); } } } int pes_strip_pts_dts(uint8_t *buf, int size) { if(size > 13 && buf[7] & 0x80) { /* pts avail */ int n = 5; int pes_len = (buf[4] << 8) | buf[5]; if ((buf[6] & 0xC0) != 0x80) return size; if ((buf[6] & 0x30) != 0) /* scrambling control */ return size; /* dts too ? */ if(size > 18 && buf[7] & 0x40) n += 5; pes_len -= n; /* update packet len */ buf[4] = pes_len >> 8; /* packet len (hi) */ buf[5] = pes_len & 0xff; /* packet len (lo) */ buf[7] &= 0x3f; /* clear pts and dts flags */ buf[8] -= n; /* update header len */ memmove(buf+4+n, buf+9+n, size-9-n); return size - n; } return size; } int pes_is_frame_h264(const uint8_t *buf, int len) { if (len < 9 || len < 9 + buf[8]) return 0; if ( (buf[6] & 0xC0) != 0x80) /* MPEG 2 PES */ return 0; buf += 9 + buf[8]; if (IS_NAL_AUD(buf)) return 1; return 0; } uint8_t pes_get_picture_type(const uint8_t *buf, int len) { int i = PES_HEADER_LEN(buf); buf += i; len -= i; if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) { if (buf[3] == NAL_AUD) return h264_get_picture_type(buf, len); return mpeg2_get_picture_type(buf, len); } return NO_PICTURE; } int pes_get_video_size(const uint8_t *buf, int len, video_size_t *size, int h264) { int i = PES_HEADER_LEN(buf); buf += i; len -= i; if (h264 || IS_NAL_AUD(buf)) return h264_get_video_size(buf, len, size); return mpeg2_get_video_size(buf, len, size); } ��������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/osd_command.h�������������������������������������������������������������0000644�0001750�0001750�00000012100�13061253352�016012� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * xine_osd_command.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINE_OSD_COMMAND_H_ #define __XINE_OSD_COMMAND_H_ #ifndef PACKED # define PACKED __attribute__((packed)) #endif #ifndef ALIGNED # define ALIGNED __attribute__((aligned)) #endif #define MAX_OSD_OBJECT 50 #if defined __cplusplus extern "C" { #endif typedef enum { OSD_Nop = 0, /* Do nothing ; used to initialize delay_ms counter */ OSD_Size = 1, /* Set size of VDR OSD area (usually 720x576) */ OSD_Set_RLE = 2, /* Create/update OSD window. Data is rle-compressed. */ OSD_SetPalette = 3, /* Modify palette of already created OSD window */ OSD_Move = 4, /* Change x/y position of already created OSD window */ OSD_Close = 5, /* Close OSD window */ OSD_Set_YUV = 6, /* Create/update OSD window. Data is in YUV420 format. */ OSD_Commit = 7, /* All OSD areas have been updated, commit changes to display */ OSD_Flush = 8, /* Flush all pending OSD operations immediately */ OSD_VideoWindow = 9, /* Set video window inside OSD */ OSD_Set_HDMV = 10, /* Create/update OSD window. Data is RLE compressed. */ OSD_Set_LUT8 = 11, /* Create/update OSD window. Data is uncompressed. */ OSD_Set_ARGB = 12, /* Create/update OSD window. Data is uncompressed. */ } osd_command_id_t; #define OSDFLAG_YUV_CLUT 0x01 /* palette is in YUV format */ #define OSDFLAG_REFRESH 0x02 /* OSD data refresh for new config, clients, etc. - no changes in bitmap */ #define OSDFLAG_UNSCALED 0x04 /* xine-lib unscaled (hardware) blending */ #define OSDFLAG_UNSCALED_LOWRES 0x08 /* unscaled blending when video resolution < .95 * 720x576 */ #define OSDFLAG_TOP_LAYER 0x10 /* window is part of top layer OSD */ typedef struct osd_clut_s { union { uint8_t cb /*: 8*/; uint8_t g; }; union { uint8_t cr /*: 8*/; uint8_t b; }; union { uint8_t y /*: 8*/; uint8_t r; }; uint8_t alpha /*: 8*/; } PACKED osd_clut_t; /* from xine, alphablend.h */ typedef struct osd_rle_elem_s { uint16_t len; uint16_t color; } PACKED osd_rle_elem_t; /* from xine */ typedef struct osd_rect_s { uint16_t x1; uint16_t y1; uint16_t x2; uint16_t y2; } osd_rect_t; typedef struct osd_command_s { uint8_t size; /* size of osd_command_t struct */ uint8_t cmd; /* osd_command_id_t */ uint8_t wnd; /* OSD window handle */ uint8_t layer; /* OSD layer */ int64_t pts; /* execute at given pts */ uint32_t delay_ms; /* execute 'delay_ms' ms after previous command (for same window). */ uint16_t x; /* window position, x */ uint16_t y; /* window position, y */ uint16_t w; /* window width */ uint16_t h; /* window height */ uint32_t datalen; /* size of image data, in bytes */ uint32_t num_rle; union { osd_rle_elem_t *data; /* RLE compressed image */ uint8_t *raw_data; uint64_t dummy01; }; uint32_t colors; /* palette size */ union { osd_clut_t *palette; /* palette (YCrCb) */ uint64_t dummy02; }; osd_rect_t dirty_area; uint8_t flags; uint8_t scaling; } PACKED osd_command_t ALIGNED; #if __BYTE_ORDER == __LITTLE_ENDIAN # define hton_osdcmd(cmdP) \ do { \ cmdP.pts = htonll(cmdP.pts); \ cmdP.delay_ms = htonl (cmdP.delay_ms); \ cmdP.x = htons (cmdP.x); \ cmdP.y = htons (cmdP.y); \ cmdP.w = htons (cmdP.w); \ cmdP.h = htons (cmdP.h); \ cmdP.datalen = htonl (cmdP.datalen); \ cmdP.num_rle = htonl (cmdP.num_rle); \ cmdP.colors = htonl (cmdP.colors); \ cmdP.dirty_area.x1 = htons(cmdP.dirty_area.x1); \ cmdP.dirty_area.y1 = htons(cmdP.dirty_area.y1); \ cmdP.dirty_area.x2 = htons(cmdP.dirty_area.x2); \ cmdP.dirty_area.y2 = htons(cmdP.dirty_area.y2); \ } while(0) # define ntoh_osdcmd(cmdP) \ do { \ cmdP.pts = ntohll(cmdP.pts); \ cmdP.delay_ms = ntohl (cmdP.delay_ms); \ cmdP.x = ntohs (cmdP.x); \ cmdP.y = ntohs (cmdP.y); \ cmdP.w = ntohs (cmdP.w); \ cmdP.h = ntohs (cmdP.h); \ cmdP.datalen = ntohl (cmdP.datalen); \ cmdP.num_rle = ntohl (cmdP.num_rle); \ cmdP.colors = ntohl (cmdP.colors); \ cmdP.dirty_area.x1 = ntohs(cmdP.dirty_area.x1); \ cmdP.dirty_area.y1 = ntohs(cmdP.dirty_area.y1); \ cmdP.dirty_area.x2 = ntohs(cmdP.dirty_area.x2); \ cmdP.dirty_area.y2 = ntohs(cmdP.dirty_area.y2); \ } while(0) #elif __BYTE_ORDER == __BIG_ENDIAN # define hton_osdcmd(cmd) do {} while(0) # define ntoh_osdcmd(cmd) do {} while(0) #else # error __BYTE_ORDER undefined ! #endif #if defined __cplusplus } #endif #endif /*__XINE_OSD_COMMAND_H_*/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/mpeg.h��������������������������������������������������������������������0000644�0001750�0001750�00000003017�13061253352�014466� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * mpeg.h: MPEG definitions * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_MPEG_H_ #define XINELIBOUTPUT_MPEG_H_ #ifdef __cplusplus extern "C" { #endif #define SC_PICTURE 0x00 /* picture atart code */ #define SC_SEQUENCE 0xb3 /* sequence header */ #define SC_SEQUENCE_END 0xb7 #if defined(__i386__) || defined(__x86_64__) # define IS_SC_PICTURE(buf) (*(const uint32_t *)(buf) == 0x00010000U) # define IS_SC_SEQUENCE(buf) (*(const uint32_t *)(buf) == 0xb3010000U) #else # define IS_SC_PICTURE(buf) ((buf)[0] == 0 && (buf)[1] == 0 && (buf)[2] == 1 && (buf)[3] == SC_PICTURE) # define IS_SC_SEQUENCE(buf) ((buf)[0] == 0 && (buf)[1] == 0 && (buf)[2] == 1 && (buf)[3] == SC_SEQUENCE) #endif /* Picture types */ #define NO_PICTURE 0 #define I_FRAME 1 #define P_FRAME 2 #define B_FRAME 3 typedef struct mpeg_rational_s { int num; int den; } mpeg_rational_t; typedef struct video_size_s { uint16_t width; uint16_t height; mpeg_rational_t pixel_aspect; } video_size_t; extern const char * const picture_type_str[]; /* * input: start of MPEG video data (not PES) */ int mpeg2_get_picture_type(const uint8_t *buf, size_t len); /* * input: start of MPEG video data (not PES) */ int mpeg2_get_video_size(const uint8_t *buf, size_t len, video_size_t *size); /* * */ int mpeg2_is_sequence_header(const uint8_t *buf, size_t len); #ifdef __cplusplus } /* extern "C" { */ #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/mpeg.c��������������������������������������������������������������������0000644�0001750�0001750�00000002734�13061253352�014466� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * mpeg.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include <inttypes.h> #include <string.h> #include "mpeg.h" const char * const picture_type_str[] = { "(none)", "I-Frame", "B-Frame", "P-Frame" }; int mpeg2_get_picture_type(const uint8_t *buf, size_t len) { size_t i; if (len > 5) for (i = 0; i < len-5; i++) if (IS_SC_PICTURE(buf + i)) return (buf[i + 5] >> 3) & 0x07; return NO_PICTURE; } int mpeg2_is_sequence_header(const uint8_t *buf, size_t len) { size_t i; if (len > 6) for (i = 0; i < len-6; i++) { if (IS_SC_SEQUENCE(buf + i)) { return 1; } } return 0; } int mpeg2_get_video_size(const uint8_t *buf, size_t len, video_size_t *size) { size_t i; if (len > 6) for (i = 0; i < len-6; i++) { if (IS_SC_SEQUENCE(buf + i)) { static const mpeg_rational_t mpeg2_aspect[16] = { {0,1}, {1,1}, {4,3}, {16,9}, {221,100}, {0,1}, {0,1}, {0,1}, { 0,1}, { 0,1}, {0,1}, {0,1}, {0,1}, { 0,1}, { 0,1}, {0,1}, }; unsigned d = (buf[i+4] << 16) | (buf[i+5] << 8) | buf[i+6]; unsigned a = (unsigned)buf[i+7] >> 4; size->width = (d >> 12); size->height = (d & 0xfff); memcpy(&size->pixel_aspect, &mpeg2_aspect[a & 0xf], sizeof(mpeg_rational_t)); size->pixel_aspect.num *= size->height; size->pixel_aspect.den *= size->width; return 1; } } return 0; } ������������������������������������xineliboutput-2.0.0/tools/metainfo_menu.h�����������������������������������������������������������0000644�0001750�0001750�00000001074�13061253352�016365� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * metainfo_menu.h: Media file info menu * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_INFO_MENU_H_ #define __XINELIB_INFO_MENU_H_ // // cMetainfoMenu // #include <vdr/osdbase.h> class cMetainfoMenu : public cOsdMenu { protected: cString m_Filename; public: cMetainfoMenu(cString Filename); virtual ~cMetainfoMenu(); virtual void Display(void); // cOsdMenu virtual eOSState ProcessKey(eKeys Key); }; #endif // __XINELIB_INFO_MENU_H_ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/metainfo_menu.c�����������������������������������������������������������0000644�0001750�0001750�00000010724�13061253352�016362� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * metainfo_menu.c: Media file info menu * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "../features.h" #ifdef HAVE_LIBEXTRACTOR # include <extractor.h> #endif #include <vdr/status.h> #include <vdr/i18n.h> #include "../config.h" #include "metainfo_menu.h" // // cMetainfoMenu // struct CallbackData { uint8_t *seen_types; char *text; size_t text_len; size_t text_size; CallbackData(void) { #if defined(HAVE_LIBEXTRACTOR) && EXTRACTOR_VERSION >= 0x00060000 seen_types = (uint8_t*)calloc(1, EXTRACTOR_metatype_get_max()); #else seen_types = NULL; #endif text_size = 4096; text = (char*)malloc(text_size); text[0] = 0; text_len = 0; } ~CallbackData() { free(seen_types); free(text); } void Append(const char *key, const char *data) { if (text_len < text_size - 1) { cString s = cString::sprintf("%s: %s\n", key, data); strn0cpy(text + text_len, s, text_size - text_len); text_len = strlen(text); } } }; #if defined(HAVE_LIBEXTRACTOR) && EXTRACTOR_VERSION >= 0x00060000 static int extractor_callback_metainfo(void *priv, const char *plugin_name, enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format, const char *data_mime_type, const char *data, size_t data_len) { struct CallbackData *cd = (struct CallbackData *)priv; if (format == EXTRACTOR_METAFORMAT_UTF8 && type != EXTRACTOR_METATYPE_THUMBNAIL && cd && !cd->seen_types[type] && data && data[0]) { const char *key = EXTRACTOR_metatype_to_string(type); if (key) { cd->Append(key, data); cd->seen_types[type] = 1; } } return 0; } #endif // defined(HAVE_LIBEXTRACTOR) && EXTRACTOR_VERSION >= 0x00060000 cMetainfoMenu::cMetainfoMenu(cString Filename) : cOsdMenu(Filename), m_Filename(Filename) { const char *Title = strrchr(Filename, '/'); if(Title && *(Title+1)) SetTitle(Title+1); } cMetainfoMenu::~cMetainfoMenu() { } void cMetainfoMenu::Display(void) { cOsdMenu::Display(); CallbackData data; #ifdef HAVE_LIBEXTRACTOR # if EXTRACTOR_VERSION >= 0x00060000 EXTRACTOR_PluginList * plugins; plugins = EXTRACTOR_plugin_add_defaults(EXTRACTOR_OPTION_DEFAULT_POLICY); EXTRACTOR_extract(plugins, m_Filename, NULL, 0, (EXTRACTOR_MetaDataProcessor)&extractor_callback_metainfo, &data); EXTRACTOR_plugin_remove_all(plugins); /* unload plugins */ # else // EXTRACTOR_VERSION >= 0x00060000 EXTRACTOR_ExtractorList * plugins; EXTRACTOR_KeywordList * md_list; plugins = EXTRACTOR_loadDefaultLibraries(); md_list = EXTRACTOR_getKeywords(plugins, m_Filename); md_list = EXTRACTOR_removeEmptyKeywords (md_list); md_list = EXTRACTOR_removeDuplicateKeywords(md_list, 0); md_list = EXTRACTOR_removeKeywordsOfType(md_list, EXTRACTOR_THUMBNAILS); while(md_list) { const char *key = EXTRACTOR_getKeywordTypeAsString(md_list->keywordType); if (key) data.Append(key, md_list->keyword); md_list = md_list->next; } EXTRACTOR_freeKeywords(md_list); EXTRACTOR_removeAll(plugins); /* unload plugins */ # endif // EXTRACTOR_VERSION >= 0x00060000 #else // HAVE_LIBEXTRACTOR cString cmd; if(xc.IsPlaylistFile(m_Filename)) cmd = cString::sprintf("file -b '%s'; cat '%s'", *m_Filename, *m_Filename); else if(xc.IsAudioFile(m_Filename)) cmd = cString::sprintf("mp3info -x '%s' ; file -b '%s'", *m_Filename, *m_Filename); else if(xc.IsVideoFile(m_Filename)) cmd = cString::sprintf("file -b '%s'; midentify '%s'", *m_Filename, *m_Filename); else if(xc.IsImageFile(m_Filename)) cmd = cString::sprintf("file -b '%s'; identify '%s'", *m_Filename, *m_Filename); else cmd = cString::sprintf("file -b '%s'", *m_Filename); cPipe p; if(p.Open(*cmd, "r")) { data.text_len = fread(data.text, 1, data.text_size - 1, p); if (data.text_len > 0) { data.text[data.text_len] = 0; strreplace(data.text, ',', '\n'); } } #endif // HAVE_LIBEXTRACTOR DisplayMenu()->SetText(data.text, false); data.text = NULL; cStatus::MsgOsdTextItem(cString::sprintf("%s\n%s", tr("Metainfo"), *m_Filename)); } eOSState cMetainfoMenu::ProcessKey(eKeys Key) { eOSState state = cOsdMenu::ProcessKey(Key); return state; } ��������������������������������������������xineliboutput-2.0.0/tools/listiter.h����������������������������������������������������������������0000644�0001750�0001750�00000005624�13061253352�015403� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * listiter.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _LISTITER_H_ #define _LISTITER_H_ //------------------------------ list ---------------------------------------- template <class LIST,class ITEM, class RESULT> void ForEach(LIST& List, RESULT (ITEM::*f)()) { for(ITEM *it = List.First(); it; it = List.Next(it)) (*it.*f)(); } template <class LIST,class ITEM, class ARG1, class RESULT> void ForEach(LIST& List, RESULT (ITEM::*f)(ARG1), ARG1 arg1) { for(ITEM *it = List.First(); it; it = List.Next(it)) (*it.*f)(arg1); } template <class LIST,class ITEM, class ARG1, class ARG2> void ForEach(LIST& List, void (ITEM::*f)(ARG1,ARG2), ARG1 arg1, ARG2 arg2) { for(ITEM *it = List.First(); it; it = List.Next(it)) (*it.*f)(arg1,arg2); } template <class LIST,class ITEM, class ARG1, class ARG2, class ARG3, class ARG4, class ARG5> void ForEach(LIST& List, void (ITEM::*f)(ARG1,ARG2,ARG3,ARG4,ARG5), ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4, ARG5 arg5) { for(ITEM *it = List.First(); it; it = List.Next(it)) (*it.*f)(arg1,arg2,arg3,arg4,arg5); } template <class LIST,class ITEM, class ARG1, class RESULT> RESULT ForEach(LIST& List, RESULT (ITEM::*f)(ARG1), ARG1 arg1, RESULT (*combiner)(RESULT,RESULT), RESULT def) { RESULT result = def; for(ITEM *it = List.First(); it; it = List.Next(it)) result = (*combiner)((*it.*f)(arg1),result); return result; } template <class LIST,class ITEM, class ARG1, class ARG2, class RESULT> RESULT ForEach(LIST& List, RESULT (ITEM::*f)(ARG1,ARG2), ARG1 arg1, ARG2 arg2, RESULT (*combiner)(RESULT,RESULT), RESULT def) { RESULT result = def; for(ITEM *it = List.First(); it; it = List.Next(it)) result = (*combiner)((*it.*f)(arg1,arg2),result); return result; } template <class LIST,class ITEM, class ARG1, class ARG2, class ARG3, class RESULT> RESULT ForEach(LIST& List, RESULT (ITEM::*f)(ARG1,ARG2,ARG3), ARG1 arg1, ARG2 arg2, ARG3 arg3, RESULT (*combiner)(RESULT,RESULT), RESULT def) { RESULT result = def; for(ITEM *it = List.First(); it; it = List.Next(it)) result = (*combiner)((*it.*f)(arg1,arg2,arg3),result); return result; } template <class LIST, class ITEM, class ARG1, class ARG2, class ARG3, class ARG4, class RESULT> RESULT ForEach(LIST& List, RESULT (ITEM::*f)(ARG1,ARG2,ARG3,ARG4), ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4, RESULT (*combiner)(RESULT,RESULT), RESULT def) { RESULT result = def; for(ITEM *it = List.First(); it; it = List.Next(it)) result = (*combiner)((*it.*f)(arg1,arg2,arg3,arg4),result); return result; } template<class T> T mmin(T a, T b) {return a<b ? a : b;} template<class T> T mmax(T a, T b) {return a>b ? a : b;} template<class T> T mand(T a, T b) {return a&&b;} template<class T> T mor(T a, T b) {return a||b;} #endif ������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/iso639.h������������������������������������������������������������������0000644�0001750�0001750�00000006570�13061253352�014601� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * iso639.h: iso-639-1 <-> iso-639-2 language code translations * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __ISO_639_H #define __ISO_639_H static const struct { const char iso639_2[4]; const char iso639_1[4]; } ISO639_map[] = { {"???", "??"}, {"abk", "ab"}, {"aar", "aa"}, {"afr", "af"}, {"alb", "sq"}, {"amh", "am"}, {"ara", "ar"}, {"arm", "hy"}, {"ast", "as"}, {"aym", "ay"}, {"aze", "az"}, {"bak", "ba"}, {"baq", "eu"}, {"bel", "be"}, {"ben", "bn"}, {"bih", "bh"}, {"bis", "bi"}, {"bre", "br"}, {"bul", "bg"}, {"bur", "my"}, {"cat", "ca"}, {"chi", "zh"}, {"cos", "co"}, {"scr", "hr"}, {"cze", "cs"}, {"dan", "da"}, {"dut", "nl"}, {"dzo", "dz"}, {"eng", "en"}, {"epo", "eo"}, {"est", "et"}, {"fao", "fo"}, {"fij", "fj"}, {"fin", "fi"}, {"fre", "fr"}, {"fry", "fy"}, {"glg", "gl"}, {"geo", "ka"}, {"ger", "de"}, {"gre", "el"}, {"grn", "gn"}, {"guj", "gu"}, {"hau", "ha"}, {"heb", "he"}, {"heb", "iw"}, {"hin", "hi"}, {"hun", "hu"}, {"ice", "is"}, {"ind", "id"}, {"ina", "ia"}, {"iku", "iu"}, {"ipk", "ik"}, {"gle", "ga"}, {"ita", "it"}, {"jpn", "ja"}, {"jav", "jv"}, {"kal", "kl"}, {"kan", "kn"}, {"kas", "ks"}, {"kaz", "kk"}, {"khm", "km"}, {"kin", "rw"}, {"kir", "ky"}, {"kor", "ko"}, {"kur", "ku"}, {"lao", "lo"}, {"lat", "la"}, {"lav", "lv"}, {"lin", "ln"}, {"lit", "lt"}, {"mac", "mk"}, {"mlg", "mg"}, {"may", "ms"}, {"mlt", "ml"}, {"mao", "mi"}, {"mar", "mr"}, {"mol", "mo"}, {"mon", "mn"}, {"nau", "na"}, {"nep", "ne"}, {"nor", "no"}, {"oci", "oc"}, {"ori", "or"}, {"orm", "om"}, {"per", "fa"}, {"pol", "pl"}, {"por", "pt"}, {"pus", "ps"}, {"que", "qu"}, {"roh", "rm"}, {"rum", "ro"}, {"run", "rn"}, {"rus", "ru"}, {"smo", "sm"}, {"sag", "sg"}, {"san", "sa"}, {"srp", "sr"}, {"scr", "sh"}, {"sna", "sn"}, {"snd", "sd"}, {"sin", "si"}, {"slo", "sk"}, {"slv", "sl"}, {"som", "so"}, {"sot", "st"}, {"spa", "es"}, {"sun", "su"}, {"swa", "sw"}, {"ssw", "ss"}, {"swe", "sv"}, {"tgl", "tl"}, {"tgk", "tg"}, {"tam", "ta"}, {"tat", "tt"}, {"tel", "te"}, {"tha", "th"}, {"tib", "bo"}, {"tir", "ti"}, {"tog", "to"}, {"tso", "ts"}, {"tsn", "tn"}, {"tur", "tr"}, {"tuk", "tk"}, {"twi", "tw"}, {"uig", "ug"}, {"ukr", "uk"}, {"urd", "ur"}, {"uzb", "uz"}, {"vie", "vi"}, {"vol", "vo"}, {"wel", "cy"}, {"wol", "wo"}, {"xho", "xh"}, {"yid", "yi"}, {"yor", "yo"}, {"zha", "za"}, {"zul", "zu"}, }; static const char *iso639_1_to_iso639_2(const char *lang) { if (lang && lang[0]) { if (lang[1] && !lang[2]) { unsigned int i; for (i = 0 ; i < sizeof(ISO639_map) / sizeof(ISO639_map[0]); i++) if (!memcmp(ISO639_map[i].iso639_1, lang, 2)) return ISO639_map[i].iso639_2; LOGMSG("Unknown iso639-1 code: %s", lang); } return lang; } return NULL; } static const char *iso639_2_to_iso639_1(const char *lang) { if (lang && lang[0]) { if (lang[1] && lang[2] && !lang[3]) { unsigned int i; for (i = 0 ; i < sizeof(ISO639_map) / sizeof(ISO639_map[0]); i++) if (!memcmp(ISO639_map[i].iso639_2, lang, 3)) return ISO639_map[i].iso639_1; LOGMSG("Unknown iso639-2 code: %s", lang); } return lang; } return NULL; } #endif ����������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/http.h��������������������������������������������������������������������0000644�0001750�0001750�00000005415�13061253352�014521� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * http.h: HTTP (/RTSP) helper classes * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_HTTP_H_ #define XINELIBOUTPUT_HTTP_H_ #include <vdr/tools.h> #define HTTP_REPLY_401 \ "HTTP/1.1 401 Unauthorized\r\n" \ "Connection: Close\r\n" \ "\r\n" #define HTTP_REPLY_404 \ "HTTP/1.1 404 Not Found\r\n" \ "Connection: Close\r\n" \ "\r\n" #define HTTP_REPLY_416 \ "HTTP/1.1 416 Requested Range Not Satisfiable\r\n" \ "Connection: Close\r\n" \ "\r\n" #define HTTP_REPLY_200_PRIMARY \ "HTTP/1.1 200 OK\r\n" \ "Content-Type: video/mpeg\r\n" \ "Connection: Close\r\n" \ "\r\n" //"Content-Type: video/mp2p\r\n" // // cHeader // class cHeader : public cListObject { protected: cString m_Name; cString m_Value; private: cHeader(); public: cHeader(const char *Name, const char *Value) : m_Name(Name), m_Value(Value) {}; const cString& Name(void) { return m_Name; } const cString& Value(void) { return m_Value; } int IntValue(void) { return *m_Value?atoi(m_Value):-1; } void SetValue(const char *Value) { m_Value = Value; } }; // // cHttpReq // class cHttpReq { private: cString m_Name; cString m_Uri; cString m_Version; cList<cHeader> m_Headers; bool m_Valid; public: cHttpReq() : m_Valid(false) {} bool SetCommand(const char *Command); const cString& Name(void) { return m_Name; } const cString& Uri(void) { return m_Uri; } const cString& Version(void) { return m_Version; } bool Valid(void) { return m_Valid; } void AddHeader(const char *Header, bool Duplicate=false); void AddHeader(const char *Name, const char *Value, bool Duplicate=false); cHeader *Header(const char *Name); void Reset(void); }; // // cConnState // class cConnState : public cHttpReq { public: }; // // cHttpStreamer // #include <vdr/tools.h> #include <vdr/thread.h> #include "cxsocket.h" class cHttpStreamer : protected cListObject, cThread { public: cHttpStreamer(int fd_http, const char *filename, cConnState *Request); virtual ~cHttpStreamer(); static void CloseAll(bool OnlyFinished = false); private: static cList<cHttpStreamer> m_Streamers; cxSocket m_fds; int m_fdf; cString m_Filename; int64_t m_FileSize; int64_t m_Start; int64_t m_End; bool m_KeepOpen; cConnState *m_ConnState; bool m_Finished; virtual void Action(void); bool ParseRequest(void); void ParseRange(const char *Range); bool ReadPipelined(void); bool Seek(void); }; #endif // XINELIBOUTPUT_HTTP_H_ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/http.c��������������������������������������������������������������������0000644�0001750�0001750�00000022440�13061253352�014511� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * http.c: HTTP (/RTSP) helper classes * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_FORMAT_MACROS #include <inttypes.h> #include <string.h> #include <vdr/config.h> #include <vdr/tools.h> #include "../logdefs.h" #include "http.h" // // cHttpReq // bool cHttpReq::SetCommand(const char *Command) { char *tmp = strdup(Command); char *pt = strchr(tmp, ' '), *uri; m_Valid = false; if(pt) { *pt++ = 0; m_Name = tmp; while(*pt && *pt == ' ') pt++; uri = pt; pt = strrchr(uri, ' '); if(pt) { m_Version = pt+1; while(*pt && *pt == ' ') *pt-- = 0; m_Uri = uri; m_Valid = true; } } free(tmp); return m_Valid; } cHeader *cHttpReq::Header(const char *Name) { for(cHeader *i = m_Headers.First(); i; i = m_Headers.Next(i)) if(!strcmp(Name, i->Name())) return i; return NULL; } void cHttpReq::AddHeader(const char *Header, bool Duplicate) { if(strlen(Header) < 4096) { char *name = strdup(Header); char *val = strchr(name, ':'); if(val) { *val++ = 0; while(*val == ' ') val++; AddHeader(name, val, Duplicate); } free(name); } else { LOGMSG("cConnState::AddHeader: header length exceeds 4096 !"); } } void cHttpReq::AddHeader(const char *Name, const char *Value, bool Duplicate) { if(strlen(Name) > 64 || strlen(Value) > 4096) { LOGMSG("cConnState::AddHeader: header length exceeds limit !"); } else { cHeader *h = Header(Name); if(!Duplicate && h) h->SetValue(Value); else { if(m_Headers.Count() < 50) m_Headers.Add(new cHeader(Name, Value)); else LOGMSG("cConnState::AddHeader: header count exceeds 50 !"); } } } void cHttpReq::Reset(void) { m_Name = NULL; m_Uri = NULL; m_Version = NULL; m_Valid = false; m_Headers.Clear(); } // // Map file extensions to mime types // static const char *mimetype(const char *ext) { static const struct { const char *ext; const char *mime; } ext2mime[] = { {"avi", "video/avi"}, {"vob", "video/mpeg"}, {"mpg", "video/mpeg"}, {"mpeg", "video/mpeg"}, {"vdr", "video/mp2p"}, {"mp3", "audio/mp3"}, {"flac", "audio/flac"}, {"jpg", "image/jpeg"}, {"jpeg", "image/jpeg"}, {"gif", "image/gif"}, {NULL, NULL} }; int i = -1; while(ext2mime[++i].ext) if(!strcmp(ext, ext2mime[i].ext)) return ext2mime[i].mime; return NULL; } static char *unescape_uri(const char *uri) { char *d = strdup(uri), *s = d, *result = d; while(*s) { if(s[0] == '%' && s[1] && s[2]) { unsigned int c; if (sscanf(s+1, "%02x", &c) == 1) { *d++ = (char)c; s += 3; continue; } } *d++ = *s++; } *d = 0; return result; } // // cHttpStreamer // cList<cHttpStreamer> cHttpStreamer::m_Streamers; void cHttpStreamer::CloseAll(bool OnlyFinished) { if(!OnlyFinished) { while(m_Streamers.First()) m_Streamers.Del(m_Streamers.First()); } else { /* purge finished streamers from list */ cHttpStreamer *it = m_Streamers.First(); while(it) { if(it->Active()) { it = (cHttpStreamer*)it->Next(); } else { m_Streamers.Del(it); it = m_Streamers.First(); } } } } cHttpStreamer::cHttpStreamer(int fd_http, const char *filename, cConnState *Request) : m_Filename(unescape_uri(filename), true) { m_fds.set_handle(fd_http); m_fds.set_cork(true); m_fdf = -1; //m_Filename = filename; m_FileSize = -1; m_Start = 0; m_End = -1; m_KeepOpen = true; m_ConnState = Request; m_Finished = false; CloseAll(true); m_Streamers.Add(this); if(m_Streamers.Count() > 5) { LOGMSG("WARNING: There are %d running HTTP streamers !", m_Streamers.Count()); if(m_Streamers.Count() > 20) { errno = 0; LOGERR("ERROR: There are %d running HTTP streamers, cancelling first", m_Streamers.Count()); m_Streamers.Del(m_Streamers.First()); } } Start(); } cHttpStreamer::~cHttpStreamer() { Cancel(3); if(m_ConnState) delete m_ConnState; if(m_fdf >= 0) close(m_fdf); m_fdf = -1; } void cHttpStreamer::ParseRange(const char *Range) { m_Start = 0; m_End = -1; if(Range) { LOGDBG("cHttpStreamer: Request range is \'%s\'", Range); switch(sscanf(Range, "bytes=%" PRId64 "-%" PRId64, &m_Start, &m_End)) { case 2: LOGMSG(" Range: %s (%" PRId64 " - %" PRId64 ")", Range, m_Start, m_End); break; case 1: m_End = -1; LOGMSG(" Range start: %s (%" PRId64 " - )", Range, m_Start); break; default: case 0: m_Start = 0; m_End = -1; break; } } } bool cHttpStreamer::ParseRequest(void) { cHeader *h; if((h = m_ConnState->Header("Range")) != NULL) ParseRange(h->Value()); m_KeepOpen = false; if((h = m_ConnState->Header("Connection")) != NULL) { m_KeepOpen = !strcasecmp(h->Value(), "keep-alive"); if(m_KeepOpen) LOGDBG("cHttpStreamer: client wants to keep connection open"); } return true; } bool cHttpStreamer::Seek(void) { if(m_fdf < 0) { m_fdf = open(m_Filename, O_RDONLY); if(m_fdf < 0) { LOGERR("cHttpStreamer: error opening %s", *m_Filename); m_fds.write_cmd(HTTP_REPLY_401); // 401 Not Found return false; } m_FileSize = lseek(m_fdf, 0, SEEK_END); if(m_FileSize <= 0) { LOGERR("cHttpStreamer: error seeking %s to end", *m_Filename); m_fds.write_cmd(HTTP_REPLY_401); // 401 Not Found return false; } } if(m_Start >= m_FileSize) { LOGERR("cHttpStreamer: Requested range not available " "(%s:%" PRId64 "-%" PRId64 " ; len=%" PRIu64 ")", *m_Filename, m_Start, m_End, (uint64_t)m_FileSize); m_fds.write_cmd(HTTP_REPLY_416); // 416 Requested Range Not Satisfiable return false; } bool result; if (m_Start > 0) result = (lseek(m_fdf, (off_t)m_Start, SEEK_SET) == (off_t)m_Start); else result = (lseek(m_fdf, 0, SEEK_SET) == 0); if (!result) { LOGERR("cHttpStreamer: error seeking to %" PRId64, m_Start); m_fds.write_cmd(HTTP_REPLY_416); // 416 Requested Range Not Satisfiable return false; } if(m_Start > 0) { if(m_End >= m_FileSize || m_End < 0) m_End = m_FileSize-1; m_fds.write_cmd("HTTP/1.1 206 Partial Content\r\n"); m_fds.printf("Content-Range: bytes %" PRId64 "-%" PRId64 "/%" PRIu64 "\r\n", m_Start, m_End, (uint64_t)m_FileSize); } else { m_fds.write_cmd("HTTP/1.1 200 OK\r\n"); } /* content type */ const char *ext = strrchr(m_Filename, '.'); if(ext) { const char *mime = mimetype(ext+1); if(mime) m_fds.printf("Content-Type: %s\r\n", mime); } /* Content-Length */ if(m_FileSize >= 0) { int64_t len = m_FileSize; if(m_End >= 0) len = m_End + 1; if(m_Start >= 0) len -= m_Start; m_fds.printf("Content-Length: %" PRId64 "\r\n", len); } /* Connection and end of reply */ if(m_KeepOpen) m_fds.write_cmd("Connection: Keep-Alive\r\n" "\r\n"); else m_fds.write_cmd("Connection: Close\r\n" "\r\n"); return true; } bool cHttpStreamer::ReadPipelined(void) { char buf[2048]; int r; if(m_ConnState) delete m_ConnState; m_ConnState = new cConnState; do { r = m_fds.readline(buf, sizeof(buf), 1000); if(r < 0 || errno == EAGAIN || r >= (int)sizeof(buf)) { LOGMSG("cHttpStreamer: disconnected"); return false; } LOGMSG("cHttpStreamer: pipelined request: %s", buf); if(!*m_ConnState->Name()) { if(!m_ConnState->SetCommand(buf) || strcmp(m_ConnState->Name(), "GET") || strncmp(m_ConnState->Uri(), "/PLAYFILE", 9) || strncmp(m_ConnState->Version(), "HTTP/1.", 7)) { LOGMSG("Incorrect HTTP request: %s", buf); return false; } } else if(r > 0) m_ConnState->AddHeader(buf); } while(r>0); return true; } void cHttpStreamer::Action(void) { int n = 0; cxPoller p(m_fds); bool Disc = !(ParseRequest() && Seek()); //uint64_t pos = m_Start; off_t start = (off_t)m_Start; while(Running() && !Disc) { n = m_End>0 ? (m_End-start+1) : m_FileSize - start; if(n > 0) { errno = 0; pthread_testcancel(); n = m_fds.sendfile(m_fdf, &start, n); pthread_testcancel(); if(n <= 0) { if(errno == EAGAIN || errno == EINTR) { p.Poll(100); pthread_testcancel(); } else { LOGERR("cHttpStreamer: sendfile() failed"); Disc=true; } } else if(n == 0) { LOGMSG("cHttpStreamer: disconnected at %" PRId64, (int64_t)start); Disc = true; } continue; } LOGDBG("cHttpStreamer: Hit to EOF or end of requested range"); m_fds.flush_cork(); if(!m_KeepOpen) { LOGMSG("cHttpStreamer: disconnecting (request complete)"); Disc = true; continue; } // keep connection open for new range for max. 30 sec n = 30; do { pthread_testcancel(); //cxPoller p(m_fds); LOGDBG("cHttpStreamer: Request complete, waiting..."); if(p.Poll(1000)) { LOGDBG("cHttpStreamer: Reading pipelined request"); pthread_testcancel(); Disc = !(ReadPipelined() && ParseRequest() && Seek()); //pos = m_Start; } } while(--n && Running() && !Disc); if(n <= 0) { LOGMSG("cHttpStreamer: Disconnecting (timeout)"); Disc = true; } } close(m_fdf); m_fdf = -1; m_fds.close(); m_Finished = true; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/h265.h��������������������������������������������������������������������0000644�0001750�0001750�00000002061�13061253352�014220� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * h265.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_H265_H_ #define _XINELIBOUTPUT_H265_H_ #ifdef __cplusplus extern "C" { #endif #include "mpeg.h" #define H265_NAL_SPS 0x21 /* Sequence Parameter Set */ #define H265_NAL_AUD 0x23 /* Access Unit Delimiter */ #define H265_NAL_EOS_NUT 0x24 /* End of Sequence */ #define H265_NAL_EOB_NUT 0x25 /* End of bitstream */ #if defined(__i386__) || defined(__x86_64__) # define IS_H265_NAL_AUD(buf) (*(const uint32_t *)(buf) == 0x46010000U) #else # define IS_H265_NAL_AUD(buf) ((buf)[0] == 0 && (buf)[1] == 0 && (buf)[2] == 1 && (buf)[3] == (H265_NAL_AUD<<1)) #endif typedef struct { uint16_t width; uint16_t height; /* ... */ } h265_sps_data_t; /* * input: start of H.265 video data (not PES) */ int h265_get_video_size(const uint8_t *buf, size_t len, struct video_size_s *size); #ifdef __cplusplus } /* extern "C" { */ #endif #endif /* _XINELIBOUTPUT_H265_H_ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/h265.c��������������������������������������������������������������������0000644�0001750�0001750�00000006364�13061253352�014225� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * h265.c: H.265 bitstream decoding * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include <stdint.h> #include <string.h> #ifndef LOG_MODULENAME # define LOG_MODULENAME "[h265 ] " # define SysLogLevel iSysLogLevel # include "../logdefs.h" #endif #define NOCACHE 1 #include "bitstream.h" #include "h265.h" static int h265_parse_sps(const uint8_t *buf, size_t len, h265_sps_data_t *sps) { br_state br = BR_INIT(buf, len); unsigned i; /* sps_video_parameter_set_id = */ br_skip_bits(&br, 4); uint8_t sps_max_sub_layers_minus1 = br_get_bits(&br,3); br_skip_bits(&br, 1 + 8 + 32 + 4 + 43 + 1 + 8); uint8_t sub_layer_profile_present_flag[8]; uint8_t sub_layer_level_present_flag[8]; for (i = 0; i < sps_max_sub_layers_minus1; i++) { sub_layer_profile_present_flag[i] = br_get_bit(&br); sub_layer_level_present_flag[i] = br_get_bit(&br); } if (sps_max_sub_layers_minus1 > 0) { for (i = sps_max_sub_layers_minus1; i < 8; i++) { br_skip_bits(&br, 2); } } for (i = 0; i < sps_max_sub_layers_minus1; i++) { if (sub_layer_profile_present_flag[i]) { br_skip_bits(&br, 8 + 32 + 4 + 43 + 1); } if (sub_layer_level_present_flag[i]) { br_skip_bits(&br, 8); } } /* sps_seq_parameter_set_id = */br_skip_ue_golomb(&br); unsigned int chroma_format_idc = br_get_ue_golomb(&br); if (chroma_format_idc == 3) { /* separate_colour_plane_flag = */br_skip_bit(&br); } sps->width = br_get_ue_golomb(&br); sps->height = br_get_ue_golomb(&br); if (BR_EOF(&br)) { LOGMSG("h265_parse_sps: not enough data ?"); return 0; } return 1; } static int h265_nal_unescape(uint8_t *dst, const uint8_t *src, size_t len) { size_t s = 0, d = 0; while (s < len - 3) { if (!src[s] && !src[s+1] && src[s+2]) { /* hit 00 00 xx */ dst[d] = dst[d+1] = 0; s += 2; d += 2; if (src[s] == 3) { s++; /* 00 00 03 xx --> 00 00 xx */ /*LOGDBG("h265_nal_unescape: hit 00 00 03 %02x", src[s]);*/ if (s >= len) return d; } /* else if (src[s] == 0 || src[s] == 1) { LOGDBG("h265_nal_unescape: invalid NAL sequence 00 00 %02x %02x", src[s], src[s+1]); return -1; }*/ } else { dst[d++] = src[s++]; } } return d; } /* * input: start of H.265 video data (not PES) */ int h265_get_video_size(const uint8_t *buf, size_t len, struct video_size_s *size) { size_t i; /* scan video packet for sequence parameter set */ if (len > 5) for (i = 5; i < len-5; i++) { if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && (buf[i + 3] >> 1) == H265_NAL_SPS) { uint8_t nal_data[len]; int nal_len; LOGDBG("H.265: Found NAL SPS at offset %zu/%zu", i, len); if (0 < (nal_len = h265_nal_unescape(nal_data, buf+i+5, len-i-5))) { h265_sps_data_t sps = {0}; if (h265_parse_sps(nal_data, nal_len, &sps)) { size->width = sps.width; size->height = sps.height; /* XXX */ size->pixel_aspect.num = 1; size->pixel_aspect.den = 1; return 1; } LOGMSG("h265_get_video_size: not enough data ?"); } } } return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/h264.h��������������������������������������������������������������������0000644�0001750�0001750�00000003172�13061253352�014223� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * h264.h: H.264 bitstream decoding * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_H264_H_ #define _XINELIBOUTPUT_H264_H_ #ifdef __cplusplus extern "C" { #endif #include "mpeg.h" #define NAL_SPS 0x07 /* Sequence Parameter Set */ #define NAL_AUD 0x09 /* Access Unit Delimiter */ #define NAL_END_SEQ 0x0a /* End of Sequence */ #if defined(__i386__) || defined(__x86_64__) # define IS_NAL_SPS(buf) (*(const uint32_t *)(buf) == 0x07010000U) # define IS_NAL_AUD(buf) (*(const uint32_t *)(buf) == 0x09010000U) # define IS_NAL_END_SEQ(buf) (*(const uint32_t *)(buf) == 0x0a010000U) #else # define IS_NAL_SPS(buf) ((buf)[0] == 0 && (buf)[1] == 0 && (buf)[2] == 1 && (buf)[3] == NAL_SPS) # define IS_NAL_AUD(buf) ((buf)[0] == 0 && (buf)[1] == 0 && (buf)[2] == 1 && (buf)[3] == NAL_AUD) # define IS_NAL_END_SEQ(buf) ((buf)[0] == 0 && (buf)[1] == 0 && (buf)[2] == 1 && (buf)[3] == NAL_END_SEQ) #endif typedef struct { uint16_t width; uint16_t height; mpeg_rational_t pixel_aspect; /* ... */ } h264_sps_data_t; struct video_size_s; /* * input: start of NAL SPS (without 00 00 01 07) */ int h264_parse_sps(const uint8_t *buf, size_t len, h264_sps_data_t *sps); /* * input: start of H.264 video data (not PES) */ int h264_get_picture_type(const uint8_t *buf, size_t len); /* * input: start of H.264 video data (not PES) */ int h264_get_video_size(const uint8_t *buf, size_t len, struct video_size_s *size); #ifdef __cplusplus } /* extern "C" { */ #endif #endif /* _XINELIBOUTPUT_H264_H_ */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/h264.c��������������������������������������������������������������������0000644�0001750�0001750�00000016135�13061253352�014221� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * h264.c: H.264 bitstream decoding * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include <stdint.h> #include <string.h> #ifndef LOG_MODULENAME # define LOG_MODULENAME "[h264 ] " # define SysLogLevel iSysLogLevel # include "../logdefs.h" #endif #define NOCACHE 1 #include "bitstream.h" #include "mpeg.h" #include "h264.h" int h264_parse_sps(const uint8_t *buf, size_t len, h264_sps_data_t *sps) { br_state br = BR_INIT(buf, len); int profile_idc, pic_order_cnt_type; int frame_mbs_only; int i, j; profile_idc = br_get_u8(&br); LOGDBG("H.264 SPS: profile_idc %d", profile_idc); /* constraint_set0_flag = br_get_bit(br); */ /* constraint_set1_flag = br_get_bit(br); */ /* constraint_set2_flag = br_get_bit(br); */ /* constraint_set3_flag = br_get_bit(br); */ /* reserved = br_get_bits(br,4); */ /* level_idc = br_get_u8(br); */ br_skip_bits(&br, 16); br_skip_ue_golomb(&br); /* seq_parameter_set_id */ if (profile_idc >= 100) { if (br_get_ue_golomb(&br) == 3) /* chroma_format_idc */ br_skip_bit(&br); /* residual_colour_transform_flag */ br_skip_ue_golomb(&br); /* bit_depth_luma - 8 */ br_skip_ue_golomb(&br); /* bit_depth_chroma - 8 */ br_skip_bit(&br); /* transform_bypass */ if (br_get_bit(&br)) /* seq_scaling_matrix_present */ for (i = 0; i < 8; i++) if (br_get_bit(&br)) { /* seq_scaling_list_present */ int last = 8, next = 8, size = (i<6) ? 16 : 64; for (j = 0; j < size; j++) { if (next) next = (last + br_get_se_golomb(&br)) & 0xff; last = next ?: last; } } } br_skip_ue_golomb(&br); /* log2_max_frame_num - 4 */ pic_order_cnt_type = br_get_ue_golomb(&br); if (pic_order_cnt_type == 0) br_skip_ue_golomb(&br); /* log2_max_poc_lsb - 4 */ else if (pic_order_cnt_type == 1) { br_skip_bit(&br); /* delta_pic_order_always_zero */ br_skip_se_golomb(&br); /* offset_for_non_ref_pic */ br_skip_se_golomb(&br); /* offset_for_top_to_bottom_field */ j = br_get_ue_golomb(&br); /* num_ref_frames_in_pic_order_cnt_cycle */ for (i = 0; i < j; i++) br_skip_se_golomb(&br); /* offset_for_ref_frame[i] */ } br_skip_ue_golomb(&br); /* ref_frames */ br_skip_bit(&br); /* gaps_in_frame_num_allowed */ sps->width /* mbs */ = br_get_ue_golomb(&br) + 1; sps->height /* mbs */ = br_get_ue_golomb(&br) + 1; frame_mbs_only = br_get_bit(&br); LOGDBG("H.264 SPS: pic_width: %u mbs", (unsigned) sps->width); LOGDBG("H.264 SPS: pic_height: %u mbs", (unsigned) sps->height); LOGDBG("H.264 SPS: frame only flag: %d", frame_mbs_only); sps->width *= 16; sps->height *= 16 * (2-frame_mbs_only); if (!frame_mbs_only) if (br_get_bit(&br)) /* mb_adaptive_frame_field_flag */ LOGDBG("H.264 SPS: MBAFF"); br_skip_bit(&br); /* direct_8x8_inference_flag */ if (br_get_bit(&br)) { /* frame_cropping_flag */ uint32_t crop_left = br_get_ue_golomb(&br); uint32_t crop_right = br_get_ue_golomb(&br); uint32_t crop_top = br_get_ue_golomb(&br); uint32_t crop_bottom = br_get_ue_golomb(&br); LOGDBG("H.264 SPS: cropping %d %d %d %d", crop_left, crop_top, crop_right, crop_bottom); sps->width -= 2*(crop_left + crop_right); if (frame_mbs_only) sps->height -= 2*(crop_top + crop_bottom); else sps->height -= 4*(crop_top + crop_bottom); } /* VUI parameters */ sps->pixel_aspect.num = 0; if (br_get_bit(&br)) { /* vui_parameters_present flag */ if (br_get_bit(&br)) { /* aspect_ratio_info_present */ uint32_t aspect_ratio_idc = br_get_u8(&br); LOGDBG("H.264 SPS: aspect_ratio_idc %d", aspect_ratio_idc); if (aspect_ratio_idc == 255 /* Extended_SAR */) { sps->pixel_aspect.num = br_get_u16(&br); /* sar_width */ sps->pixel_aspect.den = br_get_u16(&br); /* sar_height */ LOGDBG("H.264 SPS: -> sar %dx%d", sps->pixel_aspect.num, sps->pixel_aspect.den); } else { static const mpeg_rational_t aspect_ratios[] = { /* page 213: */ /* 0: unknown */ {0, 1}, /* 1...16: */ { 1, 1}, {12, 11}, {10, 11}, {16, 11}, { 40, 33}, {24, 11}, {20, 11}, {32, 11}, {80, 33}, {18, 11}, {15, 11}, {64, 33}, {160, 99}, { 4, 3}, { 3, 2}, { 2, 1} }; if (aspect_ratio_idc < sizeof(aspect_ratios)/sizeof(aspect_ratios[0])) { memcpy(&sps->pixel_aspect, &aspect_ratios[aspect_ratio_idc], sizeof(mpeg_rational_t)); LOGDBG("H.264 SPS: -> aspect ratio %d / %d", sps->pixel_aspect.num, sps->pixel_aspect.den); } else { LOGMSG("H.264 SPS: aspect_ratio_idc out of range !"); } } } } LOGDBG("H.264 SPS: -> video size %dx%d, aspect %d:%d", sps->width, sps->height, sps->pixel_aspect.num, sps->pixel_aspect.den); if(BR_EOF(&br)) { LOGDBG("H.264 SPS: not enough data ?"); return 0; } return 1; } static int h264_nal_unescape(uint8_t *dst, const uint8_t *src, size_t len) { size_t s = 0, d = 0; while (s < len) { if (!src[s] && !src[s+1]) { /* hit 00 00 xx */ dst[d] = dst[d+1] = 0; s += 2; d += 2; if (src[s] == 3) { s++; /* 00 00 03 xx --> 00 00 xx */ /*LOGDBG("h264_nal_unescape: hit 00 00 03 %02x", src[s]);*/ if (s >= len) return d; } /* else if (src[s] == 0 || src[s] == 1) { LOGDBG("h264_nal_unescape: invalid NAL sequence 00 00 %02x %02x", src[s], src[s+1]); return -1; }*/ } dst[d++] = src[s++]; } return d; } int h264_get_picture_type(const uint8_t *buf, size_t len) { size_t i; if (len > 5) for (i = 0; i < len-5; i++) { if (IS_NAL_AUD(buf + i)) { uint8_t type = (buf[i + 4] >> 5); switch (type) { case 0: case 3: case 5: return I_FRAME; case 1: case 4: case 6: return P_FRAME; case 2: case 7: return B_FRAME; default:; } } } return NO_PICTURE; } int h264_get_video_size(const uint8_t *buf, size_t len, video_size_t *size) { size_t i; /* if I-frame, search for NAL SPS */ if (h264_get_picture_type(buf, len) != I_FRAME) return 0; /* scan video packet for sequence parameter set */ if (len > 4) for (i = 5; i < len-4; i++) if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && (buf[i + 3] & 0x1f) == NAL_SPS) { uint8_t nal_data[len]; int nal_len; LOGDBG("H.264: Found NAL SPS at offset %zd/%zd", i, len); if (0 < (nal_len = h264_nal_unescape(nal_data, buf+i+4, len-i-4))) { h264_sps_data_t sps = {0}; if (h264_parse_sps(nal_data, nal_len, &sps)) { size->width = sps.width; size->height = sps.height; memcpy(&size->pixel_aspect, &sps.pixel_aspect, sizeof(mpeg_rational_t)); return 1; } LOGMSG("h264_get_video_size: not enough data ?"); } } return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/gnome_screensaver.h�������������������������������������������������������0000644�0001750�0001750�00000000222�13061253352�017236� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef _GNOME_SCREENSAVER_H #define _GNOME_SCREENSAVER_H extern void gnome_screensaver_control(int enable); #endif /* !_GNOME_SCREENSAVER_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/gnome_screensaver.c�������������������������������������������������������0000644�0001750�0001750�00000016764�13061253352�017253� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * gnome_screensaver.c v0.0.7 * * Enable/Disable the GNOME screensaver * Supports GNOME screensaver API 2.14 and 2.15 * Supports GNOME SessionManager API * * Call gnome_screensaver_control(1) to enable and * gnome_screensaver_control(0) to disable * */ /* * Orginally written for mplayer by Piotr Kaczuba * (http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2006-April/042661.html) * * Modified for xineliboutput by Alex Stansfield * (http://www.linuxtv.org/pipermail/vdr/2007-July/013458.html) * * GNOME SessionManager support by Timo Eskola. */ #include <stdlib.h> #include <unistd.h> #include <dbus/dbus-glib.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include "../features.h" #ifdef HAVE_MCE_DBUS_NAMES # include <mce/dbus-names.h> #endif #define LOG_MODULENAME "[scrnsaver] " #include "../logdefs.h" #include "gnome_screensaver.h" #define GS_SERVICE "org.gnome.ScreenSaver" #define GS_PATH "/org/gnome/ScreenSaver" #define GS_INTERFACE "org.gnome.ScreenSaver" #define SM_SERVICE "org.gnome.SessionManager" #define SM_PATH "/org/gnome/SessionManager" #define SM_INTERFACE "org.gnome.SessionManager" #define GS_APPLICATION_NAME "vdr-sxfe" #define GS_REASON_FOR_INHIBIT "Watching TV" /* Log Messages */ #define MSG_OpenBusConnectionError "Failed to open connection to bus: %s" #define MSG_RemoteMethodException "Caught remote method exception %s: %s" #define MSG_GnomeAPI215Failed "GNOME screensaver 2.15 API failed, trying 2.14 API" #define MSG_GError "Error: %s" #define MSG_GNOMEScreensaverEnabled "GNOME screensaver enabled" #define MSG_GNOMEScreensaverDisabled "GNOME screensaver disabled" #define MSG_MCEScreensaverEnabled "MCE screensaver enabled" #define MSG_MCEScreensaverDisabled "MCE screensaver disabled" static guint32 cookie; static int gnome_sessionmanager_control(DBusGConnection *connection, int enable) { GError *error; DBusGProxy *proxy; gboolean ret; /* Create a proxy object */ proxy = dbus_g_proxy_new_for_name(connection, SM_SERVICE, SM_PATH, SM_INTERFACE); if (!proxy) { LOGDBG("Failed to get a proxy for " SM_SERVICE); return 0; } error = NULL; if (enable) { ret = dbus_g_proxy_call(proxy, "Uninhibit", &error, G_TYPE_UINT, cookie, G_TYPE_INVALID, G_TYPE_INVALID); } else { ret = dbus_g_proxy_call(proxy, "Inhibit", &error, G_TYPE_STRING, GS_APPLICATION_NAME, G_TYPE_UINT, 0, G_TYPE_STRING, GS_REASON_FOR_INHIBIT, G_TYPE_UINT, 12, G_TYPE_INVALID, G_TYPE_UINT, &cookie, G_TYPE_INVALID); } g_object_unref(proxy); if (!ret) { /* Check if it's a remote exception or a regular GError */ if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) { LOGMSG(MSG_RemoteMethodException, dbus_g_error_get_name(error), error->message); } else { LOGMSG(MSG_GError, error->message); } g_error_free(error); return 0; } LOGMSG(enable ? MSG_GNOMEScreensaverEnabled : MSG_GNOMEScreensaverDisabled); return 1; } #ifdef HAVE_MCE_DBUS_NAMES static int mce_control(DBusGConnection *connection, int enable) { GError *error; DBusGProxy *proxy; gboolean ret = 1; /* Create a proxy object */ proxy = dbus_g_proxy_new_for_name(connection, MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF); if (!proxy) { LOGDBG("Failed to get a proxy for " SM_SERVICE); return 0; } error = NULL; if (enable) { ret = dbus_g_proxy_call(proxy, MCE_CANCEL_PREVENT_BLANK_REQ, &error, G_TYPE_INVALID, G_TYPE_INVALID); } else { ret = dbus_g_proxy_call(proxy, MCE_PREVENT_BLANK_REQ, &error, G_TYPE_INVALID, G_TYPE_INVALID); } g_object_unref(proxy); if (!ret) { /* Check if it's a remote exception or a regular GError */ if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) { LOGMSG(MSG_RemoteMethodException, dbus_g_error_get_name(error), error->message); } else { LOGMSG(MSG_GError, error->message); } g_error_free(error); return 0; } LOGMSG(enable ? MSG_MCEScreensaverEnabled : MSG_MCEScreensaverDisabled); return 1; } #endif void gnome_screensaver_control(int enable) { DBusGConnection *connection; GError *error; DBusGProxy *proxy; gboolean ret; #ifdef GLIB_CHECK_VERSION #if !GLIB_CHECK_VERSION(2,35,0) g_type_init(); #endif #endif #ifdef HAVE_MCE_DBUS_NAMES error = NULL; connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); if (!connection) { LOGMSG(MSG_OpenBusConnectionError, error ? error->message : "<null>"); g_error_free(error); } else if (mce_control(connection, enable)) { return; } #endif /* Get a connection to the session bus */ error = NULL; connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (!connection) { LOGMSG(MSG_OpenBusConnectionError, error ? error->message : "<null>"); g_error_free(error); return; } /* try session manager interface first */ if (gnome_sessionmanager_control(connection, enable)) return; /* Create a proxy object */ proxy = dbus_g_proxy_new_for_name(connection, GS_SERVICE, GS_PATH, GS_INTERFACE); if (!proxy) { LOGDBG("Failed to get a proxy for " GS_SERVICE); return; } error = NULL; /* Enable the screensaver */ if (enable) { /* First call the GNOME screensaver 2.15 API method */ ret = dbus_g_proxy_call(proxy, "UnInhibit", &error, G_TYPE_UINT, cookie, G_TYPE_INVALID, G_TYPE_INVALID); /* If this fails, try the GNOME screensaver 2.14 API */ if (!ret && error->domain == DBUS_GERROR && error->code == DBUS_GERROR_UNKNOWN_METHOD) { LOGMSG(MSG_GnomeAPI215Failed); g_error_free(error); error = NULL; ret = dbus_g_proxy_call(proxy, "AllowActivation", &error, G_TYPE_INVALID, G_TYPE_INVALID); } } /* Disable the screensaver */ else { /* First call the GNOME screensaver 2.15 API method */ ret = dbus_g_proxy_call(proxy, "Inhibit", &error, G_TYPE_STRING, GS_APPLICATION_NAME, G_TYPE_STRING, GS_REASON_FOR_INHIBIT, G_TYPE_INVALID, G_TYPE_UINT, &cookie, G_TYPE_INVALID); /* If this fails, try the GNOME screensaver 2.14 API */ if (!ret && error->domain == DBUS_GERROR && error->code == DBUS_GERROR_UNKNOWN_METHOD) { LOGMSG(MSG_GnomeAPI215Failed); g_error_free(error); error = NULL; ret = dbus_g_proxy_call(proxy, "InhibitActivation", &error, G_TYPE_STRING, GS_REASON_FOR_INHIBIT, G_TYPE_INVALID, G_TYPE_INVALID); } } if (!ret) { /* Check if it's a remote exception or a regular GError */ if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) { LOGMSG(MSG_RemoteMethodException, dbus_g_error_get_name(error), error->message); } else { LOGMSG(MSG_GError, error->message); } g_error_free(error); } else { LOGMSG(enable ? MSG_GNOMEScreensaverEnabled : MSG_GNOMEScreensaverDisabled); } g_object_unref(proxy); } ������������xineliboutput-2.0.0/tools/general_remote.h����������������������������������������������������������0000644�0001750�0001750�00000001046�13061253352�016526� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * general_remote.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __GENERAL_REMOTE_H #define __GENERAL_REMOTE_H //----------------------------- cGeneralRemote -------------------------------- #include <vdr/remote.h> class cGeneralRemote : public cRemote { public: cGeneralRemote(const char *Name) : cRemote(Name) {}; bool Put(const char *Code, bool Repeat=false, bool Release=false) { return cRemote::Put(Code, Repeat, Release); }; }; #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/future.h������������������������������������������������������������������0000644�0001750�0001750�00000002407�13061253352�015052� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * future.h: A variable that gets its value in future. * Used to convert asynchronous IPCs to synchronous. * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __FUTURE_H #define __FUTURE_H #include <vdr/thread.h> template <class T> class cFuture { private: cMutex mutex; cCondVar cond; bool m_Ready; T m_Value; public: cFuture() { m_Ready = false; } void Reset(void) { cMutexLock l(&mutex); m_Ready = false; } // // Producer interface // void Set(T& Value) { cMutexLock l(&mutex); m_Value = Value; m_Ready = true; cond.Broadcast(); } // // Consumer interface // bool Wait(int Timeout = -1) { cMutexLock l(&mutex); if(Timeout==0 || m_Ready) return m_Ready; if(Timeout >= 0) return cond.TimedWait(mutex, Timeout) && m_Ready; while(!m_Ready) cond.Wait(mutex); return m_Ready; } bool IsReady(void) { cMutexLock l(&mutex); return m_Ready; } T Value(void) { cMutexLock l(&mutex); while(!m_Ready) cond.Wait(mutex); return m_Value; } }; #endif // __FUTURE_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/functorimpl.h�������������������������������������������������������������0000644�0001750�0001750�00000005674�13061253352�016113� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * functorimpl.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_FUNCTORIMPL_H #ifndef __XINELIB_FUNCTOR_H # error functorimpl.h should not be included, use functor.h instead #endif #if 1 /* gcc 3.3.x (?) does not accept class TRESULT=void */ template <class TCLASS> class cFunctor0 : public cFunctor { public: protected: typedef void (TCLASS::*TFUNC)(void); cFunctor0(TCLASS *obj, TFUNC f) : m_obj(obj), m_f(f) {} virtual ~cFunctor0() {}; virtual void Execute(void) { (*m_obj.*m_f)(); } private: TCLASS *m_obj; TFUNC m_f; friend cFunctor *CreateFunctor<TCLASS>(TCLASS*,TFUNC); }; template <class TCLASS, class TARG1> class cFunctor1 : public cFunctor { public: protected: typedef void (TCLASS::*TFUNC)(TARG1); cFunctor1(TCLASS *obj, TFUNC f, TARG1 arg1) : m_obj(obj), m_f(f), m_arg1(arg1) {} virtual ~cFunctor1() {}; virtual void Execute(void) { (*m_obj.*m_f)(m_arg1); } private: TCLASS *m_obj; TFUNC m_f; TARG1 m_arg1; friend cFunctor *CreateFunctor<TCLASS,TARG1>(TCLASS*,TFUNC,TARG1); }; #endif template <class TCLASS, class TRESULT> class cFunctorR0 : public cFunctor { public: protected: typedef TRESULT (TCLASS::*TFUNC)(void); cFunctorR0(TCLASS *obj, TFUNC f) : m_obj(obj), m_f(f) {} virtual ~cFunctorR0() {}; virtual void Execute(void) { // TODO: use future to pass back value (void) (*m_obj.*m_f)(); } private: TCLASS *m_obj; TFUNC m_f; friend cFunctor *CreateFunctor<TCLASS,TRESULT>(TCLASS*,TFUNC); }; template <class TCLASS, class TRESULT, class TARG1> class cFunctorR1 : public cFunctor { public: protected: typedef TRESULT (TCLASS::*TFUNC)(TARG1); cFunctorR1(TCLASS *obj, TFUNC f, TARG1 arg1) : m_obj(obj), m_f(f), m_arg1(arg1) {} virtual ~cFunctorR1() {}; virtual void Execute(void) { // TODO: use future to pass back value (void) (*m_obj.*m_f)(m_arg1); } private: TCLASS *m_obj; TFUNC m_f; TARG1 m_arg1; friend cFunctor *CreateFunctor<TCLASS,TRESULT>(TCLASS*,TFUNC,TARG1); }; #if 1 /* gcc 3.3.x (?) does not accept class TRESULT=void */ template<class TCLASS> cFunctor *CreateFunctor(TCLASS *c, void (TCLASS::*fp)(void)) { return new cFunctor0<TCLASS>(c, fp); } template<class TCLASS, class TARG1> cFunctor *CreateFunctor(TCLASS *c, void (TCLASS::*fp)(TARG1), TARG1 arg1) { return new cFunctor1<TCLASS,TARG1>(c, fp, arg1); } #endif template<class TCLASS, class TRESULT> cFunctor *CreateFunctor(TCLASS *c, TRESULT (TCLASS::*fp)(void)) { return new cFunctorR0<TCLASS,TRESULT>(c, fp); } template<class TCLASS, class TRESULT, class TARG1> cFunctor *CreateFunctor(TCLASS *c, TRESULT (TCLASS::*fp)(TARG1), TARG1 arg1) { return new cFunctorR1<TCLASS,TRESULT,TARG1>(c, fp, arg1); } #endif ��������������������������������������������������������������������xineliboutput-2.0.0/tools/functor.h�����������������������������������������������������������������0000644�0001750�0001750�00000001644�13061253352�015222� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * functor.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_FUNCTOR_H #define __XINELIB_FUNCTOR_H #include <vdr/tools.h> class cFunctor : public cListObject { public: cFunctor() : cListObject() {} virtual ~cFunctor() {} virtual void Execute(void) = 0; }; #if 1 /* gcc 3.3.x (?) does not accept class TRESULT=void */ template<class TCLASS> cFunctor *CreateFunctor(TCLASS *c, void (TCLASS::*fp)(void)); template<class TCLASS, class TARG1> cFunctor *CreateFunctor(TCLASS *c, void (TCLASS::*fp)(TARG1), TARG1 arg1); #endif template<class TCLASS, class TRESULT> cFunctor *CreateFunctor(TCLASS *c, TRESULT (TCLASS::*fp)(void)); template<class TCLASS, class TRESULT, class TARG1> cFunctor *CreateFunctor(TCLASS *c, TRESULT (TCLASS::*fp)(TARG1), TARG1 arg1); #include "functorimpl.h" #endif ��������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/display_message.h���������������������������������������������������������0000644�0001750�0001750�00000002331�13061253352�016705� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * display_message.h: Display simple message * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __DISPLAY_MESSAGE_H #define __DISPLAY_MESSAGE_H #include <vdr/osdbase.h> #include <vdr/skins.h> class cDisplayMessage : public cOsdObject { cSkinDisplayMessage *displayMessage; char *Message; int Timer; int Timeout; public: cDisplayMessage(const char *message, int timeout = 3) { displayMessage = NULL; Message = strdup(message); Timer = 0; Timeout = timeout; } virtual ~cDisplayMessage() { delete displayMessage; free(Message); } void Update(const char *message) { Timer = 0; free(Message); Message = strdup(message); Show(); } virtual eOSState ProcessKey(eKeys Key) { if(Key == kNone && Timer++ > Timeout) return osEnd; if(Key != kNone) { // put back and close cRemote::Put(Key, true); return osEnd; } return osContinue; } virtual void Show(void) { if(!displayMessage) displayMessage = Skins.Current()->DisplayMessage(); displayMessage->SetMessage(mtInfo, Message); displayMessage->Flush(); } }; #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/debug_mutex.h�������������������������������������������������������������0000644�0001750�0001750�00000011440�13061253352�016045� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * debug_mutex.h: debugging wrappers for pthread_mutex_ functions * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef DEBUG_MUTEX_H #define DEBUG_MUTEX_H #ifndef _PTHREAD_H # error pthread.h must be included before debug_mutex.h #endif /* * Override pthread_mutex_ calls: * * Change type of each mutex to PTHREAD_MUTEX_ERRORCHECK_NP * * Store line number of last succeed pthread_mutex_lock call * for each initialized mutex * * Check every pthread_mutex_ call for errors and log all errors * * To help detecting deadlocks and minimize logging: * - Try locking first in pthread_mutex_lock * - If pthread_mutex_trylock fails, log a message and retry. * - When trylock failed, log another message when lock is acquired. * * * NOTE: debugging itself is not thread-safe and may indicate wrong line numbers ! * */ #define MAX_DBG_MUTEX 64 static struct { pthread_mutex_t *lock; int line; int tid; } dbgdata[MAX_DBG_MUTEX+1] = {{NULL,0}}; static void dbg_setdata(pthread_mutex_t *mutex, int line) { int i; for(i=0; i<MAX_DBG_MUTEX; i++) if(dbgdata[i].lock == mutex) { dbgdata[i].line = line; dbgdata[i].tid = syscall(__NR_gettid); return; } LOGMSG("********** dbg_setdata: new entry (0x%lx at %d)", (unsigned long int)mutex, line); for(i=0; i<MAX_DBG_MUTEX; i++) if(!dbgdata[i].lock) { dbgdata[i].lock = mutex; dbgdata[i].line = line; dbgdata[i].tid = syscall(__NR_gettid); return; } LOGMSG("********** dbg_setdata: table full !"); } static int dbg_getdata(pthread_mutex_t *mutex, int line) { int i; for(i=0; i<MAX_DBG_MUTEX; i++) if(dbgdata[i].lock == mutex) return dbgdata[i].line; LOGMSG("********** dbg_getdata: NO ENTRY ! (%d)", line); return -1; } static void dbg_deldata(pthread_mutex_t *mutex, int line) { int i; for(i=0; i<MAX_DBG_MUTEX; i++) if(dbgdata[i].lock == mutex) { dbgdata[i].lock = NULL; return; } LOGMSG("********** dbg_deldata: NO ENTRY ! (%d)", line); return; } static int dbg_init(pthread_mutex_t *mutex, pthread_mutexattr_t *pattr, int line) { int r; errno = 0; if(!pattr) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP); r = pthread_mutex_init(mutex, &attr); } else { LOGMSG("********** dbg_init: mutex attribute already given !"); r = pthread_mutex_init(mutex, pattr); } if(r) LOGERR("********** dbg_init: pthread_mutex_init FAILED at %d", line); dbg_setdata(mutex, line); return r; } static int dbg_free(pthread_mutex_t *mutex, int line) { int r; errno = 0; r = pthread_mutex_destroy(mutex); if(r) LOGERR("********** dbg_free: pthread_mutex_destroy FAILED at %d ; last lock at %d", line, dbg_getdata(mutex, line)); dbg_deldata(mutex, line); return r; } static int dbg_lock(pthread_mutex_t *mutex, int line) { int r; /*struct timespec abs_timeout;*/ /* try lock first to reduce logging */ errno = 0; r = pthread_mutex_trylock(mutex); if(!r) { dbg_setdata(mutex,line); return r; } /* try failed - we're going to wait, so log at wait start and end to detect deadlocks */ LOGERR("********** dbg_lock: pthread_mutex_trylock failed at %d (locked at %d)", line, dbg_getdata(mutex, line)); /* int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout); */ errno = 0; r = pthread_mutex_lock(mutex); if(r) LOGERR("********** dbg_lock: pthread_mutex_lock FAILED at %d", line); dbg_setdata(mutex, line); LOGMSG("********** dbg_lock: pthread_mutex_lock done at %d", line); return r; } static int dbg_trylock(pthread_mutex_t *mutex, int line) { int r; /*struct timespec abs_timeout;*/ /* try lock first to reduce logging */ errno = 0; r = pthread_mutex_trylock(mutex); if(!r) { dbg_setdata(mutex,line); return r; } LOGERR("********** dbg_trylock: pthread_mutex_trylock failed at %d (locked at %d)", line, dbg_getdata(mutex, line)); return r; } static int dbg_unlock(pthread_mutex_t *mutex, int line) { int r; errno = 0; r = pthread_mutex_unlock(mutex); if(r) LOGERR("********** dbg_unlock: pthread_mutex_unlock FAILED at %d (last locket at %d)", line, dbg_getdata(mutex, line)); //else // dbg_setdata(mutex, 0); return r; } /* override pthread_ functions with own ones */ #define pthread_mutex_init(l,a) dbg_init(l, a, __LINE__) #define pthread_mutex_lock(l) dbg_lock(l, __LINE__) #define pthread_mutex_trylock(l) dbg_trylock(l, __LINE__) #define pthread_mutex_unlock(l) dbg_unlock(l, __LINE__) #define pthread_mutex_destroy(l) dbg_free(l, __LINE__) #else # error debug_mutex.h included twice #endif /* DEBUG_MUTEX_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/cxsocket.h����������������������������������������������������������������0000644�0001750�0001750�00000013062�13061253352�015362� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * cxsocket.h: Socket wrapper classes * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __CXSOCKET_H #define __CXSOCKET_H #include <inttypes.h> #include <sys/types.h> #include <sys/socket.h> #ifdef __FreeBSD__ #include <netinet/in.h> #endif #define CLOSESOCKET(fd) do { if(fd>=0) { ::close(fd); fd=-1; } } while(0) class cxSocket { private: int m_fd; cxSocket(const cxSocket& s) ;//{ m_fd = s.m_fd>=0 ? dup(s.m_fd) : -1; } cxSocket &operator=(const cxSocket &S) ;// { close(); m_fd = S.m_fd >= 0 ? dup(S.m_fd) : -1; return *this; }; public: typedef enum { estSTREAM = SOCK_STREAM, estDGRAM = SOCK_DGRAM } eSockType; cxSocket() : m_fd(-1) {} cxSocket(eSockType type) : m_fd(::socket(PF_INET, (int)type, 0)) {} ~cxSocket() { CLOSESOCKET(m_fd); } //operator int () const { return Handle(); } //operator bool () const { return open(); } //bool operator==(const cxSocket &s) { return m_fd == s.m_fd; } int handle(bool take_ownership=false) { int r=m_fd; if(take_ownership) m_fd=-1; return r; } void set_handle(int h) { if(h != m_fd) {close(); m_fd = h;} } bool create(eSockType type) { close(); return (m_fd=::socket(PF_INET, (int)type, 0)) >= 0; } bool open(void) const { return m_fd>0; } void close(void) { CLOSESOCKET(m_fd); } ssize_t send(const void *buf, size_t size, int flags=0, const struct sockaddr *to = NULL, socklen_t tolen = 0); ssize_t recv(void *buf, size_t size, int flags = 0, struct sockaddr *from = NULL, socklen_t *fromlen = NULL); ssize_t sendfile(int fd_file, off_t *offset, size_t count); ssize_t read(void *buffer, size_t size, int timeout_ms = -1); ssize_t write(const void *buffer, size_t size, int timeout_ms = -1); ssize_t printf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); ssize_t write_str(const char *str, int timeout_ms=-1, int len=0) { return write(str, len ?: strlen(str), timeout_ms); } ssize_t write_cmd(const char *str, int len=0) { return write(str, len ?: strlen(str), 10); } /* readline return value: * <0 : failed * >=maxsize : buffer overflow * >=0 : if errno = EAGAIN -> line is not complete (there was timeout) * if errno = 0 -> succeed * (return value 0 indicates empty line "\r\n") */ ssize_t readline(char *buf, int bufsize, int timeout=0, int bufpos=0); bool set_buffers(int Tx, int Rx); bool set_multicast(int ttl); bool set_blocking(bool state); bool set_cork(bool state); bool flush_cork(void) { return set_nodelay(true); }; bool set_nodelay(bool state); ssize_t tx_buffer_size(void); ssize_t tx_buffer_free(void); int getsockname(struct sockaddr *name, socklen_t *namelen); int getpeername(struct sockaddr *name, socklen_t *namelen); bool connect(struct sockaddr *addr, socklen_t len); bool connect(const char *ip, int port); uint32_t get_local_address(char *ip_address); static char *ip2txt(uint32_t ip, unsigned int port, char *str); }; #include <vdr/tools.h> class cxPoller : public cPoller { public: cxPoller(cxSocket& Sock, bool Out=false) : cPoller(Sock.handle(), Out) {}; cxPoller(cxSocket* Socks, int count, bool Out=false) { for(int i=0; i<count; i++) Add(Socks[i].handle(), Out); } }; // // Set socket buffers // static inline void set_socket_buffers(int s, int txbuf, int rxbuf) { int max_buf = txbuf; /*while(max_buf) {*/ errno = 0; if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(int))) { LOGERR("setsockopt(SO_SNDBUF,%d) failed", max_buf); /*max_buf >>= 1;*/ } /*else {*/ int tmp = 0; int len = sizeof(int); errno = 0; if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, &tmp, (socklen_t*)&len)) { LOGERR("getsockopt(SO_SNDBUF,%d) failed", max_buf); /*break;*/ } else if(tmp != max_buf) { LOGDBG("setsockopt(SO_SNDBUF): got %d bytes", tmp); /*max_buf >>= 1;*/ /*continue;*/ } /*}*/ /*}*/ max_buf = rxbuf; if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &max_buf, sizeof(int)) < 0) { LOGERR("setsockopt(SO_RCVBUF,%d) failed", max_buf); } } // // Connect data socket to client (take address from fd_control) // static inline int sock_connect(int fd_control, int port, int type) { struct sockaddr_in sin; socklen_t len = sizeof(sin); int s, one = 1; if(getpeername(fd_control, (struct sockaddr *)&sin, &len)) { LOGERR("sock_connect: getpeername failed"); return -1; } uint32_t tmp = ntohl(sin.sin_addr.s_addr); LOGMSG("Client address: %d.%d.%d.%d", ((tmp>>24)&0xff), ((tmp>>16)&0xff), ((tmp>>8)&0xff), ((tmp)&0xff)); #if 0 if ((h = gethostbyname(tmp)) == NULL) { LOGDBG("sock_connect: unable to resolve host name", tmp); } #endif if ((s = socket(PF_INET, type, type==SOCK_DGRAM?IPPROTO_UDP:IPPROTO_TCP)) < 0) { LOGERR("sock_connect: failed to create socket"); return -1; } // Set socket buffers: large send buffer, small receive buffer set_socket_buffers(s, KILOBYTE(256), 2048); if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) LOGERR("sock_connect: setsockopt(SO_REUSEADDR) failed"); sin.sin_family = AF_INET; sin.sin_port = htons(port); if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 && errno != EINPROGRESS) { LOGERR("connect() failed"); CLOSESOCKET(s); } if (fcntl (s, F_SETFL, fcntl (s, F_GETFL) | O_NONBLOCK) == -1) { LOGERR("can't put socket in non-blocking mode"); CLOSESOCKET(s); return -1; } return s; } #endif // __CXSOCKET_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/cxsocket.c����������������������������������������������������������������0000644�0001750�0001750�00000022300�13061253352�015350� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * cxsocket.c: Socket wrapper classes * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_FORMAT_MACROS #include <inttypes.h> #include <stdlib.h> #include <stdarg.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #if !defined(__APPLE__) && !defined(__FreeBSD__) # include <sys/sendfile.h> #endif #include <netinet/tcp.h> #include <vdr/config.h> #include <vdr/tools.h> #include "../logdefs.h" #include "cxsocket.h" bool cxSocket::connect(struct sockaddr *addr, socklen_t len) { return ::connect(m_fd, addr, len) == 0; } bool cxSocket::connect(const char *ip, int port) { struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = inet_addr(ip); return connect((struct sockaddr *)&sin, sizeof(sin)); } bool cxSocket::set_blocking(bool state) { int flags = fcntl (m_fd, F_GETFL); if(flags == -1) { LOGERR("cxSocket::SetBlocking: fcntl(F_GETFL) failed"); return false; } flags = state ? (flags&(~O_NONBLOCK)) : (flags|O_NONBLOCK); if(fcntl (m_fd, F_SETFL, flags) == -1) { LOGERR("cxSocket::SetBlocking: fcntl(F_SETFL) failed"); return false; } return true; } bool cxSocket::set_buffers(int Tx, int Rx) { ::set_socket_buffers(m_fd, Tx, Rx); return true; } bool cxSocket::set_multicast(int ttl) { int iReuse = 1, iLoop = 1, iTtl = ttl; errno = 0; if(setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &iReuse, sizeof(int)) < 0) { LOGERR("cxSocket: setsockopt(SO_REUSEADDR) failed"); return false; } if(setsockopt(m_fd, IPPROTO_IP, IP_MULTICAST_TTL, &iTtl, sizeof(int))) { LOGERR("cxSocket: setsockopt(IP_MULTICAST_TTL, %d) failed", iTtl); return false; } if(setsockopt(m_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &iLoop, sizeof(int))) { LOGERR("cxSocket: setsockopt(IP_MULTICAST_LOOP) failed"); return false; } return true; } ssize_t cxSocket::sendfile(int fd_file, off_t *offset, size_t count) { int r; #if !defined(__APPLE__) && !defined(__FreeBSD__) r = ::sendfile(m_fd, fd_file, offset, count); if(r<0 && (errno == ENOSYS || errno == EINVAL)) { // fall back to read/write LOGERR("sendfile failed - using simple read/write"); #endif cxPoller p(*this, true); char buf[0x10000]; int todor = count, todow, done = 0; if(offset) if((r=::lseek(fd_file, *offset, SEEK_SET)) < 0) return r; todow = ::read(fd_file, buf, count>sizeof(buf) ? sizeof(buf) : count); if(todow <= 0) return todow; todor -= todow; while(todow > 0) { if(p.Poll(100)) { r = write(buf+done, todow); if(r <= 0) return r; todow -= r; done += r; } } return done; #if !defined(__APPLE__) && !defined(__FreeBSD__) } return r; #endif } bool cxSocket::set_cork(bool state) { #if defined(__APPLE__) || defined(__FreeBSD__) return false; #else int iCork = state ? 1 : 0; if(setsockopt(m_fd, IPPROTO_TCP, TCP_CORK, &iCork, sizeof(int))) { LOGERR("cxSocket: setsockopt(TCP_CORK) failed"); return false; } return true; #endif } bool cxSocket::set_nodelay(bool state) { int i = state ? 1 : 0; if(setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(int))) { LOGERR("cxSocket: setsockopt(TCP_NODELAY) failed"); return false; } return true; } ssize_t cxSocket::tx_buffer_size(void) { socklen_t l = sizeof(int); int wmem = -1; if(getsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &wmem, &l)) { LOGERR("getsockopt(SO_SNDBUF) failed"); return (ssize_t)-1; } return (ssize_t)wmem; } ssize_t cxSocket::tx_buffer_free(void) { int wmem = tx_buffer_size(); int size = -1; #if defined(__FreeBSD__) && defined(FIONWRITE) if(ioctl(m_fd, FIONWRITE, &size)) { LOGERR("ioctl(FIONWRITE) failed"); return (ssize_t)-1; } #else if(ioctl(m_fd, TIOCOUTQ, &size)) { LOGERR("ioctl(TIOCOUTQ) failed"); return (ssize_t)-1; } #endif return (ssize_t)(wmem - size); } int cxSocket::getsockname(struct sockaddr *name, socklen_t *namelen) { return ::getsockname(m_fd, name, namelen); } int cxSocket::getpeername(struct sockaddr *name, socklen_t *namelen) { return ::getpeername(m_fd, name, namelen); } ssize_t cxSocket::send(const void *buf, size_t size, int flags, const struct sockaddr *to, socklen_t tolen) { return ::sendto(m_fd, buf, size, flags, to, tolen); } ssize_t cxSocket::recv(void *buf, size_t size, int flags, struct sockaddr *from, socklen_t *fromlen) { return ::recvfrom(m_fd, buf, size, flags, from, fromlen); } ssize_t cxSocket::write(const void *buffer, size_t size, int timeout_ms) { ssize_t written = (ssize_t)size; const unsigned char *ptr = (const unsigned char *)buffer; cPoller poller(m_fd, true); while (size > 0) { errno = 0; if(!poller.Poll(timeout_ms)) { LOGERR("cxSocket::write: poll() failed"); return written-size; } errno = 0; ssize_t p = ::write(m_fd, ptr, size); if (p <= 0) { if (errno == EINTR || errno == EAGAIN) { LOGDBG("cxSocket::write: EINTR during write(), retrying"); continue; } LOGERR("cxSocket::write: write() error"); return p; } ptr += p; size -= p; } return written; } ssize_t cxSocket::read(void *buffer, size_t size, int timeout_ms) { ssize_t missing = (ssize_t)size; unsigned char *ptr = (unsigned char *)buffer; cPoller poller(m_fd); while (missing > 0) { if(!poller.Poll(timeout_ms)) { LOGERR("cxSocket::read: poll() failed at %d/%d", (int)(size-missing), (int)size); return size-missing; } errno = 0; ssize_t p = ::read(m_fd, ptr, missing); if (p <= 0) { if (errno == EINTR || errno == EAGAIN) { LOGDBG("cxSocket::read: EINTR/EAGAIN during read(), retrying"); continue; } LOGERR("cxSocket::read: read() error at %d/%d", (int)(size-missing), (int)size); return size-missing; } ptr += p; missing -= p; } return size; } ssize_t cxSocket::printf(const char *fmt, ...) { va_list argp; char buf[1024]; int r; va_start(argp, fmt); r = vsnprintf(buf, sizeof(buf), fmt, argp); va_end(argp); if(r<0) LOGERR("cxSocket::printf: vsnprintf failed"); else if(r >= (int)sizeof(buf)) LOGMSG("cxSocket::printf: vsnprintf overflow (%20s)", buf); else return write(buf, r); return (ssize_t)-1; } /* readline return value: * <0 : failed * >=maxsize : buffer overflow * >=0 : if errno = EAGAIN -> line is not complete (there was timeout) * if errno = 0 -> succeed * (return value 0 indicates empty line "\r\n") */ ssize_t cxSocket::readline(char *buf, int bufsize, int timeout, int bufpos) { int n = -1, cnt = bufpos; cPoller p(m_fd); do { if(timeout>0 && !p.Poll(timeout)) { errno = EAGAIN; return cnt; } while((n = ::read(m_fd, buf+cnt, 1)) == 1) { buf[++cnt] = 0; if( cnt > 1 && buf[cnt - 2] == '\r' && buf[cnt - 1] == '\n') { cnt -= 2; buf[cnt] = 0; errno = 0; return cnt; } if( cnt >= bufsize) { LOGMSG("cxSocket::readline: too long control message (%d bytes): %20s", cnt, buf); errno = 0; return bufsize; } } /* connection closed ? */ if (n == 0) { LOGMSG("cxSocket::readline: disconnected"); if(errno == EAGAIN) errno = ENOTCONN; return -1; } } while (timeout>0 && n<0 && errno == EAGAIN); if(errno == EAGAIN) return cnt; LOGERR("cxSocket::readline: read failed"); return n; } #include <sys/ioctl.h> #include <net/if.h> uint32_t cxSocket::get_local_address(char *ip_address) { uint32_t local_addr = 0; struct ifconf conf; struct ifreq buf[3]; unsigned int n; struct sockaddr_in sin; socklen_t len = sizeof(sin); if(!getsockname((struct sockaddr *)&sin, &len)) { local_addr = sin.sin_addr.s_addr; } else { //LOGERR("getsockname failed"); // scan network interfaces conf.ifc_len = sizeof(buf); conf.ifc_req = buf; memset(buf, 0, sizeof(buf)); errno = 0; if(ioctl(m_fd, SIOCGIFCONF, &conf) < 0) LOGERR("cxSocket: can't obtain socket local address"); else { for(n=0; n<conf.ifc_len/sizeof(struct ifreq); n++) { struct sockaddr_in *in = (struct sockaddr_in *) &buf[n].ifr_addr; # if 0 uint32_t tmp = ntohl(in->sin_addr.s_addr); LOGMSG("Local address %6s %d.%d.%d.%d", conf.ifc_req[n].ifr_name, ((tmp>>24)&0xff), ((tmp>>16)&0xff), ((tmp>>8)&0xff), ((tmp)&0xff)); # endif if(n==0 || local_addr == htonl(INADDR_LOOPBACK)) local_addr = in->sin_addr.s_addr; else break; } } } if(!local_addr) LOGERR("No local address found"); if(ip_address) cxSocket::ip2txt(local_addr, 0, ip_address); return local_addr; } char *cxSocket::ip2txt(uint32_t ip, unsigned int port, char *str) { // inet_ntoa is not thread-safe (?) if(str) { unsigned int iph =(unsigned int)ntohl(ip); unsigned int porth =(unsigned int)ntohs(port); if(!porth) sprintf(str, "%d.%d.%d.%d", ((iph>>24)&0xff), ((iph>>16)&0xff), ((iph>>8)&0xff), ((iph)&0xff)); else sprintf(str, "%u.%u.%u.%u:%u", ((iph>>24)&0xff), ((iph>>16)&0xff), ((iph>>8)&0xff), ((iph)&0xff), porth); } return str; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/bitstream.h���������������������������������������������������������������0000644�0001750�0001750�00000006610�13061253352�015532� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * bitstream.h: generic bitstream parsing * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIBOUTPUT_BITSTREAM_H_ #define _XINELIBOUTPUT_BITSTREAM_H_ # ifdef NOCACHE typedef struct { const uint8_t *data; size_t count; /* in bits */ size_t index; /* in bits */ } br_state; #define BR_INIT(data,bytes) { (data), 8*(bytes), 0 } #define BR_EOF(br) ((br)->index >= (br)->count) static inline void br_init(br_state *br, const uint8_t *data, size_t bytes) { br->data = data; br->count = 8*bytes; br->index = 0; } static inline int br_get_bit(br_state *br) { if(br->index >= br->count) return 1; /* -> no infinite colomb's ... */ int r = (br->data[br->index>>3] >> (7 - (br->index&7))) & 1; br->index++; return r; } static inline uint32_t br_get_bits(br_state *br, uint32_t n) { uint32_t r = 0; while(n--) r = r | (br_get_bit(br) << n); return r; } #define br_skip_bit(br) br_skip_bits(br,1) static inline void br_skip_bits(br_state *br, int n) { br->index += n; } # else /* NOCACHE */ typedef struct { const uint8_t *data; const uint8_t *data_end; uint32_t cache; uint32_t cache_bits; } br_state; #define BR_INIT(data,bytes) { (data), (data)+(bytes), 0, 0 } static inline void br_init(br_state *br, const uint8_t *data, size_t bytes) { br->data = data; br->data_end = data + bytes; br->cache = 0; br->cache_bits = 0; } #define BR_GET_BYTE(br) \ (br->data < br->data_end ? *br->data++ : 0xff) #define BR_EOF(br) ((br)->data >= (br)->data_end) static inline uint32_t br_get_bits(br_state *br, uint32_t n) { if(n > 24) return (br_get_bits(br, 16) << 16) | br_get_bits(br, n-16); while (br->cache_bits < 24) { br->cache = (br->cache<<8) | BR_GET_BYTE(br); br->cache_bits += 8; } br->cache_bits -= n; return (br->cache >> br->cache_bits) & ((1<<n) - 1); } static inline int br_get_bit(br_state *br) { if(!br->cache_bits) { br->cache = BR_GET_BYTE(br); br->cache_bits = 7; } else { br->cache_bits--; } return (br->cache >> br->cache_bits) & 1; } static inline void br_skip_bit(br_state *br) { if(!br->cache_bits) { br->cache = BR_GET_BYTE(br); br->cache_bits = 7; } else { br->cache_bits--; } } static inline void br_skip_bits(br_state *br, int n) { if(br->cache_bits >= n) { br->cache_bits -= n; } else { /* drop cached bits */ n -= br->cache_bits; /* drop full bytes */ br->data += (n >> 3); n &= 7; /* update cache */ if(n) { br->cache = BR_GET_BYTE(br); br->cache_bits = 8 - n; } else { br->cache_bits = 0; } } } # endif /* NOCACHE */ #define br_get_u8(br) br_get_bits(br, 8) #define br_get_u16(br) ((br_get_bits(br, 8)<<8) | br_get_bits(br, 8)) static inline uint32_t br_get_ue_golomb(br_state *br) { int n = 0; while (!br_get_bit(br) && n < 32) n++; return n ? ((1<<n) - 1) + br_get_bits(br, n) : 0; } static inline int32_t br_get_se_golomb(br_state *br) { uint32_t r = br_get_ue_golomb(br) + 1; return (r&1) ? -(r>>1) : (r>>1); } static inline void br_skip_golomb(br_state *br) { int n = 0; while (!br_get_bit(br) && n < 32) n++; br_skip_bits(br, n); } #define br_skip_ue_golomb(br) br_skip_golomb(br) #define br_skip_se_golomb(br) br_skip_golomb(br) #endif /* _XINELIBOUTPUT_BITSTREAM_H_ */ ������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/backgroundwriter.h��������������������������������������������������������0000644�0001750�0001750�00000007662�13061253352�017124� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * backgroundwriter.h: Buffered socket/file writing thread * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __BACKGROUNDWRITER_H #define __BACKGROUNDWRITER_H #include <stdint.h> #include <vdr/thread.h> #include <vdr/ringbuffer.h> // // cBackgroundWriterI // - generic interface for buffered output // class cBackgroundWriterI : public cThread { protected: cRingBufferLinear m_RingBuffer; int m_fd; bool m_IsSocket; uint64_t m_PutPos; uint64_t m_DiscardStart; uint64_t m_DiscardEnd; int m_BufferOverflows; protected: virtual void Action(void) = 0; void Cork(void); public: #if VDRVERSNUM >= 10708 cBackgroundWriterI(int fd, int Size = KILOBYTE(2048), int Margin = 0); #else cBackgroundWriterI(int fd, int Size = KILOBYTE(512), int Margin = 0); #endif virtual ~cBackgroundWriterI(); // Add PES frame to buffer // // Return value: // Success: Count (all bytes pushed to queue) // Error: 0 (write error ; socket disconnected) // Buffer full: -Count (no bytes will be pushed to queue) // virtual int Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount) = 0; int Free(void); // Return largest possible Put size void Clear(void); // Drop all data (only complete frames) from buffer bool Flush(int TimeoutMs); // Flush buffer (wait for data to be sent) }; // // cTcpWriter // - xineliboutput TCP data steam // - stream_tcp_header_t encapsulated PES frames // class cTcpWriter : public cBackgroundWriterI { protected: virtual void Action(void); int Put(const uchar *Header, int HeaderCount, const uchar *Data, int DataCount); public: #if VDRVERSNUM >= 10708 cTcpWriter(int fd, int Size = KILOBYTE(2048)); #else cTcpWriter(int fd, int Size = KILOBYTE(512)); #endif virtual ~cTcpWriter() {}; virtual int Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount); }; // // cRawWriter // - Raw PES stream // - Used with HTTP // class cRawWriter : public cBackgroundWriterI { protected: virtual void Action(void); public: cRawWriter(int fd, int Size = KILOBYTE(512)); virtual ~cRawWriter() {}; virtual int Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount); }; // // cTsWriter // - Demux PES stream to PS // class cTsWriter : public cBackgroundWriterI { protected: virtual void Action(void); public: cTsWriter(int fd, int Size = KILOBYTE(512)); virtual ~cTsWriter() {}; virtual int Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount); }; // // cRtspMuxWriter // - RTSP multiplexed control+data // - Each encapsulated PES frame is written atomically to socket buffer // - Atomic control data can be written directly to socket // from another thread to bypass buffer // class cRtspMuxWriter : public cBackgroundWriterI { protected: virtual void Action(void); public: cRtspMuxWriter(int fd, int Size = KILOBYTE(512)); virtual ~cRtspMuxWriter() {}; virtual int Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount); }; // // cRtspRemuxWriter // - RTSP multiplexed control+data // - Demux PES stream to independent ES streams // - encapsulate ES to RTP/AVP compatible frames // - Mux RTP/AVP ES streams to pipelined RTCP control connection // - Each encapsulated frame is written atomically to socket buffer // - Atomic control data can be written directly to socket // from another thread to bypass buffer // class cRtspRemuxWriter : public cBackgroundWriterI { protected: virtual void Action(void); public: cRtspRemuxWriter(int fd, int Size = KILOBYTE(512)); virtual ~cRtspRemuxWriter() {}; virtual int Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount); }; #endif ������������������������������������������������������������������������������xineliboutput-2.0.0/tools/backgroundwriter.c��������������������������������������������������������0000644�0001750�0001750�00000026274�13061253352�017117� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * backgroundwriter.h: Buffered socket/file writing thread * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_FORMAT_MACROS #define __STDC_CONSTANT_MACROS #include <inttypes.h> #include <stdint.h> #include <unistd.h> #ifdef __FreeBSD__ #include <sys/socket.h> #include <netinet/in.h> #endif #include <netinet/tcp.h> // CORK, NODELAY #include <vdr/tools.h> #include <vdr/config.h> // VDRVERSNUM #include "../logdefs.h" #include "../xine_input_vdr_net.h" // stream_tcp_header_t #include "ts.h" #include "pes.h" #include "backgroundwriter.h" #define MAX_OVERFLOWS_BEFORE_DISCONNECT 1000 // ~ 1 second // // cBackgroundWriterI // cBackgroundWriterI::cBackgroundWriterI(int fd, int Size, int Margin) : m_RingBuffer(Size, Margin) { m_fd = fd; m_RingBuffer.SetTimeouts(0, 100); m_PutPos = 0; m_DiscardStart = 0; m_DiscardEnd = 0; m_BufferOverflows = 0; #if defined(TCP_CORK) int iCork = 1; if (setsockopt(m_fd, IPPROTO_TCP, TCP_CORK, &iCork, sizeof(int))) { if (errno != ENOTSOCK) LOGERR("cBackgroundWriter: setsockopt(TCP_CORK) failed"); m_IsSocket = false; errno = 0; } else { m_IsSocket = true; } #elif defined(TCP_NOPUSH) int iCork = 1; if (setsockopt(m_fd, IPPROTO_TCP, TCP_NOPUSH, &iCork, sizeof(int))) { if (errno != ENOTSOCK) LOGERR("cBackgroundWriter: setsockopt(TCP_NOPUSH) failed"); m_IsSocket = false; errno = 0; } else { m_IsSocket = true; } #endif LOGDBG("cBackgroundWriterI initialized (buffer %d kb)", Size/1024); } cBackgroundWriterI::~cBackgroundWriterI() { Cancel(3); } int cBackgroundWriterI::Free(void) { return m_RingBuffer.Free(); } void cBackgroundWriterI::Clear(void) { // Can't just drop buffer contents or PES frames will be broken. // Serialize with Put LOCK_THREAD; m_DiscardEnd = m_PutPos; } void cBackgroundWriterI::Cork(void) { if (m_IsSocket) { #if defined(TCP_CORK) int i = 1; if (setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(int))) { LOGERR("cBackgroundWriter: setsockopt(TCP_NODELAY) failed"); errno = 0; } #elif defined(TCP_NOPUSH) int On = 1, Off = 0; if (setsockopt(m_fd, IPPROTO_TCP, TCP_NOPUSH, &Off, sizeof(int)) || setsockopt(m_fd, IPPROTO_TCP, TCP_NOPUSH, &On, sizeof(int))) { LOGERR("cBackgroundWriter: setsockopt(TCP_NOPUSH) failed"); errno = 0; } #endif } } bool cBackgroundWriterI::Flush(int TimeoutMs) { uint64_t WaitEnd = cTimeMs::Now(); // wait for ring buffer to drain if (TimeoutMs > 0) { WaitEnd += (uint64_t)TimeoutMs; while (cTimeMs::Now() < WaitEnd && Running() && m_RingBuffer.Available() > 0) cCondWait::SleepMs(3); } int Available = m_RingBuffer.Available(); if (m_IsSocket && Available <= 0) { // flush corked data too Cork(); } return Available <= 0; } // // cTcpWriter // cTcpWriter::cTcpWriter(int fd, int Size) : cBackgroundWriterI(fd, Size, sizeof(stream_tcp_header_t)) { LOGDBG("cTcpWriter initialized (buffer %d kb)", Size/1024); Start(); } void cTcpWriter::Action(void) { uint64_t NextHeaderPos = 0; uint64_t GetPos = 0; cPoller Poller (m_fd, true); bool CorkReq = false; while (Running()) { if (!Poller.Poll(100)) continue; if (CorkReq && m_RingBuffer.Available() <= 0) { // Force TCP packet to avoid delaying control messages Cork(); CorkReq = false; } uint64_t StartPos; int Count = 0; int n; uchar *Data = m_RingBuffer.Get(Count); if (!Data || Count <= 0) continue; Lock(); // uint64_t m_DiscardStart can not be read atomically (IA32) StartPos = m_DiscardEnd; Unlock(); // Next frame ? if (NextHeaderPos == GetPos) { // Discard data ? if (StartPos > GetPos) { // we're at frame boundary // drop only data packets, not control messages stream_tcp_header_t *header = (stream_tcp_header_t*)Data; if (eStreamId(header->stream) == sidVdr) { Count = min(Count, (int)(StartPos - GetPos)); // size of next (complete) packet. // drop only one packet at time. int pkt_len = ntohl(header->len) + sizeof(stream_tcp_header_t); if (Count >= pkt_len) { // drop only complete packets. // some packets are not dropped (packets overlapping end of ringbuffer) Count = pkt_len; m_RingBuffer.Del(Count); GetPos += Count; NextHeaderPos = GetPos; CorkReq = true; // force sending last frame continue; } } } // Next frame if (Count < (int)sizeof(stream_tcp_header_t)) LOGMSG("cBackgroundWriter @NextHeaderPos: Count < header size !"); // limit single write to size of next (complete) packet. // (we need to track packet boundaries) stream_tcp_header_t *header = (stream_tcp_header_t*)Data; int pkt_len = ntohl(header->len) + sizeof(stream_tcp_header_t); if (Count > pkt_len) Count = pkt_len; // next packet start position in stream NextHeaderPos = GetPos + pkt_len; // check for control message if (eStreamId(header->stream) == sidControl) CorkReq = true; } else { // end of prev frame Count = min(Count, (int)(NextHeaderPos-GetPos)); } errno = 0; n = write(m_fd, Data, Count); if (n <= 0) { if (n == 0) { LOGERR("cBackgroundWriter: Client disconnected data stream ?"); break; } if (errno == EINTR || errno == EWOULDBLOCK) continue; LOGERR("cBackgroundWriter: TCP write error"); break; } GetPos += n; m_RingBuffer.Del(n); } m_RingBuffer.Clear(); } int cTcpWriter::Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount) { stream_tcp_header_t header; header.pos = htonull(StreamPos); header.len = htonl(DataCount); header.stream = (uint8_t)StreamId; return Put((uchar*)&header, sizeof(header), Data, DataCount); } int cTcpWriter::Put(const uchar *Header, int HeaderCount, const uchar *Data, int DataCount) { if (Running()) { // Serialize Put access to keep Data and Header together LOCK_THREAD; if (m_RingBuffer.Free() < HeaderCount+DataCount) { //LOGMSG("cXinelibServer: TCP buffer overflow !"); if (m_BufferOverflows++ > MAX_OVERFLOWS_BEFORE_DISCONNECT) { LOGMSG("cXinelibServer: Too many TCP buffer overflows, dropping client"); m_RingBuffer.Clear(); Cancel(-1); return 0; } return -HeaderCount-DataCount; } int n = m_RingBuffer.Put(Header, HeaderCount) + m_RingBuffer.Put(Data, DataCount); if (n == HeaderCount + DataCount) { m_BufferOverflows = 0; m_PutPos += n; return n; } LOGMSG("cXinelibServer: TCP buffer internal error ?!?"); m_RingBuffer.Clear(); Cancel(-1); } return 0; } // // cRawWriter // cRawWriter::cRawWriter(int fd, int Size) : cBackgroundWriterI(fd, Size, 6) { LOGDBG("cRawWriter initialized (buffer %d kb)", Size/1024); Start(); } void cRawWriter::Action(void) { uint64_t NextHeaderPos = 0ULL; uint64_t GetPos = 0ULL; cPoller Poller(m_fd, true); while (Running()) { if (!Poller.Poll(100)) continue; uint64_t StartPos; int Count = 0; int n; uchar *Data = m_RingBuffer.Get(Count); if (!Data || Count <= 0) continue; Lock(); // uint64_t m_DiscardStart can not be read atomically (IA32) StartPos = m_DiscardEnd; Unlock(); // Next frame ? if (NextHeaderPos == GetPos) { // Discard data ? if (StartPos > GetPos) { // we're at frame boundary Count = min(Count, (int)(StartPos - GetPos)); m_RingBuffer.Del(Count); GetPos += Count; NextHeaderPos = GetPos; continue; } // Next frame if (Count < 6) LOGMSG("cBackgroundWriter @NextHeaderPos: Count < header size !"); int packlen = DATA_IS_TS(Data) ? TS_SIZE : pes_packet_len(Data, Count); if (Count < packlen) ;//LOGMSG("Count = %d < %d", Count, // header->len + sizeof(stream_tcp_header_t)); else Count = packlen; NextHeaderPos = GetPos + packlen; } else { // end of prev frame Count = min(Count, (int)(NextHeaderPos-GetPos)); } errno = 0; n = write(m_fd, Data, Count); if (n <= 0) { if (n == 0) { LOGERR("cBackgroundWriter: Client disconnected data stream ?"); break; } if (errno == EINTR || errno == EWOULDBLOCK) continue; LOGERR("cBackgroundWriter: TCP write error"); break; } GetPos += n; m_RingBuffer.Del(n); } m_RingBuffer.Clear(); } int cRawWriter::Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount) { if (Running() && StreamId == sidVdr) { // Serialize Put access to keep Data and Header together LOCK_THREAD; if (m_RingBuffer.Free() < DataCount) { if (m_BufferOverflows++ > MAX_OVERFLOWS_BEFORE_DISCONNECT) { LOGMSG("cXinelibServer: Too many TCP buffer overflows, dropping client"); m_RingBuffer.Clear(); Cancel(-1); return 0; } return -DataCount; } int n = m_RingBuffer.Put(Data, DataCount); if (n == DataCount) { m_BufferOverflows = 0; m_PutPos += n; return n; } LOGMSG("cXinelibServer: TCP buffer internal error ?!?"); m_RingBuffer.Clear(); Cancel(-1); } return 0; } // // cTsWriter // - Demux PES stream to PS // cTsWriter::cTsWriter(int fd, int Size) : cBackgroundWriterI(fd, Size, 6) { LOGDBG("cTsWriter initialized (buffer %d kb)", Size/1024); Start(); } void cTsWriter::Action(void) { } int cTsWriter::Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount) { return 0; } // // cRtspMuxWriter // - RTSP multiplexed control+data // - Each encapsulated PES frame is written atomically to socket buffer // - Atomic control data can be written directly to socket // from another thread to bypass buffer // cRtspMuxWriter::cRtspMuxWriter(int fd, int Size) : cBackgroundWriterI(fd, Size, 6) { LOGDBG("cRtspMuxWriter initialized (buffer %d kb)", Size/1024); Start(); } void cRtspMuxWriter::Action(void) { } int cRtspMuxWriter::Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount) { return 0; } // // cRtspRemuxWriter // - RTSP multiplexed control+data // - Demux PES stream to independent ES streams // - encapsulate ES to RTP/AVP compatible frames // - Mux RTP/AVP ES streams to pipelined RTCP control connection // - Each encapsulated frame is written atomically to socket buffer // - Atomic control data can be written directly to socket // from another thread to bypass buffer // cRtspRemuxWriter::cRtspRemuxWriter(int fd, int Size) : cBackgroundWriterI(fd, Size, 6) { LOGDBG("cRtspRemuxWriter initialized (buffer %d kb)", Size/1024); Start(); } void cRtspRemuxWriter::Action(void) { } int cRtspRemuxWriter::Put(eStreamId StreamId, uint64_t StreamPos, const uchar *Data, int DataCount) { return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/avahi.h�������������������������������������������������������������������0000644�0001750�0001750�00000000516�13061253352�014627� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * avahi.h: mDNS announce * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef XINELIBOUTPUT_AVAHI_H_ #define XINELIBOUTPUT_AVAHI_H_ void x_avahi_stop(void *h); void *x_avahi_start(int port, int rtsp, int http); #endif /* XINELIBOUTPUT_AVAHI_H_ */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/tools/avahi.c�������������������������������������������������������������������0000644�0001750�0001750�00000014121�13061253352�014617� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * avahi.c: mDNS announce * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "avahi.h" #include "../logdefs.h" // logging #include "../features.h" #include <stdlib.h> #ifdef HAVE_AVAHI_CLIENT #include <errno.h> #include <stdio.h> // snprintf #include <string.h> #include <pthread.h> #include <avahi-client/client.h> #include <avahi-client/publish.h> #include <avahi-common/alternative.h> #include <avahi-common/simple-watch.h> #include <avahi-common/malloc.h> #include <avahi-common/error.h> #include <avahi-common/timeval.h> typedef struct { AvahiEntryGroup *group; AvahiSimplePoll *simple_poll; char *name; int port; int rtsp; int http; pthread_t thread; } avahi_data; static void _create_services(AvahiClient *c, avahi_data *d); static void _entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { avahi_data *d = (avahi_data *)userdata; d->group = g; switch (state) { case AVAHI_ENTRY_GROUP_ESTABLISHED: LOGMSG("AVAHI service '%s' successfully established.", d->name); break; case AVAHI_ENTRY_GROUP_COLLISION: { char *n; /* A service name collision with a remote service * happened. Let's pick a new name */ n = avahi_alternative_service_name(d->name); avahi_free(d->name); d->name = n; LOGERR("AVAHI service name collision, renaming service to '%s'", d->name); _create_services(avahi_entry_group_get_client(g), d); break; } case AVAHI_ENTRY_GROUP_FAILURE : LOGERR("AVAHI entry group failure: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); avahi_simple_poll_quit(d->simple_poll); break; case AVAHI_ENTRY_GROUP_UNCOMMITED: case AVAHI_ENTRY_GROUP_REGISTERING: break; } } static void _create_services(AvahiClient *c, avahi_data *d) { char *n; int ret; if (!d->group) { if (!(d->group = avahi_entry_group_new(c, _entry_group_callback, d))) { LOGERR("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(c))); goto fail; } } if (avahi_entry_group_is_empty(d->group)) { LOGMSG("AVAHI: adding service '%s'", d->name); if (d->rtsp) { if ((ret = avahi_entry_group_add_service(d->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET/*UNSPEC*/, (AvahiPublishFlags)0, d->name, "_rtsp._tcp", NULL, NULL, d->port, NULL)) < 0) { if (ret == AVAHI_ERR_COLLISION) goto collision; LOGERR("AVAHI failed to add _rtsp._tcp service: %s", avahi_strerror(ret)); goto fail; } } if (d->http) { if ((ret = avahi_entry_group_add_service(d->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET/*UNSPEC*/, (AvahiPublishFlags)0, d->name, "_http._tcp", NULL, NULL, d->port, NULL)) < 0) { if (ret == AVAHI_ERR_COLLISION) goto collision; LOGERR("AVAHI failed to add _http._tcp service: %s", avahi_strerror(ret)); goto fail; } } if ((ret = avahi_entry_group_add_service(d->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET/*UNSPEC*/, (AvahiPublishFlags)0, d->name, "_xvdr._tcp", NULL, NULL, d->port, NULL)) < 0) { if (ret == AVAHI_ERR_COLLISION) goto collision; LOGERR("AVAHI failed to add _xvdr._tcp service: %s", avahi_strerror(ret)); goto fail; } /* Tell the server to register the service */ if ((ret = avahi_entry_group_commit(d->group)) < 0) { LOGERR("AVAHI failed to commit entry group: %s", avahi_strerror(ret)); goto fail; } } return; collision: n = avahi_alternative_service_name(d->name); avahi_free(d->name); d->name = n; LOGMSG("AVAHI service name collision, renaming service to '%s'", d->name); avahi_entry_group_reset(d->group); _create_services(c, d); return; fail: avahi_simple_poll_quit(d->simple_poll); } static void _client_callback(AvahiClient *c, AvahiClientState state, void * userdata) { avahi_data *d = (avahi_data *)userdata; switch (state) { case AVAHI_CLIENT_S_RUNNING: _create_services(c, d); break; case AVAHI_CLIENT_FAILURE: LOGERR("AVAHI client failure: %s", avahi_strerror(avahi_client_errno(c))); avahi_simple_poll_quit(d->simple_poll); break; case AVAHI_CLIENT_S_COLLISION: case AVAHI_CLIENT_S_REGISTERING: if (d->group) avahi_entry_group_reset(d->group); break; case AVAHI_CLIENT_CONNECTING: break; } } /* * */ static void *_avahi_run(void *h) { avahi_data *d = (avahi_data *)h; AvahiClient *client = NULL; int error; if (!(d->simple_poll = avahi_simple_poll_new())) { LOGMSG("AVAHI failed to create simple poll object"); return NULL; } d->name = avahi_strdup("VDR (xineliboutput)"); client = avahi_client_new(avahi_simple_poll_get(d->simple_poll), (AvahiClientFlags)0, _client_callback, d, &error); if (!client) { LOGERR("AVAHI failed to create client: %s", avahi_strerror(error)); return NULL; } /* Run the main loop */ avahi_simple_poll_loop(d->simple_poll); LOGMSG("AVAHI terminating"); avahi_client_free(client); return NULL; } void *x_avahi_start(int port, int rtsp, int http) { void *h = calloc(1, sizeof(avahi_data)); if (h) { avahi_data *d = (avahi_data *)h; int err; d->port = port; d->rtsp = rtsp; d->http = http; if ((err = pthread_create (&d->thread, NULL, _avahi_run, h)) != 0) { LOGERR("AVAHI can't create new thread (%s)", strerror(err)); free(h); h = NULL; } } return h; } static void _avahi_free(void *h) { avahi_data *d = (avahi_data *)h; if (d->simple_poll) avahi_simple_poll_free(d->simple_poll); avahi_free(d->name); free(h); } void x_avahi_stop(void *h) { avahi_data *d = (avahi_data *)h; void *p; avahi_simple_poll_quit(d->simple_poll); pthread_cancel (d->thread); pthread_join (d->thread, &p); _avahi_free(d); } #else /* HAVE_AVAHI */ void *x_avahi_start(int port, int rtsp, int http) { return NULL; } void x_avahi_stop(void *h) { } #endif /* HAVE_AVAHI */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/setup_menu.h��������������������������������������������������������������������0000644�0001750�0001750�00000001047�13061253352�014563� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * setup_menu.h: Setup Menu * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_SETUP_MENU_H #define __XINELIB_SETUP_MENU_H #include <vdr/menuitems.h> class cXinelibDevice; class cMenuSetupXinelib : public cMenuSetupPage { protected: cXinelibDevice *m_Dev; void Set(void); virtual void Store(void) {}; public: cMenuSetupXinelib(cXinelibDevice *Dev); virtual eOSState ProcessKey(eKeys Key); }; #endif //__XINELIB_SETUP_MENU_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/setup_menu.c��������������������������������������������������������������������0000644�0001750�0001750�00000204462�13061253352�014564� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * setup_menu.c: Setup Menu * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "features.h" #include <vdr/config.h> #include <vdr/plugin.h> #include <vdr/remote.h> #include <vdr/i18n.h> #include "config.h" #include "device.h" #include "menuitems.h" #include "osd.h" // cXinelibOsdProvider::RefreshOsd() #include "setup_menu.h" #include "tools/playlist.h" #define indent(x) Label_Ident(x) #define inden2(x) Label_Ident(Label_Ident(x)) namespace XinelibOutputSetupMenu { //#define INTEGER_CONFIG_VIDEO_CONTROLS //#define LINEAR_VIDEO_CONTROLS //#define LOGARITHM_SCALING #define ISNUMBERKEY(k) (RAWKEY(k) >= k0 && RAWKEY(k) <= k9) //--- Setup Menu ------------------------------------------------------------- const char ModeLineChars[] = " 0123456789+-hvsync."; const char DriverNameChars[] = " abcdefghijklmnopqrstuvwxyz0123456789-.,#~:;"; const char OptionsChars[] = "=.,abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const char LangNameChars[] = "abcdefghijklmnopqrstuvwxyz"; const char *controls[] = { "Off", "[|---------------]","[|---------------]", "[-|--------------]","[-|--------------]", "[--|-------------]","[--|-------------]", "[---|------------]","[---|------------]", "[----|-----------]","[----|-----------]", "[-----|----------]","[-----|----------]", "[------|---------]","[------|---------]", "[-------|--------]","[-------|--------]", "[--------|-------]","[--------|-------]", "[---------|------]","[---------|------]", "[----------|-----]","[----------|-----]", "[-----------|----]","[-----------|----]", "[------------|---]","[------------|---]", "[-------------|--]","[-------------|--]", "[--------------|-]","[--------------|-]", "[---------------|]","[---------------|]", NULL }; #ifdef LINEAR_VIDEO_CONTROLS # define CONTROL_TO_INDEX(val) ((val)>=0 ? ((val)>>11)+1 : 0) # define INDEX_TO_CONTROL(ind) ((ind)==0 ? -1 : ((ind)-1)<<11) #else #ifdef LOGARITHM_SCALING const int ind2ctrl_tbl[33] = { -1, 0, 0x0001, 0x0002, 0x0003, 0x0004, 0x0007, 0x000a, 0x000f, 0x0014, 0x001f, 42, 0x003f, 80, 0x007f, 170, 0x00ff, 336, 0x01ff, 682, 0x03ff, 1630, 0x07ff, 2730, 0x0fff, 5726, 0x1fff, 10858, 0x3fff, 22110, 0x7fff, 43224, 0xffff }; #else const int ind2ctrl_tbl[33] = { -1, 0x0000, 0x0843, 0x1085, 0x18c7, 0x2109, 0x294b, 0x318d, 0x39cf, 0x4211, 0x4a53, 0x5295, 0x5ad7, 0x6319, 0x6b5b, 0x739d, 0x7bdf, 0x8421, 0x8c63, 0x94a5, 0x9ce7, 0xa529, 0xad6b, 0xb5ad, 0xbdef, 0xc631, 0xce73, 0xd6b5, 0xdef7, 0xe739, 0xef7b, 0xf7bd, 0xffff }; #endif static int CONTROL_TO_INDEX(int val) { for(int i=0; i<33;i++) if(val<=ind2ctrl_tbl[i]) return i; return 32; } static int INDEX_TO_CONTROL(int ind) { if(ind<0) ind=0; if(ind>32) ind=32; return ind2ctrl_tbl[ind]; } #endif //--- cMenuSetupAudio -------------------------------------------------------- class cMenuSetupAudio : public cMenuSetupPage { private: cXinelibDevice *m_Dev; config_t newconfig; int visualization; int goom_width, goom_height, goom_fps; cOsdItem *audio_ctrl_volume; cOsdItem *audio_ctrl_delay; cOsdItem *audio_ctrl_compression; cOsdItem *audio_ctrl_upmix; cOsdItem *audio_ctrl_surround; cOsdItem *audio_ctrl_headphone; cOsdItem *audio_ctrl_vis; protected: virtual void Store(void); void Set(void); public: cMenuSetupAudio(cXinelibDevice *Dev); ~cMenuSetupAudio(void); virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupAudio::cMenuSetupAudio(cXinelibDevice *Dev) { m_Dev = Dev; memcpy(&newconfig, &xc, sizeof(config_t)); visualization = strstra(xc.audio_visualization, xc.s_audioVisualizations, 0); goom_width = 720; goom_height = 576; goom_fps = 25; char *pt; if(NULL != (pt=strstr(xc.audio_vis_goom_opts, "width="))) goom_width = max(320, min(1920, atoi(pt+6))); if(NULL != (pt=strstr(xc.audio_vis_goom_opts, "height="))) goom_height = max(240, min(1280, atoi(pt+7))); if(NULL != (pt=strstr(xc.audio_vis_goom_opts, "fps="))) goom_fps = max(1, min(100, atoi(pt+4))); Set(); } cMenuSetupAudio::~cMenuSetupAudio(void) { m_Dev->ConfigurePostprocessing( xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); m_Dev->ConfigurePostprocessing("upmix", xc.audio_upmix ? true : false, NULL); #ifdef ENABLE_TEST_POSTPLUGINS m_Dev->ConfigurePostprocessing("headphone", xc.headphone ? true : false, NULL); #endif } void cMenuSetupAudio::Set(void) { SetPlugin(cPluginManager::GetPlugin(PLUGIN_NAME_I18N)); int current = Current(); Clear(); Add(SeparatorItem(tr("Audio"))); Add(audio_ctrl_volume = new cMenuEditBoolItem(tr("Volume control"), &newconfig.sw_volume_control, tr("Hardware"), tr("Software"))); Add(audio_ctrl_delay = new cMenuEditTypedIntItem(tr("Delay"), tr("ms"), &newconfig.audio_delay, -3000, 3000, tr("Off"))); Add(audio_ctrl_compression = new cMenuEditTypedIntItem(tr("Audio Compression"), "%", &newconfig.audio_compression, 100, 500, NULL, tr("Off"))); Add(audio_ctrl_upmix = new cMenuEditBoolItem(tr("Upmix stereo to 5.1"), &newconfig.audio_upmix)); Add(audio_ctrl_surround = new cMenuEditBoolItem(tr("Downmix AC3 to surround"), &newconfig.audio_surround)); #ifdef ENABLE_TEST_POSTPLUGINS Add(audio_ctrl_headphone = new cMenuEditBoolItem(tr("Mix to headphones"), &newconfig.headphone)); #else audio_ctrl_headphone = NULL; #endif Add(audio_ctrl_vis = new cMenuEditStraI18nItem(tr("Visualization"), &visualization, AUDIO_VIS_count, xc.s_audioVisualizationNames)); if(visualization == AUDIO_VIS_GOOM) { Add(new cMenuEditTypedIntItem(indent(tr("Width")), tr("px"), &goom_width, 320, 1920)); Add(new cMenuEditTypedIntItem(indent(tr("Height")),tr("px"), &goom_height, 240, 1280)); Add(new cMenuEditTypedIntItem(indent(tr("Speed")), tr("fps"), &goom_fps, 1, 100)); } else if(visualization == AUDIO_VIS_IMAGE) { Add(new cMenuEditStrItem(indent(tr("Background image MRL")), newconfig.audio_vis_image_mrl, sizeof(newconfig.audio_vis_image_mrl))); } if(current<1) current=1; /* first item is not selectable */ SetCurrent(Get(current)); Display(); } eOSState cMenuSetupAudio::ProcessKey(eKeys Key) { cOsdItem *item = Get(Current()); eOSState state = cMenuSetupPage::ProcessKey(Key); Key = NORMALKEY(Key); if(Key!=kLeft && Key!=kRight) return state; if(item == audio_ctrl_delay || item == audio_ctrl_compression) { m_Dev->ConfigurePostprocessing( xc.deinterlace_method, newconfig.audio_delay, newconfig.audio_compression, newconfig.audio_equalizer, newconfig.audio_surround, newconfig.speaker_type); } else if(item == audio_ctrl_vis) { Set(); } else if(item == audio_ctrl_surround) { m_Dev->ConfigurePostprocessing( xc.deinterlace_method, newconfig.audio_delay, newconfig.audio_compression, newconfig.audio_equalizer, newconfig.audio_surround, newconfig.speaker_type); if(newconfig.audio_surround && newconfig.audio_upmix) { newconfig.audio_upmix = 0; Set(); } } else if(item == audio_ctrl_volume) { // trigger volume control message by toggling mute cRemote::Put(kMute); cRemote::Put(kMute); } else if(item == audio_ctrl_upmix) { m_Dev->ConfigurePostprocessing( "upmix", newconfig.audio_upmix ? true : false, NULL); if(newconfig.audio_upmix && newconfig.audio_surround) { newconfig.audio_surround = 0; Set(); } } #ifdef ENABLE_TEST_POSTPLUGINS else if(item == audio_ctrl_headphone) { m_Dev->ConfigurePostprocessing( "headphone", newconfig.headphone ? true : false, NULL); } #endif return state; } void cMenuSetupAudio::Store(void) { memcpy(&xc, &newconfig, sizeof(config_t)); strn0cpy(xc.audio_visualization, xc.s_audioVisualizations[visualization], sizeof(xc.audio_visualization)); snprintf(xc.audio_vis_goom_opts, sizeof(xc.audio_vis_goom_opts), "width=%d,height=%d,fps=%d", goom_width, goom_height, goom_fps); xc.audio_vis_goom_opts[sizeof(xc.audio_vis_goom_opts)-1] = 0; if(xc.audio_vis_image_mrl[0] == '/') snprintf(xc.audio_vis_image_mrl, sizeof(xc.audio_vis_image_mrl), "%s", *cPlaylist::BuildMrl("file", xc.audio_vis_image_mrl)); SetupStore("Audio.Delay", xc.audio_delay); SetupStore("Audio.Compression", xc.audio_compression); SetupStore("Audio.Surround", xc.audio_surround); SetupStore("Audio.Upmix", xc.audio_upmix); SetupStore("Audio.Headphone", xc.headphone); SetupStore("Audio.Visualization",xc.audio_visualization); SetupStore("Audio.Visualization.GoomOpts",xc.audio_vis_goom_opts); SetupStore("Audio.Visualization.ImageMRL",xc.audio_vis_image_mrl); SetupStore("Audio.SoftwareVolumeControl", xc.sw_volume_control); Setup.Save(); } //--- cMenuSetupAudioEq ------------------------------------------------------ class cMenuSetupAudioEq : public cMenuSetupPage { private: cXinelibDevice *m_Dev; config_t newconfig; protected: virtual void Store(void); void Set(void); public: cMenuSetupAudioEq(cXinelibDevice *Dev); ~cMenuSetupAudioEq(void); virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupAudioEq::cMenuSetupAudioEq(cXinelibDevice *Dev) { m_Dev = Dev; memcpy(&newconfig, &xc, sizeof(config_t)); Set(); } cMenuSetupAudioEq::~cMenuSetupAudioEq(void) { m_Dev->ConfigurePostprocessing( xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); } void cMenuSetupAudioEq::Set(void) { SetPlugin(cPluginManager::GetPlugin(PLUGIN_NAME_I18N)); int current = Current(); Clear(); Add(SeparatorItem(tr("Audio Equalizer"))); for(int i=0; i<AUDIO_EQ_count; i++) Add(new cMenuEditTypedIntItem(config_t::s_audioEqNames[i], "%", &newconfig.audio_equalizer[i], -100, 100, tr("Off"))); if(current<1) current=1; /* first item is not selectable */ SetCurrent(Get(current)); Display(); } eOSState cMenuSetupAudioEq::ProcessKey(eKeys Key) { eOSState state = cMenuSetupPage::ProcessKey(Key); Key = NORMALKEY(Key); if(Key == kLeft || Key == kRight) { m_Dev->ConfigurePostprocessing( xc.deinterlace_method, xc.audio_delay, xc.audio_compression, newconfig.audio_equalizer, xc.audio_surround, xc.speaker_type); } return state; } void cMenuSetupAudioEq::Store(void) { memcpy(&xc, &newconfig, sizeof(config_t)); char tmp[256]; sprintf(tmp,"%d %d %d %d %d %d %d %d %d %d", xc.audio_equalizer[0], xc.audio_equalizer[1], xc.audio_equalizer[2], xc.audio_equalizer[3], xc.audio_equalizer[4], xc.audio_equalizer[5], xc.audio_equalizer[6], xc.audio_equalizer[7], xc.audio_equalizer[8], xc.audio_equalizer[9]); SetupStore("Audio.Equalizer", tmp); Setup.Save(); } //--- cMenuSetupVideo -------------------------------------------------------- static const char * const tvtime_method[] = { "use_vo_driver", "Linear", "LinearBlend", "Greedy", "Greedy2Frame", "Weave", "LineDoubler", "Vertical", "ScalerBob", "GreedyH", "TomsMoComp", NULL}; static const int tvtime_methods_count = (sizeof(tvtime_method)/sizeof(tvtime_method[0]) - 1); static const char * const tvtime_method_name[] = {trNOOP("Use Video-Out Driver"), // "use_vo_driver" "Linear Interpolation", // "Linear", "Linear Blend (mplayer)", // "LinearBlend", "Greedy - Low motion (DScaler)", // "Greedy", "Greedy 2-frame (DScaler)", // "Greedy2Frame", "Weave Last Field", // "Weave", "Line Doubler", // "LineDoubler", "Vertical Blend (ffmpeg)", // "Vertical", "Scaler Bob", // "ScalerBob", "Greedy - High Motion (DScaler)", // "GreedyH", "Tom's Motion Compensated (DScaler)", // "TomsMoComp", NULL}; static const char * const tvtime_pulldown[] = { "none", "vector", NULL}; static const char * const tvtime_pulldown_name[] = { trNOOP("none"), trNOOP("vector"), NULL}; static const char * const tvtime_framerate[] = { "full", "half_top", "half_bottom", NULL}; static const char * const tvtime_framerate_name[] = { trNOOP("full"), trNOOP("half (top)"), trNOOP("half (bottom)"), NULL}; struct tvtime_s { int method; int cheap_mode; // on/off int pulldown; // none, vector int framerate; // full, half_top, half_bottom int judder_correction; // on/off int use_progressive_frame_flag; // on/off int chroma_filter; // on/off void Parse(const char *str) { cheap_mode = strstr(str, "cheap_mode=1") ? 1 : 0; pulldown = strstr(str, "pulldown=none") ? 0 : strstr(str, "pulldown=0") ? 0 : 1; framerate = strstr(str, "framerate_mode=half_top") ? 1 : strstr(str, "framerate_mode=1") ? 1 : strstr(str, "framerate_mode=half_bottom") ? 2 : strstr(str, "framerate_mode=2") ? 2 : 0; chroma_filter = strstr(str, "chroma_filter=1") ? 1 : 0; judder_correction = strstr(str, "judder_correction=0") ? 0 : 1; use_progressive_frame_flag = strstr(str, "use_progressive_frame_flag=0") ? 0 : 1; method=1; const char *m = strstr(str, "method="); if(m) { char *tmp = strdup(m + 7); if(strchr(tmp, ',')) *strchr(tmp, ',') = 0; method = strstra(tmp, tvtime_method, 1); free(tmp); } } const char *ToString(void) { static char buf[256]; snprintf(buf, sizeof(buf), "method=%s,cheap_mode=%d,pulldown=%s,framerate_mode=%s," "judder_correction=%d,use_progressive_frame_flag=%d," "chroma_filter=%d,enabled=1", tvtime_method[method], cheap_mode, tvtime_pulldown[pulldown], tvtime_framerate[framerate], judder_correction, use_progressive_frame_flag, chroma_filter); buf[sizeof(buf)-1] = 0; return buf; } }; class cMenuSetupVideo : public cMenuSetupPage { private: cXinelibDevice *m_Dev; config_t newconfig; cOsdItem *ctrl_autocrop; cOsdItem *ctrl_autocrop_autodetect; cOsdItem *ctrl_autocrop_autodetect_rate; cOsdItem *ctrl_autocrop_soft; cOsdItem *ctrl_autocrop_soft_start_step; cOsdItem *ctrl_autocrop_fixedsize; cOsdItem *ctrl_autocrop_stabilize_time; cOsdItem *ctrl_autocrop_subs; cOsdItem *ctrl_autocrop_subs_detect_lifetime; cOsdItem *ctrl_autocrop_subs_detect_stabilize_time; cOsdItem *ctrl_autocrop_logo_width; cOsdItem *ctrl_autocrop_use_driver_crop; cOsdItem *ctrl_autocrop_use_avards_analysis; cOsdItem *ctrl_autocrop_overscan_compensate; cOsdItem *ctrl_autocrop_bar_tone_tolerance; cOsdItem *ctrl_swscale; cOsdItem *ctrl_swscale_resize; cOsdItem *ctrl_swscale_aspect; cOsdItem *ctrl_swscale_width; cOsdItem *ctrl_swscale_height; cOsdItem *ctrl_hue; cOsdItem *ctrl_saturation; cOsdItem *ctrl_contrast; cOsdItem *ctrl_brightness; cOsdItem *ctrl_sharpness; cOsdItem *ctrl_noise_reduction; cOsdItem *ctrl_overscan; cOsdItem *ctrl_pp; cOsdItem *ctrl_deinterlace; cOsdItem *ctrl_tvtime_method; cOsdItem *ctrl_unsharp; cOsdItem *ctrl_denoise3d; cOsdItem *ctrl_vo_aspect_ratio; int deinterlace; struct tvtime_s tvtime; protected: virtual void Store(void); void Set(void); public: cMenuSetupVideo(cXinelibDevice *Dev); ~cMenuSetupVideo(void); virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupVideo::cMenuSetupVideo(cXinelibDevice *Dev) { m_Dev = Dev; memcpy(&newconfig, &xc, sizeof(config_t)); newconfig.hue = CONTROL_TO_INDEX(newconfig.hue); newconfig.saturation = CONTROL_TO_INDEX(newconfig.saturation); newconfig.contrast = CONTROL_TO_INDEX(newconfig.contrast); newconfig.brightness = CONTROL_TO_INDEX(newconfig.brightness); newconfig.sharpness = CONTROL_TO_INDEX(newconfig.sharpness); newconfig.noise_reduction = CONTROL_TO_INDEX(newconfig.noise_reduction); deinterlace = strstra(xc.deinterlace_method, xc.s_deinterlaceMethods, 0); tvtime.Parse(newconfig.deinterlace_opts); Set(); } cMenuSetupVideo::~cMenuSetupVideo(void) { m_Dev->ConfigureVideo(xc.hue, xc.saturation, xc.brightness, xc.sharpness, xc.noise_reduction, xc.contrast, xc.overscan, xc.vo_aspect_ratio); m_Dev->ConfigurePostprocessing("autocrop", !!xc.autocrop, xc.AutocropOptions()); m_Dev->ConfigurePostprocessing("swscale", !!xc.swscale, xc.SwScaleOptions()); m_Dev->ConfigurePostprocessing("pp", !!xc.ffmpeg_pp, xc.FfmpegPpOptions()); m_Dev->ConfigurePostprocessing("unsharp", !!xc.unsharp, xc.UnsharpOptions()); m_Dev->ConfigurePostprocessing("denoise3d", !!xc.denoise3d, xc.Denoise3dOptions()); m_Dev->ConfigurePostprocessing( xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); } void cMenuSetupVideo::Set(void) { SetPlugin(cPluginManager::GetPlugin(PLUGIN_NAME_I18N)); int current = Current(); Clear(); Add(SeparatorItem(tr("Video"))); Add(ctrl_vo_aspect_ratio = new cMenuEditStraI18nItem(tr("Aspect ratio"), &newconfig.vo_aspect_ratio, VO_ASPECT_count, xc.s_vo_aspects)); Add(ctrl_autocrop = new cMenuEditBoolItem(tr("Crop letterbox 4:3 to 16:9"), &newconfig.autocrop)); if(newconfig.autocrop) { Add(ctrl_autocrop_use_driver_crop = new cMenuEditBoolItem(indent(tr("Use driver crop")), &newconfig.autocrop_use_driver_crop)); Add(ctrl_autocrop_autodetect = new cMenuEditBoolItem(indent(tr("Autodetect letterbox")), &newconfig.autocrop_autodetect)); if(newconfig.autocrop_autodetect) { Add(ctrl_autocrop_fixedsize = new cMenuEditBoolItem(indent(tr("Crop to")), &newconfig.autocrop_fixedsize, "4:3...20:9", "14:9/16:9")); Add(ctrl_autocrop_autodetect_rate = new cMenuEditIntItem(indent(tr("Autodetect rate")), &newconfig.autocrop_autodetect_rate, 1, 30)); Add(ctrl_autocrop_stabilize_time = new cMenuEditIntItem(indent(tr("Stabilize time")), &newconfig.autocrop_stabilize_time, 1, 9999)); Add(ctrl_autocrop_logo_width = new cMenuEditIntItem(indent(tr("Maximum logo width [%]")), &newconfig.autocrop_logo_width, 0, 99)); Add(ctrl_autocrop_overscan_compensate = new cMenuEditIntItem(indent(tr("Overscan compensate [%1000]")), &newconfig.autocrop_overscan_compensate, 0, 9999)); Add(ctrl_autocrop_soft = new cMenuEditBoolItem(indent(tr("Soft start")), &newconfig.autocrop_soft)); if(newconfig.autocrop_soft) { Add(ctrl_autocrop_soft_start_step = new cMenuEditIntItem(indent(tr("Soft start step")), &newconfig.autocrop_soft_start_step, 1, 999)); } Add(ctrl_autocrop_subs = new cMenuEditBoolItem(indent(tr("Detect subtitles")), &newconfig.autocrop_subs)); if(newconfig.autocrop_subs) { Add(ctrl_autocrop_subs_detect_stabilize_time = new cMenuEditIntItem(indent(tr("Subs detect stabilize time")), &newconfig.autocrop_subs_detect_stabilize_time, 0, 9999)); Add(ctrl_autocrop_subs_detect_lifetime = new cMenuEditIntItem(indent(tr("Subs detect lifetime")), &newconfig.autocrop_subs_detect_lifetime, 0, 9999)); } Add(ctrl_autocrop_use_avards_analysis = new cMenuEditBoolItem(indent(tr("Use avards analysis")), &newconfig.autocrop_use_avards_analysis)); if (newconfig.autocrop_use_avards_analysis) { Add(ctrl_autocrop_bar_tone_tolerance = new cMenuEditIntItem(indent(tr("Bar tone tolerance")), &newconfig.autocrop_bar_tone_tolerance, 0, 255)); } } } ctrl_swscale_resize = ctrl_swscale_aspect = ctrl_swscale_width = ctrl_swscale_height = NULL; Add(ctrl_swscale = new cMenuEditBoolItem(tr("Software scaling"), &newconfig.swscale)); if(newconfig.swscale) { Add(ctrl_swscale_aspect = new cMenuEditBoolItem(indent(tr("Change aspect ratio")), &newconfig.swscale_change_aspect)); Add(ctrl_swscale_resize = new cMenuEditBoolItem(indent(tr("Change video size")), &newconfig.swscale_resize)); if(newconfig.swscale_resize) { Add(ctrl_swscale_width = new cMenuEditIntItem( indent(tr("Width")), &newconfig.swscale_width, 360, 2000)); Add(ctrl_swscale_height = new cMenuEditIntItem( indent(tr("Height")), &newconfig.swscale_height, 288, 1200)); Add(new cMenuEditBoolItem(indent(tr("Allow downscaling")), &newconfig.swscale_downscale)); } } Add(ctrl_overscan = new cMenuEditTypedIntItem(tr("Overscan (crop image borders)"), "%", &newconfig.overscan, 0, 10, tr("Off"))); Add(ctrl_pp = new cMenuEditBoolItem(tr("Post processing (ffmpeg)"), &newconfig.ffmpeg_pp)); if(newconfig.ffmpeg_pp) { Add(new cMenuEditIntItem( indent(tr("Quality")), &newconfig.ffmpeg_pp_quality, 0, 6)); Add(new cMenuEditStrItem( indent(tr("Mode")), newconfig.ffmpeg_pp_mode, 255, OptionsChars)); } Add(ctrl_deinterlace = new cMenuEditStraI18nItem(tr("Deinterlacing"), &deinterlace, DEINTERLACE_count, xc.s_deinterlaceMethodNames)); ctrl_tvtime_method = NULL; if(deinterlace == DEINTERLACE_TVTIME) { Add(ctrl_tvtime_method = new cMenuEditStraI18nItem(indent(tr("Method")), &tvtime.method, tvtime_methods_count, tvtime_method_name)); Add(new cMenuEditBoolItem( indent(tr("Cheap mode")), &tvtime.cheap_mode)); Add(new cMenuEditStraI18nItem(indent(tr("Pulldown")), &tvtime.pulldown, 2, tvtime_pulldown_name)); Add(new cMenuEditStraI18nItem(indent(tr("Frame rate")), &tvtime.framerate, 3, tvtime_framerate_name)); Add(new cMenuEditBoolItem( indent(tr("Judder Correction")), &tvtime.judder_correction)); Add(new cMenuEditBoolItem( indent(tr("Use progressive frame flag")), &tvtime.use_progressive_frame_flag)); Add(new cMenuEditBoolItem( indent(tr("Chroma Filter")), &tvtime.chroma_filter)); } Add(ctrl_unsharp = new cMenuEditBoolItem(tr("Sharpen / Blur"), &newconfig.unsharp)); if(newconfig.unsharp) { Add(new cMenuEditOddIntItem( indent(tr("Width of the luma matrix")), &newconfig.unsharp_luma_matrix_width, 3, 11)); Add(new cMenuEditOddIntItem( indent(tr("Height of the luma matrix")), &newconfig.unsharp_luma_matrix_height, 3, 11)); Add(new cMenuEditFpIntItem( indent(tr("Amount of luma sharpness/blur")), &newconfig.unsharp_luma_amount, -20, 20, 1, tr("Off"))); Add(new cMenuEditOddIntItem( indent(tr("Width of the chroma matrix")), &newconfig.unsharp_chroma_matrix_width, 3, 11)); Add(new cMenuEditOddIntItem( indent(tr("Height of the chroma matrix")), &newconfig.unsharp_chroma_matrix_height, 3, 11)); Add(new cMenuEditFpIntItem( indent(tr("Amount of chroma sharpness/blur")), &newconfig.unsharp_chroma_amount, -20, 20, 1, tr("Off"))); } Add(ctrl_denoise3d = new cMenuEditBoolItem(tr("3D Denoiser"), &newconfig.denoise3d)); if(newconfig.denoise3d) { Add(new cMenuEditFpIntItem( indent(tr("Spatial luma strength")), &newconfig.denoise3d_luma, 0, 100, 1)); Add(new cMenuEditFpIntItem( indent(tr("Spatial chroma strength")), &newconfig.denoise3d_chroma, 0, 100, 1)); Add(new cMenuEditFpIntItem( indent(tr("Temporal strength")), &newconfig.denoise3d_time, 0, 100, 1)); } #ifdef INTEGER_CONFIG_VIDEO_CONTROLS Add(new cMenuEditIntItem(tr("HUE"), &newconfig.hue, -1, 0xffff)); Add(new cMenuEditIntItem(tr("Saturation"), &newconfig.saturation,-1,0xffff)); Add(new cMenuEditIntItem(tr("Contrast"), &newconfig.contrast, -1, 0xffff)); Add(new cMenuEditIntItem(tr("Brightness"), &newconfig.brightness,-1,0xffff)); #ifdef HAVE_VDPAU Add(new cMenuEditIntItem(tr("Sharpness"), &newconfig.sharpness, -1,0xffff)); Add(new cMenuEditIntItem(tr("Noise Reduction"), &newconfig.noise_reduction, -1,0xffff)); #endif #else Add(ctrl_hue = new cMenuEditStraItem(tr("HUE"), &newconfig.hue, 33, controls)); Add(ctrl_saturation = new cMenuEditStraItem(tr("Saturation"), &newconfig.saturation, 33, controls)); Add(ctrl_contrast = new cMenuEditStraItem(tr("Contrast"), &newconfig.contrast, 33, controls)); Add(ctrl_brightness = new cMenuEditStraItem(tr("Brightness"), &newconfig.brightness, 33, controls)); #ifdef HAVE_VDPAU Add(ctrl_sharpness = new cMenuEditStraItem(tr("Sharpness"), &newconfig.sharpness, 33, controls)); Add(ctrl_noise_reduction = new cMenuEditStraItem(tr("Noise Reduction"), &newconfig.noise_reduction, 33, controls)); #endif #endif #ifdef DEVICE_SUPPORTS_IBP_TRICKSPEED Add(new cMenuEditBoolItem(tr("Smooth fast forward"), &newconfig.ibp_trickspeed)); #endif Add(new cMenuEditIntItem(tr("Fastest trick speed"), &newconfig.max_trickspeed, 1, 12)); if(current<1) current=1; /* first item is not selectable */ SetCurrent(Get(current)); //SetCurrent(Get(1)); Display(); } eOSState cMenuSetupVideo::ProcessKey(eKeys Key) { cOsdItem *item = Get(Current()); eOSState state = cMenuSetupPage::ProcessKey(Key); Key = NORMALKEY(Key); if(Key!=kLeft && Key!=kRight) return state; if(item == ctrl_hue || item == ctrl_saturation || item == ctrl_sharpness || item == ctrl_noise_reduction || item == ctrl_contrast || item == ctrl_brightness || item == ctrl_overscan || item == ctrl_vo_aspect_ratio) #ifdef INTEGER_CONFIG_VIDEO_CONTROLS m_Dev->ConfigureVideo(newconfig.hue, newconfig.saturation, newconfig.brightness, newconfig.sharpness, newconfig.noise_reduction, newconfig.contrast, newconfig.overscan, newconfig.vo_aspect_ratio); #else m_Dev->ConfigureVideo( INDEX_TO_CONTROL(newconfig.hue), INDEX_TO_CONTROL(newconfig.saturation), INDEX_TO_CONTROL(newconfig.brightness), INDEX_TO_CONTROL(newconfig.sharpness), INDEX_TO_CONTROL(newconfig.noise_reduction), INDEX_TO_CONTROL(newconfig.contrast), newconfig.overscan, newconfig.vo_aspect_ratio); #endif else if(item == ctrl_autocrop || item == ctrl_autocrop_autodetect || item == ctrl_autocrop_autodetect_rate || item == ctrl_autocrop_soft || item == ctrl_autocrop_soft_start_step || item == ctrl_autocrop_fixedsize || item == ctrl_autocrop_stabilize_time || item == ctrl_autocrop_subs || item == ctrl_autocrop_subs_detect_lifetime || item == ctrl_autocrop_subs_detect_stabilize_time || item == ctrl_autocrop_logo_width || item == ctrl_autocrop_use_driver_crop || item == ctrl_autocrop_use_avards_analysis || item == ctrl_autocrop_overscan_compensate || item == ctrl_autocrop_bar_tone_tolerance) { m_Dev->ConfigurePostprocessing("autocrop", !!newconfig.autocrop, newconfig.AutocropOptions()); Set(); } else if(item == ctrl_swscale || item == ctrl_swscale_resize || item == ctrl_swscale_aspect || item == ctrl_swscale_width || item == ctrl_swscale_height) { m_Dev->ConfigurePostprocessing("swscale", !!newconfig.swscale, newconfig.SwScaleOptions()); Set(); } else if(item == ctrl_pp) { m_Dev->ConfigurePostprocessing("pp", !!newconfig.ffmpeg_pp, newconfig.FfmpegPpOptions()); Set(); } else if(item == ctrl_unsharp) { m_Dev->ConfigurePostprocessing("unsharp", !!newconfig.unsharp, newconfig.UnsharpOptions()); Set(); } else if(item == ctrl_denoise3d) { m_Dev->ConfigurePostprocessing("denoise3d", !!newconfig.denoise3d, newconfig.Denoise3dOptions()); Set(); } else if(item == ctrl_deinterlace) { if(deinterlace == DEINTERLACE_TVTIME && !ctrl_tvtime_method) { Set(); } else if(deinterlace != DEINTERLACE_TVTIME && ctrl_tvtime_method) { Set(); } } return state; } void cMenuSetupVideo::Store(void) { memcpy(&xc, &newconfig, sizeof(config_t)); #ifdef INTEGER_CONFIG_VIDEO_CONTROLS #else xc.hue = INDEX_TO_CONTROL(xc.hue); xc.saturation = INDEX_TO_CONTROL(xc.saturation); xc.contrast = INDEX_TO_CONTROL(xc.contrast); xc.brightness = INDEX_TO_CONTROL(xc.brightness); xc.sharpness = INDEX_TO_CONTROL(xc.sharpness); xc.noise_reduction = INDEX_TO_CONTROL(xc.noise_reduction); #endif strn0cpy(xc.deinterlace_method, xc.s_deinterlaceMethods[deinterlace], sizeof(xc.deinterlace_method)); strn0cpy(xc.deinterlace_opts, tvtime.ToString(), sizeof(xc.deinterlace_opts)); SetupStore("Video.Deinterlace", xc.deinterlace_method); SetupStore("Video.DeinterlaceOptions", xc.deinterlace_opts); SetupStore("Video.AutoCrop", xc.autocrop); SetupStore("Video.AutoCrop.AutoDetect", xc.autocrop_autodetect); SetupStore("Video.AutoCrop.AutoDetectRate", xc.autocrop_autodetect_rate); SetupStore("Video.AutoCrop.SoftStart", xc.autocrop_soft); SetupStore("Video.AutoCrop.SoftStartStep", xc.autocrop_soft_start_step); SetupStore("Video.AutoCrop.FixedSize", xc.autocrop_fixedsize); SetupStore("Video.AutoCrop.StabilizeTime", xc.autocrop_stabilize_time); SetupStore("Video.AutoCrop.DetectSubs", xc.autocrop_subs); SetupStore("Video.AutoCrop.SubsDetectLifetime", xc.autocrop_subs_detect_lifetime); SetupStore("Video.AutoCrop.SubsDetectStabilizeTime", xc.autocrop_subs_detect_stabilize_time); SetupStore("Video.AutoCrop.LogoWidth", xc.autocrop_logo_width); SetupStore("Video.AutoCrop.UseDriverCrop", xc.autocrop_use_driver_crop); SetupStore("Video.AutoCrop.UseAvardsAnalysis", xc.autocrop_use_avards_analysis); SetupStore("Video.AutoCrop.OverscanCompensate", xc.autocrop_overscan_compensate); SetupStore("Video.AutoCrop.BarToneTolerance", xc.autocrop_bar_tone_tolerance); SetupStore("Video.SwScale", xc.swscale); SetupStore("Video.SwScale.Aspect", xc.swscale_change_aspect); SetupStore("Video.SwScale.Resize", xc.swscale_resize); SetupStore("Video.SwScale.Width", xc.swscale_width); SetupStore("Video.SwScale.Height", xc.swscale_height); SetupStore("Video.SwScale.Downscale", xc.swscale_downscale); SetupStore("Video.HUE", xc.hue); SetupStore("Video.Saturation", xc.saturation); SetupStore("Video.Contrast", xc.contrast); SetupStore("Video.Brightness", xc.brightness); SetupStore("Video.Sharpness", xc.sharpness); SetupStore("Video.NoiseReduction", xc.noise_reduction); SetupStore("Video.Overscan", xc.overscan); SetupStore("Video.IBPTrickSpeed", xc.ibp_trickspeed); SetupStore("Video.MaxTrickSpeed", xc.max_trickspeed); SetupStore("Video.AspectRatio", xc.vo_aspect_ratio); SetupStore("Post.pp.Enable", xc.ffmpeg_pp); SetupStore("Post.pp.Quality", xc.ffmpeg_pp_quality); SetupStore("Post.pp.Mode", xc.ffmpeg_pp_mode); SetupStore("Post.unsharp.Enable", xc.unsharp); SetupStore("Post.unsharp.luma_matrix_width", xc.unsharp_luma_matrix_width); SetupStore("Post.unsharp.luma_matrix_height", xc.unsharp_luma_matrix_height); SetupStore("Post.unsharp.luma_amount", xc.unsharp_luma_amount); SetupStore("Post.unsharp.chroma_matrix_width", xc.unsharp_chroma_matrix_width); SetupStore("Post.unsharp.chroma_matrix_height", xc.unsharp_chroma_matrix_height); SetupStore("Post.unsharp.chroma_amount", xc.unsharp_chroma_amount); SetupStore("Post.denoise3d.Enable", xc.denoise3d); SetupStore("Post.denoise3d.luma", xc.denoise3d_luma); SetupStore("Post.denoise3d.chroma", xc.denoise3d_chroma); SetupStore("Post.denoise3d.time", xc.denoise3d_time); SetupStore("Video.Decoder.MPEG2", xc.s_decoders_MPEG2[xc.decoder_mpeg2]); SetupStore("Video.Decoder.H264", xc.s_decoders_H264[xc.decoder_h264]); #if 1 // delete old keys (<1.0.0) SetupStore("Video.AutoScale"); #endif Setup.Save(); } //--- cMenuSetupOSD ---------------------------------------------------------- class cMenuSetupOSD : public cMenuSetupPage { private: cXinelibDevice *m_Dev; config_t newconfig; int orig_alpha_correction; int orig_alpha_correction_abs; cOsdItem *ctrl_size; cOsdItem *ctrl_width; cOsdItem *ctrl_scaling; cOsdItem *ctrl_alpha; cOsdItem *ctrl_alpha_abs; cOsdItem *ctrl_blending; cOsdItem *ctrl_lowres; protected: virtual void Store(void); void Set(void); public: cMenuSetupOSD(cXinelibDevice *Dev); ~cMenuSetupOSD(); virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupOSD::cMenuSetupOSD(cXinelibDevice *Dev) { m_Dev = Dev; memcpy(&newconfig, &xc, sizeof(config_t)); orig_alpha_correction = xc.alpha_correction; orig_alpha_correction_abs = xc.alpha_correction_abs; newconfig.extsub_size++; Set(); } cMenuSetupOSD::~cMenuSetupOSD() { xc.alpha_correction = orig_alpha_correction; xc.alpha_correction_abs = orig_alpha_correction_abs; } void cMenuSetupOSD::Set(void) { SetPlugin(cPluginManager::GetPlugin(PLUGIN_NAME_I18N)); int current = Current(); Clear(); ctrl_size = NULL; ctrl_width = NULL; ctrl_scaling = NULL; ctrl_blending = NULL; ctrl_lowres = NULL; ctrl_alpha = NULL; ctrl_alpha_abs = NULL; Add(SeparatorItem(tr("On-Screen Display"))); Add(new cMenuEditBoolItem(tr("Hide main menu"), &newconfig.hide_main_menu)); Add(ctrl_size = new cMenuEditStraI18nItem(tr("Resolution"), &newconfig.osd_size, OSD_SIZE_count, xc.s_osdSizes)); if (newconfig.osd_size == OSD_SIZE_custom) { Add(ctrl_width = new cMenuEditTypedIntItem(indent(tr("Width")), "px", &newconfig.osd_width, 480, 2048)); Add(new cMenuEditTypedIntItem(indent(tr("Height")), "px", &newconfig.osd_height, 576, 1200)); } #if VDRVERSNUM >= 10717 Add(new cMenuEditStraI18nItem(tr("Color depth"), &newconfig.osd_color_depth, OSD_DEPTH_count, xc.s_osdColorDepths)); #endif Add(ctrl_blending = new cMenuEditBoolItem(tr("Blending method"), &newconfig.osd_blending, tr(xc.s_osdBlendingMethods[OSD_BLENDING_SOFTWARE]), tr(xc.s_osdBlendingMethods[OSD_BLENDING_HARDWARE]))); if(newconfig.osd_blending == OSD_BLENDING_SOFTWARE) { Add(ctrl_lowres = new cMenuEditBoolItem(indent(tr("Use hardware for low-res video")), &newconfig.osd_blending_lowresvideo)); } Add(ctrl_scaling = new cMenuEditStraI18nItem(tr("Scaling method"), &newconfig.osd_scaling, OSD_SCALING_count, xc.s_osdScalings)); Add(new cMenuEditBoolItem(tr("Scale subtitles"), &newconfig.osd_spu_scaling)); Add(new cMenuEditStraI18nItem(tr("Show all layers"), &newconfig.osd_mixer, OSD_MIXER_count, xc.s_osdMixers)); Add(ctrl_alpha = new cMenuEditTypedIntItem(tr("Dynamic transparency correction"), "%", &newconfig.alpha_correction, -200, 200, tr("Off"))); Add(ctrl_alpha_abs = new cMenuEditTypedIntItem(tr("Static transparency correction"), "", &newconfig.alpha_correction_abs, -0xff, 0xff, tr("Off"))); Add(new cMenuEditStraI18nItem(tr("External subtitle size"), &newconfig.extsub_size, SUBTITLESIZE_count, xc.s_subtitleSizes)); Add(new cMenuEditBoolItem(tr("DVB subtitle decoder"), &newconfig.dvb_subtitles, "VDR", "frontend")); if(current<1) current=1; /* first item is not selectable */ SetCurrent(Get(current)); //SetCurrent(Get(1)); Display(); } eOSState cMenuSetupOSD::ProcessKey(eKeys Key) { cOsdItem *item = Get(Current()); eOSState state = cMenuSetupPage::ProcessKey(Key); Key = NORMALKEY(Key); if(Key!=kLeft && Key!=kRight) return state; if(item == ctrl_scaling) cXinelibOsdProvider::RefreshOsd(); else if(item == ctrl_alpha) xc.alpha_correction = newconfig.alpha_correction; else if(item == ctrl_alpha_abs) xc.alpha_correction_abs = newconfig.alpha_correction_abs; if (newconfig.osd_size == OSD_SIZE_custom && !ctrl_width) Set(); if (newconfig.osd_size != OSD_SIZE_custom && ctrl_width) Set(); if(newconfig.osd_blending==OSD_BLENDING_SOFTWARE && !ctrl_lowres) Set(); if(newconfig.osd_blending!=OSD_BLENDING_SOFTWARE && ctrl_lowres) Set(); return state; } void cMenuSetupOSD::Store(void) { newconfig.extsub_size --; if(newconfig.extsub_size != xc.extsub_size) { cString tmp = cString::sprintf("EXTSUBSIZE %d", newconfig.extsub_size); m_Dev->PlayFileCtrl(tmp); } memcpy(&xc, &newconfig, sizeof(config_t)); orig_alpha_correction = xc.alpha_correction; orig_alpha_correction_abs = xc.alpha_correction_abs; SetupStore("OSD.Size", xc.s_osdSizes[xc.osd_size]); SetupStore("OSD.Width", xc.osd_width); SetupStore("OSD.Height", xc.osd_height); SetupStore("OSD.ColorDepth", xc.s_osdColorDepths[xc.osd_color_depth]); SetupStore("OSD.Scaling", xc.osd_scaling); SetupStore("OSD.ScalingSPU", xc.osd_spu_scaling); SetupStore("OSD.HideMainMenu", xc.hide_main_menu); SetupStore("OSD.LayersVisible", xc.osd_mixer); SetupStore("OSD.Blending", xc.osd_blending); SetupStore("OSD.BlendingLowRes", xc.osd_blending_lowresvideo); #if 1 // Delete old keys (<=1.0.0) SetupStore("OSD.UnscaledAlways"); SetupStore("OSD.UnscaledLowRes"); SetupStore("OSD.UnscaledOpaque"); SetupStore("OSD.Prescale"); SetupStore("OSD.Downscale"); #endif SetupStore("OSD.AlphaCorrection", xc.alpha_correction); SetupStore("OSD.AlphaCorrectionAbs", xc.alpha_correction_abs); SetupStore("OSD.ExtSubSize", xc.extsub_size); SetupStore("OSD.DvbSubtitles", xc.dvb_subtitles); Setup.Save(); } //--- cMenuSetupDecoder ------------------------------------------------------ class cMenuSetupDecoder : public cMenuSetupPage { private: cXinelibDevice *m_Dev; config_t newconfig; int pes_buffers_ind; cOsdItem *ctrl_pes_buffers_ind; cOsdItem *ctrl_pes_buffers; protected: virtual void Store(void); void Set(void); public: cMenuSetupDecoder(cXinelibDevice *Dev); virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupDecoder::cMenuSetupDecoder(cXinelibDevice *Dev) { int i; m_Dev = Dev; memcpy(&newconfig, &xc, sizeof(config_t)); pes_buffers_ind = PES_BUFFERS_CUSTOM; for(i=0;xc.s_bufferSize[i];i++) if(xc.pes_buffers == xc.i_pesBufferSize[i]) pes_buffers_ind = i; Set(); } void cMenuSetupDecoder::Set(void) { SetPlugin(cPluginManager::GetPlugin(PLUGIN_NAME_I18N)); int current = Current(); Clear(); Add(SeparatorItem(tr("Decoder"))); Add(ctrl_pes_buffers_ind = new cMenuEditStraI18nItem(tr("Buffer size"), &pes_buffers_ind, PES_BUFFERS_count, xc.s_bufferSize)); if(pes_buffers_ind == PES_BUFFERS_CUSTOM) Add(ctrl_pes_buffers = new cMenuEditIntItem(indent(tr("Number of PES packets")), &newconfig.pes_buffers, 10, 10000)); else ctrl_pes_buffers = NULL; if(current<1) current=1; /* first item is not selectable */ SetCurrent(Get(current)); Display(); } eOSState cMenuSetupDecoder::ProcessKey(eKeys Key) { cOsdItem *item = Get(Current()); eOSState state = cMenuSetupPage::ProcessKey(Key); Key = NORMALKEY(Key); if(Key!=kLeft && Key!=kRight) return state; if(item == ctrl_pes_buffers_ind) { if(pes_buffers_ind == PES_BUFFERS_CUSTOM && !ctrl_pes_buffers) { Set(); } else if(pes_buffers_ind != PES_BUFFERS_CUSTOM && ctrl_pes_buffers) { Set(); } } return state; } void cMenuSetupDecoder::Store(void) { int old_buffers = xc.pes_buffers; //memcpy(&xc, &newconfig, sizeof(config_t)); xc.pes_buffers = newconfig.pes_buffers; if(pes_buffers_ind != PES_BUFFERS_CUSTOM) xc.pes_buffers = xc.i_pesBufferSize[pes_buffers_ind]; SetupStore("Decoder.PesBuffers", xc.pes_buffers); #if 1 // delete old keys (<1.0.0) SetupStore("Decoder.Priority"); SetupStore("Decoder.InactivityTimer"); #endif if(xc.pes_buffers != old_buffers) m_Dev->ConfigureDecoder(xc.pes_buffers); Setup.Save(); } //--- cMenuSetupLocal -------------------------------------------------------- class cMenuSetupLocal : public cMenuSetupPage { private: cXinelibDevice *m_Dev; config_t newconfig; int local_frontend; int local_frontend_orig; int audio_driver; int audio_driver_orig; int video_driver; int video_driver_orig; cOsdItem *ctrl_scale; cOsdItem *ctrl_local_fe; cOsdItem *ctrl_driver; cOsdItem *ctrl_fullscreen; cOsdItem *ctrl_window_width; cOsdItem *ctrl_window_height; cOsdItem *ctrl_interlace_order; cOsdItem *ctrl_aspect; cOsdItem *ctrl_audio_driver; cOsdItem *ctrl_audio_port; protected: virtual void Store(void); void Set(void); public: cMenuSetupLocal(cXinelibDevice *Dev); ~cMenuSetupLocal(void); virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupLocal::cMenuSetupLocal(cXinelibDevice *Dev) { m_Dev = Dev; SetPlugin(cPluginManager::GetPlugin(PLUGIN_NAME_I18N)); memcpy(&newconfig, &xc, sizeof(config_t)); local_frontend_orig = local_frontend = strstra(xc.local_frontend, xc.s_frontends, 0); audio_driver_orig = audio_driver = strstra(xc.audio_driver, xc.s_audioDrivers, 0); video_driver = 0; if(local_frontend == FRONTEND_X11) video_driver = strstra(xc.video_driver, xc.s_videoDriversX11, 0); if(local_frontend == FRONTEND_FB) video_driver = strstra(xc.video_driver, xc.s_videoDriversFB, 0); video_driver_orig = video_driver; Set(); } cMenuSetupLocal::~cMenuSetupLocal(void) { m_Dev->ConfigureWindow( xc.fullscreen, xc.width, xc.height, xc.modeswitch, xc.modeline, xc.display_aspect, xc.scale_video); m_Dev->ConfigurePostprocessing( xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); } void cMenuSetupLocal::Set(void) { int current = Current(); Clear(); ctrl_interlace_order = NULL; ctrl_fullscreen = NULL; ctrl_window_width = NULL; ctrl_window_height = NULL; ctrl_driver = NULL; ctrl_aspect = NULL; ctrl_scale = NULL; ctrl_audio_driver = NULL; ctrl_audio_port = NULL; Add(SeparatorItem(tr("Local Frontend"))); Add(ctrl_local_fe = new cMenuEditStraI18nItem(tr("Local Display Frontend"), &local_frontend, FRONTEND_count, xc.s_frontendNames)); if(local_frontend == FRONTEND_X11) { Add(new cMenuEditBoolItem(tr("Use keyboard"), &newconfig.use_x_keyboard)); } if(local_frontend != FRONTEND_NONE) { Add(SubMenuItem(tr("Decoder"), osUser1)); Add(SeparatorItem(tr("Video"))); } if(local_frontend == FRONTEND_X11) { Add(ctrl_driver = new cMenuEditStraI18nItem(tr("Driver"), &video_driver, X11_DRIVER_count, xc.s_videoDriverNamesX11)); Add(new cMenuEditStrItem(tr("Display address"), newconfig.video_port, 31, DriverNameChars)); } else if(local_frontend == FRONTEND_FB) { Add(ctrl_driver = new cMenuEditStraI18nItem(tr("Driver"), &video_driver, FB_DRIVER_count, xc.s_videoDriverNamesFB)); Add(new cMenuEditStrItem(tr("Framebuffer device"), newconfig.video_port, 31, DriverNameChars)); } #if 0 if(local_frontend == FRONTEND_FB || !newconfig.fullscreen) { Add(new cMenuEditStrItem( "Modeline", newconfig.modeline, 31, ModeLineChars)); Add(new cMenuEditBoolItem("Videomode switching", &xc.modeswitch)); } #endif if(local_frontend == FRONTEND_X11) { Add(ctrl_fullscreen = new cMenuEditBoolItem(tr("Fullscreen mode"), &newconfig.fullscreen)); if(!newconfig.fullscreen) { Add(ctrl_window_width = new cMenuEditTypedIntItem( indent(tr("Window width")), tr("px"), &newconfig.width, 1, 2048)); Add(ctrl_window_height = new cMenuEditTypedIntItem( indent(tr("Window height")), tr("px"), &newconfig.height, 1, 2048)); } } if(local_frontend != FRONTEND_NONE) { Add(ctrl_aspect = new cMenuEditStraI18nItem(tr("Window aspect"), &newconfig.display_aspect, ASPECT_count, xc.s_aspects)); Add(ctrl_scale = new cMenuEditBoolItem(tr("Scale to window size"), &newconfig.scale_video)); Add(SeparatorItem(tr("Audio"))); Add(ctrl_audio_driver = new cMenuEditStraI18nItem(tr("Driver"), &audio_driver, AUDIO_DRIVER_count, xc.s_audioDriverNames)); if(audio_driver != AUDIO_DRIVER_AUTO && audio_driver != AUDIO_DRIVER_NONE) Add(ctrl_audio_port = new cMenuEditStrItem(tr("Port"), newconfig.audio_port, 31, DriverNameChars)); if(audio_driver != AUDIO_DRIVER_NONE) Add(new cMenuEditStraI18nItem(tr("Speakers"), &newconfig.speaker_type, SPEAKERS_count, xc.s_speakerArrangements)); } if(current<1) current=1; /* first item is not selectable */ SetCurrent(Get(current)); Display(); } eOSState cMenuSetupLocal::ProcessKey(eKeys Key) { int prev_frontend = local_frontend; int prev_audio_driver = audio_driver; cOsdItem *item = Get(Current()); eOSState state = cMenuSetupPage::ProcessKey(Key); if(state == osUser1) return AddSubMenu(new XinelibOutputSetupMenu::cMenuSetupDecoder(m_Dev)); Key = NORMALKEY(Key); if((Key!=kLeft && Key!=kRight) || !item) return state; if(item == ctrl_audio_driver) { if(prev_audio_driver != audio_driver) { if(audio_driver == audio_driver_orig) strcpy(newconfig.audio_port, xc.audio_port); else if(audio_driver == AUDIO_DRIVER_ALSA) strcpy(newconfig.audio_port, "default"); else if(audio_driver == AUDIO_DRIVER_OSS) strcpy(newconfig.audio_port, "/dev/dsp"); else strcpy(newconfig.audio_port, ""); Set(); } else if((audio_driver != AUDIO_DRIVER_AUTO && audio_driver != AUDIO_DRIVER_NONE) && !ctrl_audio_port) Set(); else if((audio_driver == AUDIO_DRIVER_AUTO || audio_driver == AUDIO_DRIVER_NONE) && ctrl_audio_port) Set(); } else if(item == ctrl_aspect || item == ctrl_scale || item == ctrl_interlace_order) m_Dev->ConfigureWindow( xc.fullscreen, xc.width, xc.height, xc.modeswitch, xc.modeline, newconfig.display_aspect, newconfig.scale_video); else if(item == ctrl_local_fe && local_frontend != prev_frontend) { if(local_frontend == local_frontend_orig) { video_driver = video_driver_orig; strcpy(newconfig.video_port, xc.video_port); } else if(local_frontend == FRONTEND_FB) strcpy(newconfig.video_port, "/dev/fb/0"); else if(local_frontend == FRONTEND_X11) strcpy(newconfig.video_port, "0.0"); Set(); } else if(item == ctrl_fullscreen) { if(!newconfig.fullscreen && !ctrl_window_width) { Set(); } else if(newconfig.fullscreen && ctrl_window_width) { Set(); } } return state; } void cMenuSetupLocal::Store(void) { int old_buffers = xc.pes_buffers; memcpy(&xc, &newconfig, sizeof(config_t)); xc.pes_buffers = old_buffers; strn0cpy(xc.audio_driver, xc.s_audioDrivers[audio_driver], sizeof(xc.audio_driver)); strn0cpy(xc.local_frontend, xc.s_frontends[local_frontend], sizeof(xc.local_frontend)); if(local_frontend == FRONTEND_X11) strn0cpy(xc.video_driver, xc.s_videoDriversX11[video_driver], sizeof(xc.video_driver)); if(local_frontend == FRONTEND_FB) strn0cpy(xc.video_driver, xc.s_videoDriversFB[video_driver], sizeof(xc.video_driver)); SetupStore("Frontend", xc.local_frontend); SetupStore("Audio.Driver", xc.audio_driver); SetupStore("Audio.Port", xc.audio_port); SetupStore("Audio.Speakers", xc.s_speakerArrangements[xc.speaker_type]); SetupStore("Video.Driver", xc.video_driver); SetupStore("Video.Port", xc.video_port); #if 0 SetupStore("Video.Port", NULL); /* should delete entry ? */ SetupStore("Video.Port.X11",xc.video_port_x11); SetupStore("Video.Port.FB", xc.video_port_fb); #endif SetupStore("Video.Scale", xc.scale_video); SetupStore("Modeline", xc.modeline); SetupStore("VideoModeSwitching", xc.modeswitch); SetupStore("Fullscreen", xc.fullscreen); SetupStore("DisplayAspect", xc.s_aspects[xc.display_aspect]); SetupStore("X11.WindowWidth", xc.width); SetupStore("X11.WindowHeight", xc.height); SetupStore("X11.UseKeyboard", xc.use_x_keyboard); Setup.Save(); } //--- cMenuSetupRemote ------------------------------------------------------- class cMenuSetupRemote : public cMenuSetupPage { private: cXinelibDevice *m_Dev; config_t newconfig; cOsdItem *ctrl_remote_mode; cOsdItem *ctrl_usertp; cOsdItem *ctrl_rtp_addr; cOsdItem *ctrl_use_http; cOsdItem *ctrl_http_ctrl; cOsdItem *ctrl_use_rtsp; cOsdItem *ctrl_rtsp_ctrl; protected: virtual void Store(void); void Set(void); public: cMenuSetupRemote(cXinelibDevice *Dev); virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupRemote::cMenuSetupRemote(cXinelibDevice *Dev) { m_Dev = Dev; memcpy(&newconfig, &xc, sizeof(config_t)); Set(); } void cMenuSetupRemote::Set(void) { int current = Current(); SetPlugin(cPluginManager::GetPlugin(PLUGIN_NAME_I18N)); Clear(); Add(SeparatorItem(tr("Remote Clients"))); Add(ctrl_remote_mode = new cMenuEditBoolItem(tr("Allow remote clients"), &newconfig.remote_mode)); ctrl_usertp = NULL; ctrl_rtp_addr = NULL; ctrl_use_http = NULL; ctrl_use_rtsp = NULL; ctrl_http_ctrl = NULL; ctrl_rtsp_ctrl = NULL; if(newconfig.remote_mode) { Add(new cMenuEditIntItem( indent(tr("Listen port (TCP and broadcast)")), &newconfig.listen_port, 0, 0xffff)); Add(new cMenuEditStrItem( indent(tr("Listen address")), &newconfig.remote_local_ip[0], 16, "0123456789.")); Add(new cMenuEditBoolItem(indent(tr("Remote keyboard")), &newconfig.remote_keyboard)); Add(new cMenuEditIntItem( indent(tr("Max number of clients")), &newconfig.remote_max_clients, 1, MAXCLIENTS)); Add(new cMenuEditBoolItem(indent(tr("PIPE transport")), &newconfig.remote_usepipe)); Add(new cMenuEditBoolItem(indent(tr("TCP transport")), &newconfig.remote_usetcp)); Add(new cMenuEditBoolItem(indent(tr("UDP transport")), &newconfig.remote_useudp)); Add(ctrl_usertp = new cMenuEditBoolItem(indent(tr("RTP (multicast) transport")), &newconfig.remote_usertp)); if(newconfig.remote_usertp) { Add(ctrl_rtp_addr = new cMenuEditStrItem( inden2(tr("Address")), &newconfig.remote_rtp_addr[0], 16, "0123456789.")); Add(new cMenuEditOddIntItem( inden2(tr("Port")), &newconfig.remote_rtp_port, 1000, 0xfffe)); Add(new cMenuEditIntItem( inden2(tr("TTL")), &newconfig.remote_rtp_ttl, 1, 10)); Add(new cMenuEditBoolItem( inden2(tr("Transmit always on")), &newconfig.remote_rtp_always_on)); Add(new cMenuEditBoolItem( inden2(tr("SAP announcements")), &newconfig.remote_rtp_sap)); } Add(new cMenuEditBoolItem(indent(tr("Server announce broadcasts")), &newconfig.remote_usebcast)); Add(new cMenuEditBoolItem(indent(tr("HTTP transport for media files")), &newconfig.remote_http_files)); Add(SeparatorItem(tr("Additional network services"))); Add(ctrl_use_http = new cMenuEditBoolItem(tr("HTTP server"), &newconfig.remote_use_http)); if(newconfig.remote_use_http) Add(ctrl_http_ctrl = new cMenuEditBoolItem(tr("HTTP clients can control VDR"), &newconfig.remote_use_http_ctrl)); Add(ctrl_use_rtsp = new cMenuEditBoolItem(tr("RTSP server"), &newconfig.remote_use_rtsp)); if(newconfig.remote_use_rtsp) Add(ctrl_rtsp_ctrl = new cMenuEditBoolItem(tr("RTSP clients can control VDR"), &newconfig.remote_use_rtsp_ctrl)); } if(current<1) current=1; /* first item is not selectable */ SetCurrent(Get(current)); Display(); } eOSState cMenuSetupRemote::ProcessKey(eKeys Key) { cOsdItem *item = Get(Current()); eOSState state = cMenuSetupPage::ProcessKey(Key); Key = NORMALKEY(Key); if(Key!=kLeft && Key!=kRight) return state; if(item == ctrl_remote_mode) { if(newconfig.remote_mode && !ctrl_usertp) { Set(); } else if(!newconfig.remote_mode && ctrl_usertp) { Set(); } } if(item == ctrl_usertp) { if(newconfig.remote_usertp && !ctrl_rtp_addr) { Set(); } else if(!newconfig.remote_usertp && ctrl_rtp_addr) { Set(); } } if(item == ctrl_use_http) { if(newconfig.remote_use_http && !ctrl_http_ctrl) { Set(); } else if(!newconfig.remote_use_http && ctrl_http_ctrl) { Set(); } } if(item == ctrl_use_rtsp) { if(newconfig.remote_use_rtsp && !ctrl_rtsp_ctrl) { Set(); } else if(!newconfig.remote_use_rtsp && ctrl_rtsp_ctrl) { Set(); } } return state; } void cMenuSetupRemote::Store(void) { memcpy(&xc, &newconfig, sizeof(config_t)); SetupStore("RemoteMode", xc.remote_mode); SetupStore("Remote.ListenPort", xc.listen_port); SetupStore("Remote.Iface", xc.remote_local_if); SetupStore("Remote.LocalIP", xc.remote_local_ip); SetupStore("Remote.Keyboard", xc.remote_keyboard); SetupStore("Remote.MaxClients", xc.remote_max_clients); SetupStore("Remote.UsePipe",xc.remote_usepipe); SetupStore("Remote.UseTcp", xc.remote_usetcp); SetupStore("Remote.UseUdp", xc.remote_useudp); SetupStore("Remote.UseRtp", xc.remote_usertp); SetupStore("Remote.UseBroadcast", xc.remote_usebcast); SetupStore("Remote.UseHttp", xc.remote_http_files); SetupStore("Remote.Rtp.Address", xc.remote_rtp_addr); SetupStore("Remote.Rtp.Port", xc.remote_rtp_port); SetupStore("Remote.Rtp.TTL", xc.remote_rtp_ttl); SetupStore("Remote.Rtp.AlwaysOn", xc.remote_rtp_always_on); SetupStore("Remote.Rtp.SapAnnouncements", xc.remote_rtp_sap); SetupStore("Remote.AllowRtsp", xc.remote_use_rtsp); SetupStore("Remote.AllowRtspCtrl", xc.remote_use_rtsp_ctrl); SetupStore("Remote.AllowHttp", xc.remote_use_http); SetupStore("Remote.AllowHttpCtrl", xc.remote_use_http_ctrl); m_Dev->Listen(xc.remote_mode, xc.listen_port); Setup.Save(); } //--- cMenuSetupMediaPlayer -------------------------------------------------------- class cMenuSetupMediaPlayer : public cMenuSetupPage { private: config_t newconfig; cOsdItem *media_ctrl_playlist_tracknumber; cOsdItem *media_ctrl_playlist_artist; cOsdItem *media_ctrl_playlist_album; cOsdItem *media_ctrl_playlist_cache; cOsdItem *media_ctrl_playlist_id3scanner; protected: virtual void Store(void); void Set(void); public: cMenuSetupMediaPlayer(void); virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupMediaPlayer::cMenuSetupMediaPlayer(void) { memcpy(&newconfig, &xc, sizeof(config_t)); Set(); } void cMenuSetupMediaPlayer::Set(void) { SetPlugin(cPluginManager::GetPlugin(PLUGIN_NAME_I18N)); int current = Current(); Clear(); Add(SeparatorItem(tr("Playlist settings"))); Add(media_ctrl_playlist_tracknumber = new cMenuEditBoolItem(tr("Show the track number"), &newconfig.playlist_tracknumber)); Add(media_ctrl_playlist_artist = new cMenuEditBoolItem(tr("Show the name of the artist"), &newconfig.playlist_artist)); Add(media_ctrl_playlist_album = new cMenuEditBoolItem(tr("Show the name of the album"), &newconfig.playlist_album)); Add(media_ctrl_playlist_id3scanner = new cMenuEditBoolItem(tr("Scan for metainfo"), &newconfig.enable_id3_scanner)); Add(media_ctrl_playlist_cache = new cMenuEditBoolItem(tr("Cache metainfo"), &newconfig.cache_implicit_playlists)); Add(new cMenuEditBoolItem(tr("Arrow keys control DVD playback"), &newconfig.dvd_arrow_keys_control_playback)); Add(new cMenuEditBoolItem(tr("Show hidden files"), &newconfig.show_hidden_files)); Add(new cMenuEditBoolItem(tr("Allow removing files"), &newconfig.media_enable_delete)); Add(new cMenuEditBoolItem(tr("Remember last playback position"), &newconfig.media_enable_resume)); Add(SeparatorItem(tr("Media Player"))); Add(new cMenuEditBitItem(tr("Play file"), &newconfig.media_menu_items, MEDIA_MENU_FILES)); Add(new cMenuEditBitItem(tr("Play music"), &newconfig.media_menu_items, MEDIA_MENU_MUSIC)); Add(new cMenuEditBitItem(tr("View images"), &newconfig.media_menu_items, MEDIA_MENU_IMAGES)); Add(new cMenuEditBitItem(tr("Play DVD disc"), &newconfig.media_menu_items, MEDIA_MENU_DVD)); Add(new cMenuEditBitItem(tr("Play audio CD"), &newconfig.media_menu_items, MEDIA_MENU_CD)); Add(new cMenuEditBitItem(tr("Play BluRay disc"), &newconfig.media_menu_items, MEDIA_MENU_BLURAY)); Add(new cMenuEditBitItem(tr("Video settings"), &newconfig.media_menu_items, MEDIA_MENU_VIDEO_SETUP)); Add(new cMenuEditBitItem(tr("Audio settings"), &newconfig.media_menu_items, MEDIA_MENU_AUDIO_SETUP)); if(current<1) current=1; /* first item is not selectable */ SetCurrent(Get(current)); Display(); } eOSState cMenuSetupMediaPlayer::ProcessKey(eKeys Key) { eOSState state = cMenuSetupPage::ProcessKey(Key); return state; } void cMenuSetupMediaPlayer::Store(void) { memcpy(&xc, &newconfig, sizeof(config_t)); SetupStore("Playlist.Tracknumber", xc.playlist_tracknumber); SetupStore("Playlist.Album", xc.playlist_album); SetupStore("Playlist.Artist", xc.playlist_artist); SetupStore("Media.CacheImplicitPlaylists", xc.cache_implicit_playlists); SetupStore("Media.EnableID3Scanner", xc.enable_id3_scanner); SetupStore("Media.DVD.ArrowKeysControlPlayback", xc.dvd_arrow_keys_control_playback); SetupStore("Media.MenuItems", xc.media_menu_items); SetupStore("Media.ShowHiddenFiles", xc.show_hidden_files); SetupStore("Media.EnableDelete", xc.media_enable_delete); SetupStore("Media.EnableResume", xc.media_enable_resume); Setup.Save(); } } // namespace //--- cMenuTestImages ------------------------------------------------------ #include <vdr/osdbase.h> #define OSD_W (720-2) #define OSD_H (576-2) #define OSD_X (1) #define OSD_Y (1) // // cTestGrayscale // class cTestGrayscale : public cOsdObject { private: cXinelibDevice *m_Dev; cOsd *m_Osd; public: cTestGrayscale(cXinelibDevice *Dev) { m_Dev = Dev; m_Osd = NULL; } virtual ~cTestGrayscale() { delete m_Osd; } virtual void Show(); virtual eOSState ProcessKey(eKeys Key); }; void cTestGrayscale::Show() { tArea areas [] = { { 0, 0, OSD_W/2 - 1, OSD_H - 1, 8}, {OSD_W/2, 0, OSD_W - 1, OSD_H - 1, 8}}; int i; if(!m_Osd) m_Osd = cOsdProvider::NewOsd(OSD_X, OSD_Y, 0); if(m_Osd) { if (m_Osd->CanHandleAreas(areas, sizeof(areas) / sizeof(tArea) ) == oeOk) { m_Osd->SetAreas(areas, sizeof(areas) / sizeof(tArea)); m_Osd->Flush(); // border m_Osd->DrawRectangle(0, 0, OSD_W - 1, OSD_H - 1, 0xff000000); m_Osd->DrawRectangle(1, 1, OSD_W - 2, OSD_H - 2, 0xff000000); // background m_Osd->DrawRectangle(2, 2, 2+103, OSD_H - 3, 0xffffffff); m_Osd->DrawRectangle(OSD_W-2-103, 2, OSD_W-2, OSD_H - 3, 0xff000000); for(i=0; i<0xff; i++) m_Osd->DrawRectangle(2+103+2*i, 2, 2+103+2*(i+1), OSD_H - 3, 0xff000000|(i*0x00010101)/*=(i<<16)|(i<<8)|(i)*/); // line m_Osd->DrawRectangle(1, OSD_H/2-20, OSD_W - 2, OSD_H/2, 0xffffffff); m_Osd->DrawRectangle(1, OSD_H/2+1, OSD_W - 2, OSD_H/2+21, 0xff000000); // Cross for(int x=0; x<OSD_W;x++) { m_Osd->DrawPixel(x, x*OSD_H/OSD_W, 0x00000000); m_Osd->DrawPixel(x, OSD_H - 1 - x*OSD_H/OSD_W, 0x00000000); } // commit m_Osd->Flush(); } } } eOSState cTestGrayscale::ProcessKey(eKeys key) { char s[32]; static int br = xc.brightness; static int co = xc.contrast; eOSState state = cOsdObject::ProcessKey(key); if (state == osUnknown) { switch (key & ~k_Repeat) { case kOk: case kBack: return osEnd; case kRight: br += 0xffff/1024*2; /* fall thru */ case kLeft: br -= 0xffff/1024; sprintf(s, "b %d", br); m_Osd->DrawText(400, 100, s, 0xff000000, 0xffffffff, cFont::GetFont(fontSml)); m_Dev->ConfigureVideo(xc.hue, xc.saturation, br, xc.sharpness, xc.noise_reduction, co, xc.overscan, xc.vo_aspect_ratio); m_Osd->Flush(); return osContinue; case kUp: co += 0xffff/1024*2; /* fall thru */ case kDown: co -= 0xffff/1024; sprintf(s, "c %d", co); m_Osd->DrawText(400, 130, s, 0xff000000, 0xffffffff, cFont::GetFont(fontSml)); m_Dev->ConfigureVideo(xc.hue, xc.saturation, br, xc.sharpness, xc.noise_reduction, co, xc.overscan, xc.vo_aspect_ratio); m_Osd->Flush(); return osContinue; default:; // all other keys - do nothing. } } return state; } // // cTestBitmap // class cTestBitmap : public cOsdObject { private: cOsd *m_Osd; int bpp; public: cTestBitmap(int _bpp = 1) { m_Osd = NULL; if(_bpp<1) _bpp = 1; if(_bpp>6) _bpp = 6; bpp = 1<<_bpp; } virtual ~cTestBitmap() { delete m_Osd; } virtual void Show(); virtual eOSState ProcessKey(eKeys Key); }; void cTestBitmap::Show() { tArea areas [] = {{ 0, 0, OSD_W - 1, OSD_H - 1, 8}}; int x, y, bit = 0; if(!m_Osd) { m_Osd = cOsdProvider::NewOsd(OSD_X, OSD_Y, 0); if(m_Osd) { if (m_Osd->CanHandleAreas(areas, sizeof(areas) / sizeof(tArea) ) == oeOk) { m_Osd->SetAreas(areas, sizeof(areas) / sizeof(tArea)); m_Osd->Flush(); } } } if(m_Osd) { for(x=0; x<OSD_W; x+=bpp) { bit = (x/bpp) & 1; for(y=0; y<OSD_H; y+=bpp) { m_Osd->DrawRectangle(x, y, x+bpp, y+bpp, bit?0xffffffff:0xff000000); bit = !bit; } } // commit m_Osd->Flush(); } } eOSState cTestBitmap::ProcessKey(eKeys key) { eOSState state = cOsdObject::ProcessKey(key); if (state == osUnknown) { switch (key & ~k_Repeat) { case kOk: case kBack: return osEnd; case kRight: bpp = (bpp<64) ? (bpp<<1) : 1; Show(); return osContinue; case kLeft: bpp = (bpp>1) ? (bpp>>1) : 64; Show(); return osContinue; default: break; } } return state; } // // cMenuTestImages // #include <vdr/remote.h> // CallPlugin class cMenuTestImages : public cMenuSetupPage { protected: cXinelibDevice *m_Dev; void Set(void); virtual void Store(void) {}; public: cMenuTestImages(cXinelibDevice *Dev); virtual eOSState ProcessKey(eKeys Key); }; cMenuTestImages::cMenuTestImages(cXinelibDevice *Dev) { m_Dev = Dev; Set(); } void cMenuTestImages::Set(void) { char buf[128]; Clear(); SetHasHotkeys(); Add(new cOsdItem(tr("Grayscale"), osUser1)); snprintf(buf, sizeof(buf), "%s 1bit", tr("Bitmap")); buf[sizeof(buf)-1] = 0; Add(new cOsdItem(buf, osUser2)); snprintf(buf, sizeof(buf), "%s 4bit", tr("Bitmap")); buf[sizeof(buf)-1] = 0; Add(new cOsdItem(buf, osUser3)); Display(); } eOSState cMenuTestImages::ProcessKey(eKeys Key) { eOSState state = cMenuSetupPage::ProcessKey(Key); switch (state) { case osUser1: if(cRemote::CallPlugin("xineliboutput")) xc.pending_menu_action = new cTestGrayscale(m_Dev); return osEnd; case osUser2: if(cRemote::CallPlugin("xineliboutput")) xc.pending_menu_action = new cTestBitmap(1); return osEnd; case osUser3: if(cRemote::CallPlugin("xineliboutput")) xc.pending_menu_action = new cTestBitmap(4); return osEnd; default: ; } return state; } //--- cMenuSetupXinelib ------------------------------------------------------ cMenuSetupXinelib::cMenuSetupXinelib(cXinelibDevice *Dev) { m_Dev = Dev; XinelibOutputSetupMenu::controls[0] = tr("Off"); Set(); } void cMenuSetupXinelib::Set(void) { Clear(); SetHasHotkeys(); Add(new cOsdItem(hk(tr("Audio")), osUser1)); Add(new cOsdItem(hk(tr("Audio Equalizer")),osUser2)); Add(new cOsdItem(hk(tr("Video")), osUser3)); Add(new cOsdItem(hk(tr("OSD")), osUser4)); //Add(new cOsdItem(hk(tr("Decoder")), osUser5)); Add(new cOsdItem(hk(tr("Media Player")), osUser5)); Add(new cOsdItem(hk(tr("Local Frontend")), osUser6)); Add(new cOsdItem(hk(tr("Remote Clients")), osUser7)); Add(new cOsdItem(hk(tr("Test Images")), osUser8)); Display(); } eOSState cMenuSetupXinelib::ProcessKey(eKeys Key) { eOSState state = cMenuSetupPage::ProcessKey(Key); switch (state) { case osUser1: return AddSubMenu(new XinelibOutputSetupMenu::cMenuSetupAudio(m_Dev)); case osUser2: return AddSubMenu(new XinelibOutputSetupMenu::cMenuSetupAudioEq(m_Dev)); case osUser3: return AddSubMenu(new XinelibOutputSetupMenu::cMenuSetupVideo(m_Dev)); case osUser4: return AddSubMenu(new XinelibOutputSetupMenu::cMenuSetupOSD(m_Dev)); case osUser5: return AddSubMenu(new XinelibOutputSetupMenu::cMenuSetupMediaPlayer()); case osUser6: return AddSubMenu(new XinelibOutputSetupMenu::cMenuSetupLocal(m_Dev)); case osUser7: return AddSubMenu(new XinelibOutputSetupMenu::cMenuSetupRemote(m_Dev)); case osUser8: return AddSubMenu(new cMenuTestImages(m_Dev)); default: ; } return state; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/�����������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13061253422�012640� 5����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/zh_TW.po���������������������������������������������������������������������0000644�0001750�0001750�00000030330�13061253422�014232� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2009-09-21 23:32+0800\n" "Last-Translator: NanFeng <nfgx@21cn.com>\n" "Language-Team: Chinese (traditional) <vdr@linuxtv.org>\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "custom" msgstr "自定義" msgid "tiny" msgstr "微小" msgid "small" msgstr "小" msgid "medium" msgstr "中" msgid "large" msgstr "大" msgid "huge" msgstr "巨大" msgid "automatic" msgstr "自動" msgid "default" msgstr "默認" msgid "Pan&Scan" msgstr "全景掃描" msgid "CenterCutOut" msgstr "CenterCutOut" msgid "square" msgstr "廣場" msgid "anamorphic" msgstr "變形" msgid "DVB" msgstr "DVB" msgid "off" msgstr "關" msgid "no audio" msgstr "沒有音頻" msgid "no video" msgstr "沒有視頻" msgid "Off" msgstr "關" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "示波器" msgid "FFT Scope" msgstr "FFT的範圍" msgid "FFT Graph" msgstr "FFT圖像" msgid "Image" msgstr "" msgid "Mono 1.0" msgstr "單聲道1.0" msgid "Stereo 2.0" msgstr "立體聲2.0" msgid "Headphones 2.0" msgstr "耳機2.0" msgid "Stereo 2.1" msgstr "立體聲2.1" msgid "Surround 3.0" msgstr "環繞聲3.0" msgid "Surround 4.0" msgstr "環繞聲4.0" msgid "Surround 4.1" msgstr "環繞聲4.1" msgid "Surround 5.0" msgstr "環繞聲5.0" msgid "Surround 5.1" msgstr "環繞聲5.1" msgid "Surround 6.0" msgstr "環繞聲6.0" msgid "Surround 6.1" msgstr "環繞聲6.1" msgid "Surround 7.1" msgstr "環繞聲7.1" msgid "Pass Through" msgstr "通過" msgid "very large" msgstr "非常大" msgid "Software" msgstr "軟件" msgid "Hardware" msgstr "硬件" msgid "no" msgstr "否" msgid "grayscale" msgstr "灰度" msgid "transparent" msgstr "透明" msgid "transparent grayscale" msgstr "透明灰度" msgid "yes" msgstr "是" msgid "nearest" msgstr "最近" msgid "bilinear" msgstr "雙線性" msgid "LUT8" msgstr "" msgid "TrueColor" msgstr "" msgid "video stream" msgstr "" msgid "none" msgstr "無" msgid "nonref" msgstr "nonref" msgid "bidir" msgstr "雙向口" msgid "nonkey" msgstr "nonkey" msgid "all" msgstr "所有" msgid "Audio" msgstr "音頻" msgid "Volume control" msgstr "音量控制" msgid "Delay" msgstr "延遲" msgid "ms" msgstr "毫秒" msgid "Audio Compression" msgstr "音頻壓縮" msgid "Upmix stereo to 5.1" msgstr "上混立體聲到5.1" msgid "Downmix AC3 to surround" msgstr "縮混的AC3" msgid "Mix to headphones" msgstr "組合,以耳機" msgid "Visualization" msgstr "可視化" #, fuzzy msgid "Width" msgstr "寬度" msgid "px" msgstr "px" #, fuzzy msgid "Height" msgstr "高度" #, fuzzy msgid "Speed" msgstr "速度" msgid "fps" msgstr "幀" msgid "Background image MRL" msgstr "" msgid "Audio Equalizer" msgstr "音頻均衡器" msgid "Use Video-Out Driver" msgstr "使用視頻輸出驅動器" msgid "vector" msgstr "載體" msgid "full" msgstr "全" msgid "half (top)" msgstr "半(上)" msgid "half (bottom)" msgstr "半(下)" msgid "Video" msgstr "視頻" msgid "Aspect ratio" msgstr "畫面比例" msgid "Crop letterbox 4:3 to 16:9" msgstr "選擇4:3或者16:9" msgid "Use driver crop" msgstr "" msgid "Autodetect letterbox" msgstr "" #, fuzzy msgid "Crop to" msgstr "剪裁" msgid "Autodetect rate" msgstr "" msgid "Stabilize time" msgstr "" msgid "Maximum logo width [%]" msgstr "" msgid "Overscan compensate [%1000]" msgstr "" #, fuzzy msgid "Soft start" msgstr "軟啟動" msgid "Soft start step" msgstr "" #, fuzzy msgid "Detect subtitles" msgstr "檢測的字幕" msgid "Subs detect stabilize time" msgstr "" msgid "Subs detect lifetime" msgstr "" msgid "Use avards analysis" msgstr "" msgid "Bar tone tolerance" msgstr "" msgid "Software scaling" msgstr "軟件縮放" #, fuzzy msgid "Change aspect ratio" msgstr "更改寬高比" #, fuzzy msgid "Change video size" msgstr "改變視頻大小" #, fuzzy msgid "Allow downscaling" msgstr "允許縮小尺度" msgid "Overscan (crop image borders)" msgstr "過掃描(去掉圖像邊框)" msgid "Post processing (ffmpeg)" msgstr "後處理(ffmpeg)" #, fuzzy msgid "Quality" msgstr "Quality" #, fuzzy msgid "Mode" msgstr "Mode" msgid "Deinterlacing" msgstr "去隔行" #, fuzzy msgid "Method" msgstr "Method" #, fuzzy msgid "Cheap mode" msgstr "Cheap mode" #, fuzzy msgid "Pulldown" msgstr "Pulldown" #, fuzzy msgid "Frame rate" msgstr "Frame rate" #, fuzzy msgid "Judder Correction" msgstr "Judder Correction" #, fuzzy msgid "Use progressive frame flag" msgstr "Use progressive frame flag" #, fuzzy msgid "Chroma Filter" msgstr "Chroma Filter" msgid "Sharpen / Blur" msgstr "銳化/模糊" #, fuzzy msgid "Width of the luma matrix" msgstr "Width of the luma matrix" #, fuzzy msgid "Height of the luma matrix" msgstr "Height of the luma matrix" #, fuzzy msgid "Amount of luma sharpness/blur" msgstr "Amount of luma sharpness/blur" #, fuzzy msgid "Width of the chroma matrix" msgstr "Width of the chroma matrix" #, fuzzy msgid "Height of the chroma matrix" msgstr "Height of the chroma matrix" #, fuzzy msgid "Amount of chroma sharpness/blur" msgstr "Amount of chroma sharpness/blur" msgid "3D Denoiser" msgstr "3D噪點去除" #, fuzzy msgid "Spatial luma strength" msgstr "Spatial luma strength" #, fuzzy msgid "Spatial chroma strength" msgstr "Spatial luma strength" #, fuzzy msgid "Temporal strength" msgstr "Temporal strength" msgid "HUE" msgstr "色相" msgid "Saturation" msgstr "飽和度" msgid "Contrast" msgstr "對比度" msgid "Brightness" msgstr "亮度" msgid "Sharpness" msgstr "銳利" msgid "Noise Reduction" msgstr "降噪" msgid "Smooth fast forward" msgstr "平穩快速前進" msgid "Fastest trick speed" msgstr "速度最快的方式" msgid "On-Screen Display" msgstr "屏幕顯示" msgid "Hide main menu" msgstr "隱藏主菜單" msgid "Resolution" msgstr "解決方案" msgid "Color depth" msgstr "" msgid "Blending method" msgstr "混合方法" #, fuzzy msgid "Use hardware for low-res video" msgstr "低清晰度視頻使用硬" msgid "Scaling method" msgstr "縮放方法" msgid "Scale subtitles" msgstr "" msgid "Show all layers" msgstr "顯示所有層" msgid "Dynamic transparency correction" msgstr "動態透明度更正" msgid "Static transparency correction" msgstr "靜態透明度更正" msgid "External subtitle size" msgstr "外部字幕大小" msgid "DVB subtitle decoder" msgstr "DVB字幕解碼器" msgid "Decoder" msgstr "解碼器" msgid "Buffer size" msgstr "緩衝區大小" #, fuzzy msgid "Number of PES packets" msgstr "PES的數據包" msgid "Local Frontend" msgstr "本地前端" msgid "Local Display Frontend" msgstr "本地顯示前端" msgid "Use keyboard" msgstr "使用鍵盤" msgid "Driver" msgstr "驅動" msgid "Display address" msgstr "顯示地址" msgid "Framebuffer device" msgstr "幀緩衝設備" msgid "Fullscreen mode" msgstr "全屏模式" #, fuzzy msgid "Window width" msgstr "窗口寬度" #, fuzzy msgid "Window height" msgstr "窗口的高度" msgid "Window aspect" msgstr "窗口縮放" msgid "Scale to window size" msgstr "縮放到窗口大小" msgid "Port" msgstr "端口" msgid "Speakers" msgstr "喇叭" msgid "Remote Clients" msgstr "遠程用戶" msgid "Allow remote clients" msgstr "允許遠程客戶" #, fuzzy msgid "Listen port (TCP and broadcast)" msgstr "聽端口(TCP和廣播)" #, fuzzy msgid "Listen address" msgstr "偵聽地址" #, fuzzy msgid "Remote keyboard" msgstr "遠程鍵盤" #, fuzzy msgid "Max number of clients" msgstr "最大數量的客戶" #, fuzzy msgid "PIPE transport" msgstr "PIPE傳送" #, fuzzy msgid "TCP transport" msgstr "TCP傳送" #, fuzzy msgid "UDP transport" msgstr "UDP傳送" #, fuzzy msgid "RTP (multicast) transport" msgstr "RTP (組播)傳送" #, fuzzy msgid "Address" msgstr "地址" #, fuzzy msgid "TTL" msgstr "TTL" #, fuzzy msgid "Transmit always on" msgstr "傳送永遠在線" #, fuzzy msgid "SAP announcements" msgstr "SAP公司公佈" #, fuzzy msgid "Server announce broadcasts" msgstr "服務器宣布廣播" #, fuzzy msgid "HTTP transport for media files" msgstr "HTTP傳輸媒體文件" msgid "Additional network services" msgstr "其他網絡服務" msgid "HTTP server" msgstr "HTTP服務器" msgid "HTTP clients can control VDR" msgstr "HTTP客戶端可以控制VDR" msgid "RTSP server" msgstr "RTSP服務器" msgid "RTSP clients can control VDR" msgstr "RTSP協議的客戶可以控制VDR" msgid "Playlist settings" msgstr "播放設置" msgid "Show the track number" msgstr "顯示曲目數量" msgid "Show the name of the artist" msgstr "顯示藝術家的名字" msgid "Show the name of the album" msgstr "顯示的專輯名稱" msgid "Scan for metainfo" msgstr "信息掃描" msgid "Cache metainfo" msgstr "緩存信息" msgid "Arrow keys control DVD playback" msgstr "允許按鍵控制DVD回放" msgid "Show hidden files" msgstr "" msgid "Allow removing files" msgstr "" msgid "Remember last playback position" msgstr "" msgid "Media Player" msgstr "媒體播放器" msgid "Play file" msgstr "播放文件" msgid "Play music" msgstr "播放音樂" msgid "View images" msgstr "瀏覽圖像" msgid "Play DVD disc" msgstr "播放DVD光盤" msgid "Play audio CD" msgstr "播放音樂CD" msgid "Play BluRay disc" msgstr "播放BluRay光盤" msgid "Video settings" msgstr "視頻設置" msgid "Audio settings" msgstr "音頻設置" msgid "Grayscale" msgstr "灰度" msgid "Bitmap" msgstr "位圖" msgid "OSD" msgstr "菜單" msgid "Test Images" msgstr "測試圖像" msgid "Play movie title" msgstr "" msgid "Play disc" msgstr "" msgid "Images" msgstr "圖片" msgid "Add to playlist" msgstr "添加列表" msgid "Button$Queue" msgstr "按鈕$隊列" msgid "Media" msgstr "媒體" msgid "Play only audio" msgstr "只播放聲音" msgid "Audio equalizer" msgstr "音頻均衡器" msgid "Video aspect ratio" msgstr "視頻寬高比" msgid "On" msgstr "開" msgid "Default playlist not found" msgstr "默認播放列表沒有找到" msgid "Default playlist is not symlink" msgstr "默認播放列表不是符號" msgid "Default playlist not defined" msgstr "默認播放列表沒有定義" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput的:熱鍵%s不可綁定" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib視頻輸出插件by NFVDR-HD" msgid "Frontend initialization failed" msgstr "前端初始化失敗" msgid "Server initialization failed" msgstr "服務器初始化失敗" msgid "Playlist" msgstr "播放列表" msgid "Button$Random" msgstr "按鈕$隨機" msgid "Button$Normal" msgstr "按鈕$正常" msgid "Button$Add files" msgstr "按鈕$添加文件" msgid "Button$Remove" msgstr "按鈕$刪除" msgid "Button$Mark" msgstr "" msgid "Queued to playlist" msgstr "選擇隊列到播放列表" msgid "Random play" msgstr "隨機播放" msgid "Normal play" msgstr "正常播放" msgid "DVD Menu" msgstr "" msgid "Exit DVD menu" msgstr "" msgid "DVD Root menu" msgstr "" msgid "DVD Title menu" msgstr "" msgid "DVD SPU menu" msgstr "" msgid "DVD Audio menu" msgstr "" msgid "Close menu" msgstr "" msgid "BluRay Top menu" msgstr "" msgid "Toggle Pop-Up menu" msgstr "" msgid "Next title" msgstr "" msgid "Previous title" msgstr "" msgid "Delete image ?" msgstr "是否刪除圖片?" #~ msgid "normal" #~ msgstr "正常" #~ msgid "inverted" #~ msgstr "反轉" #~ msgid "Interlaced Field Order" #~ msgstr "隔行場令" #~ msgid "Button$Sort" #~ msgstr "按鈕$排序" #~ msgid "Play file >>" #~ msgstr "播放文件>>" #~ msgid "Play music >>" #~ msgstr "播放音樂>>" #~ msgid "View images >>" #~ msgstr "瀏覽圖像>>" #~ msgid "Play DVD disc >>" #~ msgstr "播放DVD光盤>>" #~ msgid "Play audio CD >>" #~ msgstr "播放音樂CD >>" #~ msgid "Play BluRay disc >>" #~ msgstr "播BluRay光盤>>" #~ msgid "Play remote DVD >>" #~ msgstr "玩遙控影碟>>" #~ msgid "Play remote CD >>" #~ msgstr "播放遠程光盤>>" #~ msgid "Headphone audio mode" #~ msgstr "耳機音頻模式" #~ msgid "Audio equalizer >>" #~ msgstr "音頻均衡器>>" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/zh_CN.po���������������������������������������������������������������������0000644�0001750�0001750�00000027541�13061253422�014212� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2009-09-21 23:32+0800\n" "Last-Translator: NanFeng <nfgx@21cn.com>\n" "Language-Team: Chinese (simplified) <vdr@linuxtv.org>\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "custom" msgstr "自定义" msgid "tiny" msgstr "微小" msgid "small" msgstr "小" msgid "medium" msgstr "中" msgid "large" msgstr "大" msgid "huge" msgstr "巨大" msgid "automatic" msgstr "自动" msgid "default" msgstr "默认" msgid "Pan&Scan" msgstr "全景扫描" msgid "CenterCutOut" msgstr "CenterCutOut" msgid "square" msgstr "广场" msgid "anamorphic" msgstr "变形" msgid "DVB" msgstr "DVB" msgid "off" msgstr "关" msgid "no audio" msgstr "没有音频" msgid "no video" msgstr "没有视频" msgid "Off" msgstr "关" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "示波器" msgid "FFT Scope" msgstr "FFT的范围" msgid "FFT Graph" msgstr "FFT 图像" msgid "Image" msgstr "" msgid "Mono 1.0" msgstr "单声道 1.0" msgid "Stereo 2.0" msgstr "立体声2.0" msgid "Headphones 2.0" msgstr "耳机2.0" msgid "Stereo 2.1" msgstr "立体声2.1" msgid "Surround 3.0" msgstr "环绕声3.0" msgid "Surround 4.0" msgstr "环绕声4.0" msgid "Surround 4.1" msgstr "环绕声4.1" msgid "Surround 5.0" msgstr "环绕声5.0" msgid "Surround 5.1" msgstr "环绕声5.1" msgid "Surround 6.0" msgstr "环绕声6.0" msgid "Surround 6.1" msgstr "环绕声6.1" msgid "Surround 7.1" msgstr "环绕声7.1" msgid "Pass Through" msgstr "通过" msgid "very large" msgstr "非常大" msgid "Software" msgstr "软件" msgid "Hardware" msgstr "硬件" msgid "no" msgstr "否" msgid "grayscale" msgstr "灰度" msgid "transparent" msgstr "透明" msgid "transparent grayscale" msgstr "透明灰度" msgid "yes" msgstr "是" msgid "nearest" msgstr "最近" msgid "bilinear" msgstr "双线性" msgid "LUT8" msgstr "" msgid "TrueColor" msgstr "" msgid "video stream" msgstr "" msgid "none" msgstr "无" msgid "nonref" msgstr "nonref" msgid "bidir" msgstr "双向口" msgid "nonkey" msgstr "nonkey" msgid "all" msgstr "所有" msgid "Audio" msgstr "音频" msgid "Volume control" msgstr "音量控制" msgid "Delay" msgstr "延迟" msgid "ms" msgstr "毫秒" msgid "Audio Compression" msgstr "音频压缩" msgid "Upmix stereo to 5.1" msgstr "上混立体声到5.1" msgid "Downmix AC3 to surround" msgstr "缩混的AC3" msgid "Mix to headphones" msgstr "组合,以耳机" msgid "Visualization" msgstr "可视化" msgid "Width" msgstr "宽度" msgid "px" msgstr "px" msgid "Height" msgstr "高度" msgid "Speed" msgstr "速度" msgid "fps" msgstr "帧" msgid "Background image MRL" msgstr "" msgid "Audio Equalizer" msgstr "音频均衡器" msgid "Use Video-Out Driver" msgstr "使用视频输出驱动器" msgid "vector" msgstr "载体" msgid "full" msgstr "全" msgid "half (top)" msgstr "半(上)" msgid "half (bottom)" msgstr "半(下)" msgid "Video" msgstr "视频" msgid "Aspect ratio" msgstr "画面比例" msgid "Crop letterbox 4:3 to 16:9" msgstr "选择4:3或者16:9" msgid "Use driver crop" msgstr "" msgid "Autodetect letterbox" msgstr "自动检测盒子模式" msgid "Crop to" msgstr "剪裁" msgid "Autodetect rate" msgstr "" msgid "Stabilize time" msgstr "" msgid "Maximum logo width [%]" msgstr "" msgid "Overscan compensate [%1000]" msgstr "" msgid "Soft start" msgstr "软启动" msgid "Soft start step" msgstr "" msgid "Detect subtitles" msgstr "" msgid "Subs detect stabilize time" msgstr "" msgid "Subs detect lifetime" msgstr "" msgid "Use avards analysis" msgstr "" msgid "Bar tone tolerance" msgstr "" msgid "Software scaling" msgstr "软件缩放" msgid "Change aspect ratio" msgstr "更改宽高比" msgid "Change video size" msgstr "改变视频大小" msgid "Allow downscaling" msgstr "允许缩小尺度" msgid "Overscan (crop image borders)" msgstr "过扫描(去掉图像边框)" msgid "Post processing (ffmpeg)" msgstr "后处理(ffmpeg)" msgid "Quality" msgstr "Quality" msgid "Mode" msgstr "Mode" msgid "Deinterlacing" msgstr "去隔行" msgid "Method" msgstr "Method" msgid "Cheap mode" msgstr "Cheap mode" msgid "Pulldown" msgstr "Pulldown" msgid "Frame rate" msgstr "Frame rate" msgid "Judder Correction" msgstr "Judder Correction" msgid "Use progressive frame flag" msgstr "Use progressive frame flag" msgid "Chroma Filter" msgstr "Chroma Filter" msgid "Sharpen / Blur" msgstr "锐化/模糊" msgid "Width of the luma matrix" msgstr "Width of the luma matrix" msgid "Height of the luma matrix" msgstr "Height of the luma matrix" msgid "Amount of luma sharpness/blur" msgstr "Amount of luma sharpness/blur" msgid "Width of the chroma matrix" msgstr "Width of the chroma matrix" msgid "Height of the chroma matrix" msgstr "Height of the chroma matrix" msgid "Amount of chroma sharpness/blur" msgstr "Amount of chroma sharpness/blur" msgid "3D Denoiser" msgstr "3D噪点去除" msgid "Spatial luma strength" msgstr "Spatial luma strength" msgid "Spatial chroma strength" msgstr "Spatial luma strength" msgid "Temporal strength" msgstr "Temporal strength" msgid "HUE" msgstr "色相" msgid "Saturation" msgstr "饱和度" msgid "Contrast" msgstr "对比度" msgid "Brightness" msgstr "亮度" msgid "Sharpness" msgstr "锐利" msgid "Noise Reduction" msgstr "降噪" msgid "Smooth fast forward" msgstr "平稳快速前进" msgid "Fastest trick speed" msgstr "速度最快的方式" msgid "On-Screen Display" msgstr "屏幕显示" msgid "Hide main menu" msgstr "隐藏主菜单" msgid "Resolution" msgstr "解决方案" msgid "Color depth" msgstr "" msgid "Blending method" msgstr "混合方法" msgid "Use hardware for low-res video" msgstr "低清晰度视频使用硬" msgid "Scaling method" msgstr "缩放方法" msgid "Scale subtitles" msgstr "" msgid "Show all layers" msgstr "显示所有层" msgid "Dynamic transparency correction" msgstr "动态透明度更正" msgid "Static transparency correction" msgstr "静态透明度更正" msgid "External subtitle size" msgstr "外部字幕大小" msgid "DVB subtitle decoder" msgstr "DVB字幕解码器" msgid "Decoder" msgstr "解码器" msgid "Buffer size" msgstr "缓冲区大小" msgid "Number of PES packets" msgstr "PES的数据包" msgid "Local Frontend" msgstr "本地前端" msgid "Local Display Frontend" msgstr "本地显示前端" msgid "Use keyboard" msgstr "使用键盘" msgid "Driver" msgstr "驱动" msgid "Display address" msgstr "显示地址" msgid "Framebuffer device" msgstr "帧缓冲设备" msgid "Fullscreen mode" msgstr "全屏模式" msgid "Window width" msgstr "窗口宽度" msgid "Window height" msgstr "窗口的高度" msgid "Window aspect" msgstr "窗口缩放" msgid "Scale to window size" msgstr "缩放到窗口大小" msgid "Port" msgstr "端口" msgid "Speakers" msgstr "喇叭" msgid "Remote Clients" msgstr "远程用户" msgid "Allow remote clients" msgstr "允许远程客户" msgid "Listen port (TCP and broadcast)" msgstr "听端口(TCP和广播)" msgid "Listen address" msgstr "侦听地址" msgid "Remote keyboard" msgstr "远程键盘" msgid "Max number of clients" msgstr "最大数量的客户" msgid "PIPE transport" msgstr "PIPE 传送" msgid "TCP transport" msgstr "TCP 传送" msgid "UDP transport" msgstr "UDP 传送" msgid "RTP (multicast) transport" msgstr "RTP (组播) 传送" msgid "Address" msgstr "地址" msgid "TTL" msgstr "TTL" msgid "Transmit always on" msgstr "传送永远在线" msgid "SAP announcements" msgstr "SAP公司公布" msgid "Server announce broadcasts" msgstr "服务器宣布广播" msgid "HTTP transport for media files" msgstr "HTTP传输媒体文件" msgid "Additional network services" msgstr "其他网络服务" msgid "HTTP server" msgstr "HTTP服务器" msgid "HTTP clients can control VDR" msgstr "HTTP客户端可以控制VDR" msgid "RTSP server" msgstr "RTSP服务器" msgid "RTSP clients can control VDR" msgstr "RTSP协议的客户可以控制VDR" msgid "Playlist settings" msgstr "播放设置" msgid "Show the track number" msgstr "显示曲目数量" msgid "Show the name of the artist" msgstr "显示艺术家的名字" msgid "Show the name of the album" msgstr "显示的专辑名称" msgid "Scan for metainfo" msgstr "信息扫描" msgid "Cache metainfo" msgstr "缓存信息" msgid "Arrow keys control DVD playback" msgstr "允许按键控制DVD回放" msgid "Show hidden files" msgstr "" msgid "Allow removing files" msgstr "" msgid "Remember last playback position" msgstr "" msgid "Media Player" msgstr "媒体播放器" msgid "Play file" msgstr "播放文件" msgid "Play music" msgstr "播放音乐" msgid "View images" msgstr "浏览图像" msgid "Play DVD disc" msgstr "播放DVD光盘" msgid "Play audio CD" msgstr "播放音乐CD" msgid "Play BluRay disc" msgstr "播放BluRay光盘 >>" msgid "Video settings" msgstr "视频设置" msgid "Audio settings" msgstr "音频设置" msgid "Grayscale" msgstr "灰度" msgid "Bitmap" msgstr "位图" msgid "OSD" msgstr "菜单" msgid "Test Images" msgstr "测试图像" msgid "Play movie title" msgstr "" msgid "Play disc" msgstr "" msgid "Images" msgstr "图片" msgid "Add to playlist" msgstr "添加列表" msgid "Button$Queue" msgstr "按钮$队列" msgid "Media" msgstr "媒体" msgid "Play only audio" msgstr "只播放声音" msgid "Audio equalizer" msgstr "音频均衡器" msgid "Video aspect ratio" msgstr "视频宽高比" msgid "On" msgstr "开" msgid "Default playlist not found" msgstr "默认播放列表没有找到" msgid "Default playlist is not symlink" msgstr "默认播放列表不是符号" msgid "Default playlist not defined" msgstr "默认播放列表没有定义" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput的:热键%s不可绑定" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib 视频输出插件 by NFVDR-HD" msgid "Frontend initialization failed" msgstr "前端初始化失败" msgid "Server initialization failed" msgstr "服务器初始化失败" msgid "Playlist" msgstr "播放列表" msgid "Button$Random" msgstr "按钮$随机" msgid "Button$Normal" msgstr "按钮$正常" msgid "Button$Add files" msgstr "按钮$添加文件" msgid "Button$Remove" msgstr "按钮$删除" msgid "Button$Mark" msgstr "" msgid "Queued to playlist" msgstr "选择队列到播放列表" msgid "Random play" msgstr "随机播放" msgid "Normal play" msgstr "正常播放" msgid "DVD Menu" msgstr "" msgid "Exit DVD menu" msgstr "" msgid "DVD Root menu" msgstr "" msgid "DVD Title menu" msgstr "" msgid "DVD SPU menu" msgstr "" msgid "DVD Audio menu" msgstr "" msgid "Close menu" msgstr "" msgid "BluRay Top menu" msgstr "" msgid "Toggle Pop-Up menu" msgstr "" msgid "Next title" msgstr "" msgid "Previous title" msgstr "" msgid "Delete image ?" msgstr "是否删除图片?" #~ msgid "normal" #~ msgstr "正常" #~ msgid "inverted" #~ msgstr "反转" #~ msgid "Interlaced Field Order" #~ msgstr "隔行场令" #~ msgid "Button$Sort" #~ msgstr "按钮$排序" #~ msgid "Play file >>" #~ msgstr "播放文件 >>" #~ msgid "Play music >>" #~ msgstr "播放音乐 >>" #~ msgid "View images >>" #~ msgstr "浏览图像 >>" #~ msgid "Play DVD disc >>" #~ msgstr "播放DVD光盘 >>" #~ msgid "Play audio CD >>" #~ msgstr "播放音乐CD >>" #~ msgid "Play BluRay disc >>" #~ msgstr "播放BluRay光盘 >>" #~ msgid "Play remote DVD >>" #~ msgstr "玩遥控影碟 >>" #~ msgid "Play remote CD >>" #~ msgstr "播放远程光盘 >>" #~ msgid "Headphone audio mode" #~ msgstr "耳机音频模式" #~ msgid "Audio equalizer >>" #~ msgstr "音频均衡器 >>" ���������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/uk_UA.po���������������������������������������������������������������������0000644�0001750�0001750�00000037615�13061253422�014220� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # Vladimir Monchenko # Yarema aka Knedlyk <yupadmin@gmail.com>, 2010. msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2012-11-29 13:34+0100\n" "Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n" "Language-Team: Ukrainian <vdr@linuxtv.org>\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\\n\n" msgid "custom" msgstr "користувача" msgid "tiny" msgstr "дуже малий" msgid "small" msgstr "малий" msgid "medium" msgstr "середній" msgid "large" msgstr "великий" msgid "huge" msgstr "дуже великий" msgid "automatic" msgstr "автоматично" msgid "default" msgstr "типово" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "ОбрізатиПоБоках" msgid "square" msgstr "квадрат" msgid "anamorphic" msgstr "спотворення" msgid "DVB" msgstr "DVB" msgid "off" msgstr "викл" msgid "no audio" msgstr "немає аудіо" msgid "no video" msgstr "немає відео" msgid "Off" msgstr "Виключити" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "Oscilloscope" msgid "FFT Scope" msgstr "FFT Scope" msgid "FFT Graph" msgstr "FFT Graph" msgid "Image" msgstr "Малюнок" msgid "Mono 1.0" msgstr "Моно 1.0" msgid "Stereo 2.0" msgstr "Стерео 2.0" msgid "Headphones 2.0" msgstr "Навушники 2.0" msgid "Stereo 2.1" msgstr "Стерео 2.1" msgid "Surround 3.0" msgstr "Surround 3.0" msgid "Surround 4.0" msgstr "Surround 4.0" msgid "Surround 4.1" msgstr "Surround 4.1" msgid "Surround 5.0" msgstr "Surround 5.0" msgid "Surround 5.1" msgstr "Surround 5.1" msgid "Surround 6.0" msgstr "Surround 6.0" msgid "Surround 6.1" msgstr "Surround 6.1" msgid "Surround 7.1" msgstr "Surround 7.1" msgid "Pass Through" msgstr "Pass Through" msgid "very large" msgstr "дуже великий" msgid "Software" msgstr "Програмне" msgid "Hardware" msgstr "Апаратне" msgid "no" msgstr "ні" msgid "grayscale" msgstr "відтінки сірого" msgid "transparent" msgstr "прозорий" msgid "transparent grayscale" msgstr "прозорі відтінки сірого" msgid "yes" msgstr "так" msgid "nearest" msgstr "найближчий" msgid "bilinear" msgstr "білінійний" msgid "LUT8" msgstr "LUT8" msgid "TrueColor" msgstr "TrueColor" msgid "video stream" msgstr "відеопотік" msgid "none" msgstr "немає" msgid "nonref" msgstr "nonref" msgid "bidir" msgstr "bidir" msgid "nonkey" msgstr "безклавішний" msgid "all" msgstr "все" msgid "Audio" msgstr "Аудіо" msgid "Volume control" msgstr "Регулятор гучності" msgid "Delay" msgstr "Затримка" msgid "ms" msgstr "мс" msgid "Audio Compression" msgstr "Компресія аудіо" msgid "Upmix stereo to 5.1" msgstr "Перетворити стерео до 5.1" msgid "Downmix AC3 to surround" msgstr "Перетворити AC3 до surround" msgid "Mix to headphones" msgstr "Міксувати до навушників" msgid "Visualization" msgstr "Візуалізація" msgid "Width" msgstr "Ширина" msgid "px" msgstr "пікселі" msgid "Height" msgstr "Висота" msgid "Speed" msgstr "Швидкість" msgid "fps" msgstr "кадр за сек" msgid "Background image MRL" msgstr "Малюнок тла MRL" msgid "Audio Equalizer" msgstr "Аудіо еквалайзер" msgid "Use Video-Out Driver" msgstr "Викор. вихідний відео-драйвер" msgid "vector" msgstr "вектор" msgid "full" msgstr "повністю" msgid "half (top)" msgstr "половина (верхня)" msgid "half (bottom)" msgstr "половина (нижня)" msgid "Video" msgstr "Відео" msgid "Aspect ratio" msgstr "Пропорція" msgid "Crop letterbox 4:3 to 16:9" msgstr "Змінити зображення з 4:3 до 16:9" msgid "Use driver crop" msgstr "Використати обрізання з драйвера" msgid "Autodetect letterbox" msgstr "Автовизначення розміру" msgid "Crop to" msgstr "Обрізати до" msgid "Autodetect rate" msgstr "Автовизначення швидкості" msgid "Stabilize time" msgstr "Час стібілізації" msgid "Maximum logo width [%]" msgstr "Макс. ширина лого [%]" msgid "Overscan compensate [%1000]" msgstr "Компенсація перескану [%1000]" msgid "Soft start" msgstr "М'який старт" msgid "Soft start step" msgstr "М’який крок початку" msgid "Detect subtitles" msgstr "Визначити субтитри" msgid "Subs detect stabilize time" msgstr "Час стабілізації детектування сибтитрів" msgid "Subs detect lifetime" msgstr "Тривалість детектування сибтитрів" msgid "Use avards analysis" msgstr "Використання avards аналізу" msgid "Bar tone tolerance" msgstr "Допуск тонів поділок" msgid "Software scaling" msgstr "Програмне масштабування" msgid "Change aspect ratio" msgstr "Змінити пропорції" msgid "Change video size" msgstr "Змінити розмір відео" msgid "Allow downscaling" msgstr "Дозволити масштабування з зменшенням якості" msgid "Overscan (crop image borders)" msgstr "Пересканувати (обрізати границі зображення)" msgid "Post processing (ffmpeg)" msgstr "Післяобробка (ffmpeg)" msgid "Quality" msgstr "Якість" msgid "Mode" msgstr "Режим" msgid "Deinterlacing" msgstr "Деінтерлесінг" msgid "Method" msgstr "Метод" msgid "Cheap mode" msgstr "\"Дешевий\" метод" msgid "Pulldown" msgstr "Зниження" msgid "Frame rate" msgstr "Частота кадрів" msgid "Judder Correction" msgstr "Корекція Judder-а" msgid "Use progressive frame flag" msgstr "Використовувати прогресивну мітку кадру" msgid "Chroma Filter" msgstr "Chroma-фільтр " msgid "Sharpen / Blur" msgstr "Різкість / Затемнення" msgid "Width of the luma matrix" msgstr "Ширина luma-матриці" msgid "Height of the luma matrix" msgstr "Висота luma-матриці" msgid "Amount of luma sharpness/blur" msgstr "Значення різкості/затемнення для luma" msgid "Width of the chroma matrix" msgstr "Ширина chroma-матриці" msgid "Height of the chroma matrix" msgstr "Висота chroma-матриці" msgid "Amount of chroma sharpness/blur" msgstr "Значення різкості/затемнення для chroma" msgid "3D Denoiser" msgstr "3D знімач шумів" msgid "Spatial luma strength" msgstr "Просторова міцність luma" msgid "Spatial chroma strength" msgstr "Просторова міцність chroma" msgid "Temporal strength" msgstr "Тимчасова міцність" msgid "HUE" msgstr "HUE" msgid "Saturation" msgstr "Насиченість" msgid "Contrast" msgstr "Контраст" msgid "Brightness" msgstr "Яскравість" msgid "Sharpness" msgstr "Різкість" msgid "Noise Reduction" msgstr "Зменшення шумів" msgid "Smooth fast forward" msgstr "Гладка перемотка вперед" msgid "Fastest trick speed" msgstr "Найшвидша швидкість тріків" msgid "On-Screen Display" msgstr "Екранне меню" msgid "Hide main menu" msgstr "Сховати екранне меню" msgid "Resolution" msgstr "Роздільна здатність" msgid "Color depth" msgstr "Глибина кольору" msgid "Blending method" msgstr "Метод змішування" msgid "Use hardware for low-res video" msgstr "Викор. ап. засоби для відео з низьк. розд.зд." msgid "Scaling method" msgstr "Метод масштабування" msgid "Scale subtitles" msgstr "Масштабувати субтитри" msgid "Show all layers" msgstr "Показати всі шари" msgid "Dynamic transparency correction" msgstr "Динамічна корекція прозорості" msgid "Static transparency correction" msgstr "Статична корекція прозорості" msgid "External subtitle size" msgstr "Розмір зовнішніх субтитрів" msgid "DVB subtitle decoder" msgstr "Декодер субтитрів DVB" msgid "Decoder" msgstr "Декодер" msgid "Buffer size" msgstr "Розмір буфера" msgid "Number of PES packets" msgstr "Кількість PES пакетів" msgid "Local Frontend" msgstr "Локальний фронтенд" msgid "Local Display Frontend" msgstr "Локальний фронтенд показу" msgid "Use keyboard" msgstr "Використати клавіатуру" msgid "Driver" msgstr "Драйвер" msgid "Display address" msgstr "Адреса дисплею" msgid "Framebuffer device" msgstr "Пристрій фреймбуфера" msgid "Fullscreen mode" msgstr "Режим повного екрану" msgid "Window width" msgstr "Ширина вікна" msgid "Window height" msgstr "Висота вікна" msgid "Window aspect" msgstr "Пропорція вікна" msgid "Scale to window size" msgstr "Масштабувати до розміру вікна" msgid "Port" msgstr "Порт" msgid "Speakers" msgstr "Колонки" msgid "Remote Clients" msgstr "Віддалені клієнти" msgid "Allow remote clients" msgstr "Дозволити віддалені клієнти" msgid "Listen port (TCP and broadcast)" msgstr "Слухати порт (TCP і трансляція)" msgid "Listen address" msgstr "Слухати адресу" msgid "Remote keyboard" msgstr "Віддалена клавіатура" msgid "Max number of clients" msgstr "Макс. кількість клієнтів" msgid "PIPE transport" msgstr "PIPE транспорт" msgid "TCP transport" msgstr "TCP транспорт" msgid "UDP transport" msgstr "UDP транспорт" msgid "RTP (multicast) transport" msgstr "RTP (multicast) транспорт" msgid "Address" msgstr "Адреса" msgid "TTL" msgstr "TTL" msgid "Transmit always on" msgstr "Передача завжди включена" msgid "SAP announcements" msgstr "SAP оголошення" msgid "Server announce broadcasts" msgstr "Транслювання оголошення сервера" msgid "HTTP transport for media files" msgstr "HTTP транспорт для медіа файлів" msgid "Additional network services" msgstr "Додаткові сервіси інтернету" msgid "HTTP server" msgstr "HTTP сервер" msgid "HTTP clients can control VDR" msgstr "HTTP клієнти можуть контролювати VDR" msgid "RTSP server" msgstr "RTSP сервер" msgid "RTSP clients can control VDR" msgstr "RTSP клінти можуть контролювати VDR" msgid "Playlist settings" msgstr "Налаштування списку програвання" msgid "Show the track number" msgstr "Показувати номер треку" msgid "Show the name of the artist" msgstr "Показувати ім’я виконавця" msgid "Show the name of the album" msgstr "Показувати назву альбому" msgid "Scan for metainfo" msgstr "Сканувати метадані" msgid "Cache metainfo" msgstr "Кешувати метадані" msgid "Arrow keys control DVD playback" msgstr "Клавіші зі стрілками контролюють програвання DVD" msgid "Show hidden files" msgstr "Показати приховані файли" msgid "Allow removing files" msgstr "Дозволяти вилучати файли" msgid "Remember last playback position" msgstr "" msgid "Media Player" msgstr "Програвач медіа" msgid "Play file" msgstr "Програвати файл" msgid "Play music" msgstr "Програвати музику" msgid "View images" msgstr "Переглядати зображення" msgid "Play DVD disc" msgstr "Програвати диск DVD" msgid "Play audio CD" msgstr "Програвати аудіо CD" msgid "Play BluRay disc" msgstr "Програвати диск BluRay" msgid "Video settings" msgstr "Налаштування відео" msgid "Audio settings" msgstr "Налаштування аудіо" msgid "Grayscale" msgstr "Відтінки сірого" msgid "Bitmap" msgstr "Колір" msgid "OSD" msgstr "Екранне меню" msgid "Test Images" msgstr "Тестові зображення" msgid "Play movie title" msgstr "Програвати кінострічку" msgid "Play disc" msgstr "Програвати диск" msgid "Images" msgstr "Малюнки" msgid "Add to playlist" msgstr "Додати до списку програвання" msgid "Button$Queue" msgstr "Черга" msgid "Media" msgstr "Медіа" msgid "Play only audio" msgstr "Програвати тільки аудіо" msgid "Audio equalizer" msgstr "Аудіо еквалайзер" msgid "Video aspect ratio" msgstr "Пропорція відео" msgid "On" msgstr "Вкл" msgid "Default playlist not found" msgstr "Типовий список програвання не знайдено" msgid "Default playlist is not symlink" msgstr "Типовий список програвання не є символьним посиланням" msgid "Default playlist not defined" msgstr "Типовий список програвання не визначено" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput: скорочення %s нічому не відповідає" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib output плагін" msgid "Frontend initialization failed" msgstr "Ініціалізація фронтенду не вдалася" msgid "Server initialization failed" msgstr "Ініціалізація сервера не вдалася" msgid "Playlist" msgstr "Список програвання" msgid "Button$Random" msgstr "Випадково" msgid "Button$Normal" msgstr "Нормально" msgid "Button$Add files" msgstr "Додати файли" msgid "Button$Remove" msgstr "Видалити" msgid "Button$Mark" msgstr "Позначка" msgid "Queued to playlist" msgstr "Додано до списку програвання" msgid "Random play" msgstr "Випадкове програвання" msgid "Normal play" msgstr "Нормальне програвання" msgid "DVD Menu" msgstr "Меню DVD" msgid "Exit DVD menu" msgstr "Вийти з меню DVD" msgid "DVD Root menu" msgstr "Корінне меню DVD" msgid "DVD Title menu" msgstr "Меню заголовків DVD" msgid "DVD SPU menu" msgstr "Меню DVD SPU" msgid "DVD Audio menu" msgstr "Меню DVD аудіо" msgid "Close menu" msgstr "Закрити меню" msgid "BluRay Top menu" msgstr "Меню BluRay" msgid "Toggle Pop-Up menu" msgstr "Включити вигулькне меню" msgid "Next title" msgstr "Наступний заголовок" msgid "Previous title" msgstr "Попередній заголовок" msgid "Delete image ?" msgstr "Видалити зображення?" #~ msgid "normal" #~ msgstr "нормально" #~ msgid "inverted" #~ msgstr "обернено" #~ msgid "Interlaced Field Order" #~ msgstr "Порядок поля згладження" #~ msgid "Button$Sort" #~ msgstr "Сортувати" �������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/sk_SK.po���������������������������������������������������������������������0000644�0001750�0001750�00000032536�13061253422�014223� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2014-02-05 11:12+0100\n" "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n" "Language-Team: Slovak <vdr@linuxtv.org>\n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" msgid "custom" msgstr "užívateľské" msgid "tiny" msgstr "najmenší" msgid "small" msgstr "malý" msgid "medium" msgstr "stredný" msgid "large" msgstr "veľký" msgid "huge" msgstr "najväčší" msgid "automatic" msgstr "automaticky" msgid "default" msgstr "predvolený" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "CenterCutOut" msgid "square" msgstr "do štvorca" msgid "anamorphic" msgstr "neforemne" msgid "DVB" msgstr "DVB" msgid "off" msgstr "vypnuté" msgid "no audio" msgstr "bez zvuku" msgid "no video" msgstr "bez obrazu" msgid "Off" msgstr "Vypnuté" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "Osciloskop" msgid "FFT Scope" msgstr "FFT spektrum" msgid "FFT Graph" msgstr "FFT graf" msgid "Image" msgstr "Obrázok" msgid "Mono 1.0" msgstr "Mono 1.0" msgid "Stereo 2.0" msgstr "Stereo 2.0" msgid "Headphones 2.0" msgstr "Sluchátka 2.0" msgid "Stereo 2.1" msgstr "Stereo 2.1" msgid "Surround 3.0" msgstr "Surround 3.0" msgid "Surround 4.0" msgstr "Surround 4.0" msgid "Surround 4.1" msgstr "Surround 4.1" msgid "Surround 5.0" msgstr "Surround 5.0" msgid "Surround 5.1" msgstr "Surround 5.1" msgid "Surround 6.0" msgstr "Surround 6.0" msgid "Surround 6.1" msgstr "Surround 6.1" msgid "Surround 7.1" msgstr "Surround 7.1" msgid "Pass Through" msgstr "Pass Through" msgid "very large" msgstr "veľmi veľký" msgid "Software" msgstr "Softwarové" msgid "Hardware" msgstr "Hardwarové" msgid "no" msgstr "nie" msgid "grayscale" msgstr "odtiene šedej" msgid "transparent" msgstr "priehľadný" msgid "transparent grayscale" msgstr "transparentné odtiene šedej" msgid "yes" msgstr "áno" msgid "nearest" msgstr "najbližší" msgid "bilinear" msgstr "bi-lineárne" msgid "LUT8" msgstr "LUT8" msgid "TrueColor" msgstr "Plná farebnosť" msgid "video stream" msgstr "ako video-obraz" msgid "none" msgstr "žiadny" msgid "nonref" msgstr "bez referencií" msgid "bidir" msgstr "iba B-snímky" msgid "nonkey" msgstr "žiadne kľúčové snímky" msgid "all" msgstr "všetko" msgid "Audio" msgstr "Zvuk" msgid "Volume control" msgstr "Ovládanie hlasitosti" msgid "Delay" msgstr "Omeškanie" msgid "ms" msgstr "ms" msgid "Audio Compression" msgstr "Kompresia zvuku" msgid "Upmix stereo to 5.1" msgstr "Prevzorkovať stereo na 5.1" msgid "Downmix AC3 to surround" msgstr "Prevzorkovať AC3 na surround" msgid "Mix to headphones" msgstr "Miešať zvuk pre slúchadlá" msgid "Visualization" msgstr "Vizualizácia" msgid "Width" msgstr "Šírka" msgid "px" msgstr "bodov" msgid "Height" msgstr "Výška" msgid "Speed" msgstr "Rýchlosť" msgid "fps" msgstr "snímkov/sek." msgid "Background image MRL" msgstr "Obrázok na pozadí MRL" msgid "Audio Equalizer" msgstr "Zvukový equalizér" msgid "Use Video-Out Driver" msgstr "Použiť video-výstupný ovládač" msgid "vector" msgstr "vektor" msgid "full" msgstr "plný" msgid "half (top)" msgstr "polovičný (na vrchu)" msgid "half (bottom)" msgstr "polovičný (na spodku)" msgid "Video" msgstr "Obraz" msgid "Aspect ratio" msgstr "Pomer strán" msgid "Crop letterbox 4:3 to 16:9" msgstr "Orezať letterbox 4:3 na 16:9" msgid "Use driver crop" msgstr "Použiť ovládač orezania" msgid "Autodetect letterbox" msgstr "Automaticky identifikovať letterbox" msgid "Crop to" msgstr "Orezať na" msgid "Autodetect rate" msgstr "Detekčná rýchlosť" msgid "Stabilize time" msgstr "Stabilizovať čas" msgid "Maximum logo width [%]" msgstr "Maximálna šírka loga [%]" msgid "Overscan compensate [%1000]" msgstr "Kompenzácia prevzorkovania [%1000]" msgid "Soft start" msgstr "Progresívne spustenie" msgid "Soft start step" msgstr "Krok progresívneho spustenia" msgid "Detect subtitles" msgstr "Zisťovať titulky" msgid "Subs detect stabilize time" msgstr "Stabilizačný čas odhalenia" msgid "Subs detect lifetime" msgstr "Životnosť odhalenia" msgid "Use avards analysis" msgstr "Pomocou analýzy Avards" msgid "Bar tone tolerance" msgstr "Tolerancia ladenia odtieňa" msgid "Software scaling" msgstr "Softvérová zmena obrazu" msgid "Change aspect ratio" msgstr "Zmena pomeru strán obrazu" msgid "Change video size" msgstr "Zmeniť veľkosť obrazu" msgid "Allow downscaling" msgstr "Povoliť zmenšiť" msgid "Overscan (crop image borders)" msgstr "Overscan (orezať okraje obrazu)" msgid "Post processing (ffmpeg)" msgstr "Post processing (ffmpeg)" msgid "Quality" msgstr "Kvalita" msgid "Mode" msgstr "Mód" msgid "Deinterlacing" msgstr "Odstraňovanie prekladania" msgid "Method" msgstr "Metóda" msgid "Cheap mode" msgstr "Zjednodušený mód" msgid "Pulldown" msgstr "Pulldown" msgid "Frame rate" msgstr "Snímková rýchlosť" msgid "Judder Correction" msgstr "Korekcia chvenia" msgid "Use progressive frame flag" msgstr "Použiť progresívny obraz" msgid "Chroma Filter" msgstr "Filter farebnosti" msgid "Sharpen / Blur" msgstr "Zaostrenie / rozmazanie" msgid "Width of the luma matrix" msgstr "Šírka matice jasu" msgid "Height of the luma matrix" msgstr "Výška matice jasu" msgid "Amount of luma sharpness/blur" msgstr "Množstvo jasu zaostriť/rozmazať" msgid "Width of the chroma matrix" msgstr "Šírka matice farebnosti" msgid "Height of the chroma matrix" msgstr "Výška matice farebnosti" msgid "Amount of chroma sharpness/blur" msgstr "Množstvo farebnosti zaostriť/rozmazať" msgid "3D Denoiser" msgstr "3D redukcia šumu" msgid "Spatial luma strength" msgstr "Priestorová intenzita jasu" msgid "Spatial chroma strength" msgstr "Dispozičná farebná pestrosť" msgid "Temporal strength" msgstr "Časová náročnosť" msgid "HUE" msgstr "Odtieň" msgid "Saturation" msgstr "Sýtosť farby" msgid "Contrast" msgstr "Kontrast" msgid "Brightness" msgstr "Jas" msgid "Sharpness" msgstr "Ostrosť" msgid "Noise Reduction" msgstr "Redukcia šumu" msgid "Smooth fast forward" msgstr "Plynulé pretáčanie" msgid "Fastest trick speed" msgstr "Rýchlejší trik" msgid "On-Screen Display" msgstr "Obrazovkové menu" msgid "Hide main menu" msgstr "Nezobrazovať v hlavnom menu" msgid "Resolution" msgstr "Rozlíšenie" msgid "Color depth" msgstr "Farebná hĺbka" msgid "Blending method" msgstr "Spôsob miešania" msgid "Use hardware for low-res video" msgstr "Použite hardware pre malé rozlíšenie videa" msgid "Scaling method" msgstr "Spôsob úpravy mierky" msgid "Scale subtitles" msgstr "Zväčšiť titulky" msgid "Show all layers" msgstr "Zobraziť všetky vrstvy" msgid "Dynamic transparency correction" msgstr "Úprava dynamické priehľadnosti" msgid "Static transparency correction" msgstr "Úprava statické priehľadnosti" msgid "External subtitle size" msgstr "Veľkosť externých titulkov" msgid "DVB subtitle decoder" msgstr "Použitý dekóder titulok" msgid "Decoder" msgstr "Dekodér" msgid "Buffer size" msgstr "Veľkosť vyrovnávacej pamäte" msgid "Number of PES packets" msgstr "Počet PES paketov" msgid "Local Frontend" msgstr "Lokálne rozhranie" msgid "Local Display Frontend" msgstr "Lokálne zobrazovacie rozhranie" msgid "Use keyboard" msgstr "Používať klávesnicu" msgid "Driver" msgstr "Ovládač" msgid "Display address" msgstr "Adresa obrazovky" msgid "Framebuffer device" msgstr "Framebuffer zariadenie" msgid "Fullscreen mode" msgstr "Celo obrazovkový režim" msgid "Window width" msgstr "Šírka okna" msgid "Window height" msgstr "Výška okna" msgid "Window aspect" msgstr "Pomer strán okna" msgid "Scale to window size" msgstr "Škálovať do velikosti okna" msgid "Port" msgstr "Port" msgid "Speakers" msgstr "Reproduktory" msgid "Remote Clients" msgstr "Vzdialení klienti" msgid "Allow remote clients" msgstr "Povoliť vzdialených klientov" msgid "Listen port (TCP and broadcast)" msgstr "Počúvať port (TCP a vysielanie)" msgid "Listen address" msgstr "Počúvať adresu" msgid "Remote keyboard" msgstr "Vzdialená klávesnica" msgid "Max number of clients" msgstr "Max počet klientov" msgid "PIPE transport" msgstr "PIPE transport" msgid "TCP transport" msgstr "TCP transport" msgid "UDP transport" msgstr "UDP transport" msgid "RTP (multicast) transport" msgstr "RTP (multicast) transport" msgid "Address" msgstr "Adresa" msgid "TTL" msgstr "TTL" msgid "Transmit always on" msgstr "Prenos vždy zapnutý" msgid "SAP announcements" msgstr "SAP oznámenia" msgid "Server announce broadcasts" msgstr "Server oznamuje vysielanie" msgid "HTTP transport for media files" msgstr "HTTP transport pre média" msgid "Additional network services" msgstr "Ďalšie sieťové služby" msgid "HTTP server" msgstr "HTTP server" msgid "HTTP clients can control VDR" msgstr "HTTP klienti môžu ovládať VDR" msgid "RTSP server" msgstr "RTSP server" msgid "RTSP clients can control VDR" msgstr "RTSP klienti môžu ovládať VDR" msgid "Playlist settings" msgstr "Nastavenie zoznamu stop" msgid "Show the track number" msgstr "Zobrazovať číslo stopy" msgid "Show the name of the artist" msgstr "Zobrazovať meno autora" msgid "Show the name of the album" msgstr "Zobrazovať názov albumu" msgid "Scan for metainfo" msgstr "Vyhľadávať metainfo" msgid "Cache metainfo" msgstr "Uchovávať metainfo" msgid "Arrow keys control DVD playback" msgstr "Šipkami ovládať prehrávanie DVD" msgid "Show hidden files" msgstr "Zobraziť skryté súbory" msgid "Allow removing files" msgstr "Povoliť zmazať súbory" msgid "Remember last playback position" msgstr "Pamätať si poslednú pozíciu prehrávania" msgid "Media Player" msgstr "Prehrávač médií" msgid "Play file" msgstr "Prehrať súbor" msgid "Play music" msgstr "Prehrať hudbu" msgid "View images" msgstr "Prehliadať obrázky" msgid "Play DVD disc" msgstr "Prehrať DVD" msgid "Play audio CD" msgstr "Prehrať zvukové CD" msgid "Play BluRay disc" msgstr "Prehrať BluRay" msgid "Video settings" msgstr "Nastavenie obrazu" msgid "Audio settings" msgstr "Nastavenie zvuku" msgid "Grayscale" msgstr "Odtiene šedej" msgid "Bitmap" msgstr "Rastr" msgid "OSD" msgstr "OSD" msgid "Test Images" msgstr "Skúšobné obrazce" msgid "Play movie title" msgstr "Prehrať titul filmu" msgid "Play disc" msgstr "Prehrať disk" msgid "Images" msgstr "Obrázky" msgid "Add to playlist" msgstr "Pridať do zoznamu stôp" msgid "Button$Queue" msgstr "Zoznam" msgid "Media" msgstr "Média" msgid "Play only audio" msgstr "Prehrávať iba zvuk" msgid "Audio equalizer" msgstr "Korekcia zvuku (ekvalizér)" msgid "Video aspect ratio" msgstr "Pomer strán obrazu" msgid "On" msgstr "Zapnuté" msgid "Default playlist not found" msgstr "Východzí zoznam stôp sa nenašiel" msgid "Default playlist is not symlink" msgstr "Predvolený zoznam stôp nie je symbolický odkaz" msgid "Default playlist not defined" msgstr "Predvolený zoznam stôp nie je určený" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput: klávesová skratka %s nie je priradená" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib výstupný modul" msgid "Frontend initialization failed" msgstr "Inicializácia rozhrania zlyhala" msgid "Server initialization failed" msgstr "Inicializácia serveru zlyhala" msgid "Playlist" msgstr "Zoznam stôp" msgid "Button$Random" msgstr "Náhodné" msgid "Button$Normal" msgstr "Normálne" msgid "Button$Add files" msgstr "Pridať súbory" msgid "Button$Remove" msgstr "Odstrániť" msgid "Button$Mark" msgstr "Značka" msgid "Queued to playlist" msgstr "Pridaný do zoznamu skladieb" msgid "Random play" msgstr "Náhodné prehrávanie" msgid "Normal play" msgstr "Normálne prehrávanie" msgid "DVD Menu" msgstr "DVD Menu" msgid "Exit DVD menu" msgstr "Opustiť DVD menu" msgid "DVD Root menu" msgstr "Hlavné DVD menu" msgid "DVD Title menu" msgstr "DVD menu titulov" msgid "DVD SPU menu" msgstr "DVD SPU menu" msgid "DVD Audio menu" msgstr "Zvukové menu DVD" msgid "Close menu" msgstr "Zavrieť menu" msgid "BluRay Top menu" msgstr "BluRay hlavné menu" msgid "Toggle Pop-Up menu" msgstr "Prepínač kontextového menu" msgid "Next title" msgstr "Ďalší titul" msgid "Previous title" msgstr "Predchádzajúci titul" msgid "Delete image ?" msgstr "Zmazať obrázok ?" #~ msgid "normal" #~ msgstr "normální" #~ msgid "inverted" #~ msgstr "inverzní" #~ msgid "Interlaced Field Order" #~ msgstr "Pořadí půlsnímků" #~ msgid "Button$Sort" #~ msgstr "Třídění" #~ msgid "Play file >>" #~ msgstr "Přehrát soubor >>" #~ msgid "Play music >>" #~ msgstr "Přehrát hudbu >>" #~ msgid "View images >>" #~ msgstr "Prohlížet obrázky >>" #~ msgid "Play DVD disc >>" #~ msgstr "Přehrát DVD >>" #~ msgid "Play audio CD >>" #~ msgstr "Přehrát zvukové CD >>" #~ msgid "Play BluRay disc >>" #~ msgstr "Přehrát BluRay >>" #~ msgid "Play remote DVD >>" #~ msgstr "Přehrát vzdálené DVD >>" #~ msgid "Play remote CD >>" #~ msgstr "Přehrát vzdálené CD >>" #~ msgid "Audio equalizer >>" #~ msgstr "Korekce zvuku (ekvalizér) >>" ������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/ru_RU.po���������������������������������������������������������������������0000644�0001750�0001750�00000025430�13061253422�014240� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # Vladimir Monchenko # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2007-11-23 10:17+0200\n" "Last-Translator: Vladimir Monchenko\n" "Language-Team: Russian <vdr@linuxtv.org>\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "custom" msgstr "Пользователь" msgid "tiny" msgstr "Очень маленький" msgid "small" msgstr "Маленький" msgid "medium" msgstr "Средний" msgid "large" msgstr "Большой" msgid "huge" msgstr "Очень больной" msgid "automatic" msgstr "Автоматически" msgid "default" msgstr "По умолчанию" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "" msgid "square" msgstr "" msgid "anamorphic" msgstr "" msgid "DVB" msgstr "" msgid "off" msgstr "Выкл." msgid "no audio" msgstr "Нет аудио" msgid "no video" msgstr "Нет видео" msgid "Off" msgstr "" msgid "Goom" msgstr "" msgid "Oscilloscope" msgstr "" msgid "FFT Scope" msgstr "" msgid "FFT Graph" msgstr "" msgid "Image" msgstr "" msgid "Mono 1.0" msgstr "" msgid "Stereo 2.0" msgstr "" msgid "Headphones 2.0" msgstr "" msgid "Stereo 2.1" msgstr "" msgid "Surround 3.0" msgstr "" msgid "Surround 4.0" msgstr "" msgid "Surround 4.1" msgstr "" msgid "Surround 5.0" msgstr "" msgid "Surround 5.1" msgstr "" msgid "Surround 6.0" msgstr "" msgid "Surround 6.1" msgstr "" msgid "Surround 7.1" msgstr "" msgid "Pass Through" msgstr "" msgid "very large" msgstr "" msgid "Software" msgstr "" msgid "Hardware" msgstr "" msgid "no" msgstr "" msgid "grayscale" msgstr "" msgid "transparent" msgstr "" msgid "transparent grayscale" msgstr "" msgid "yes" msgstr "" msgid "nearest" msgstr "" msgid "bilinear" msgstr "" msgid "LUT8" msgstr "" msgid "TrueColor" msgstr "" msgid "video stream" msgstr "" msgid "none" msgstr "" msgid "nonref" msgstr "" msgid "bidir" msgstr "" msgid "nonkey" msgstr "" msgid "all" msgstr "" msgid "Audio" msgstr "Аудио" msgid "Volume control" msgstr "" msgid "Delay" msgstr "Задержка" msgid "ms" msgstr "ms" msgid "Audio Compression" msgstr "Аудио компрессия" msgid "Upmix stereo to 5.1" msgstr "Преобразовать стерео в 5.1" msgid "Downmix AC3 to surround" msgstr "" msgid "Mix to headphones" msgstr "" msgid "Visualization" msgstr "Визуализация" msgid "Width" msgstr "" msgid "px" msgstr "пикселей" msgid "Height" msgstr "" msgid "Speed" msgstr "" msgid "fps" msgstr "" msgid "Background image MRL" msgstr "" msgid "Audio Equalizer" msgstr "Аудио эквалайзер" msgid "Use Video-Out Driver" msgstr "" msgid "vector" msgstr "" msgid "full" msgstr "" msgid "half (top)" msgstr "" msgid "half (bottom)" msgstr "" msgid "Video" msgstr "Видео" msgid "Aspect ratio" msgstr "" msgid "Crop letterbox 4:3 to 16:9" msgstr "" msgid "Use driver crop" msgstr "" msgid "Autodetect letterbox" msgstr "" msgid "Crop to" msgstr "" msgid "Autodetect rate" msgstr "" msgid "Stabilize time" msgstr "" msgid "Maximum logo width [%]" msgstr "" msgid "Overscan compensate [%1000]" msgstr "" msgid "Soft start" msgstr "" msgid "Soft start step" msgstr "" msgid "Detect subtitles" msgstr "" msgid "Subs detect stabilize time" msgstr "" msgid "Subs detect lifetime" msgstr "" msgid "Use avards analysis" msgstr "" msgid "Bar tone tolerance" msgstr "" msgid "Software scaling" msgstr "" msgid "Change aspect ratio" msgstr "" msgid "Change video size" msgstr "" msgid "Allow downscaling" msgstr "Масштабировать с падением качества" msgid "Overscan (crop image borders)" msgstr "" msgid "Post processing (ffmpeg)" msgstr "" msgid "Quality" msgstr "" msgid "Mode" msgstr "" msgid "Deinterlacing" msgstr "Деинтерлейсинг" msgid "Method" msgstr "" msgid "Cheap mode" msgstr "" msgid "Pulldown" msgstr "" msgid "Frame rate" msgstr "" msgid "Judder Correction" msgstr "" msgid "Use progressive frame flag" msgstr "" msgid "Chroma Filter" msgstr "" msgid "Sharpen / Blur" msgstr "" msgid "Width of the luma matrix" msgstr "" msgid "Height of the luma matrix" msgstr "" msgid "Amount of luma sharpness/blur" msgstr "" msgid "Width of the chroma matrix" msgstr "" msgid "Height of the chroma matrix" msgstr "" msgid "Amount of chroma sharpness/blur" msgstr "" msgid "3D Denoiser" msgstr "" msgid "Spatial luma strength" msgstr "" msgid "Spatial chroma strength" msgstr "" msgid "Temporal strength" msgstr "" msgid "HUE" msgstr "HUE" msgid "Saturation" msgstr "Насыщенность" msgid "Contrast" msgstr "Контрастность" msgid "Brightness" msgstr "Яркость" msgid "Sharpness" msgstr "" msgid "Noise Reduction" msgstr "" msgid "Smooth fast forward" msgstr "" msgid "Fastest trick speed" msgstr "" msgid "On-Screen Display" msgstr "Экранное меню" msgid "Hide main menu" msgstr "Скрыть основное меню" msgid "Resolution" msgstr "" msgid "Color depth" msgstr "" msgid "Blending method" msgstr "" msgid "Use hardware for low-res video" msgstr "" msgid "Scaling method" msgstr "" msgid "Scale subtitles" msgstr "" msgid "Show all layers" msgstr "" msgid "Dynamic transparency correction" msgstr "Динамическая коррекция прозрачности" msgid "Static transparency correction" msgstr "Статическая коррекция прозрачности" msgid "External subtitle size" msgstr "" msgid "DVB subtitle decoder" msgstr "" msgid "Decoder" msgstr "Декодер" msgid "Buffer size" msgstr "Размер буфера" msgid "Number of PES packets" msgstr "PES пакетов" msgid "Local Frontend" msgstr "Локальный фронтенд" msgid "Local Display Frontend" msgstr "Фронтенд локального экрана" msgid "Use keyboard" msgstr "Использовать клавиатуру" msgid "Driver" msgstr "Драйвер" msgid "Display address" msgstr "Адрес дисплея" msgid "Framebuffer device" msgstr "Framebuffer устройство" msgid "Fullscreen mode" msgstr "Полноэкранный режим" msgid "Window width" msgstr "Ширина окна" msgid "Window height" msgstr "Высота окна" msgid "Window aspect" msgstr "Соотношение сторон" msgid "Scale to window size" msgstr "Масштабировать в размер окна" msgid "Port" msgstr "Порт" msgid "Speakers" msgstr "" msgid "Remote Clients" msgstr "Удаленные клиенты" msgid "Allow remote clients" msgstr "Разрешить удаленных клиентов" msgid "Listen port (TCP and broadcast)" msgstr "Порт (TCP и широковешательный)" msgid "Listen address" msgstr "" msgid "Remote keyboard" msgstr "Удаленная клавиатура" msgid "Max number of clients" msgstr "" msgid "PIPE transport" msgstr "PIPE транспорт" msgid "TCP transport" msgstr "TCP транспорт" msgid "UDP transport" msgstr "UDP транспорт" msgid "RTP (multicast) transport" msgstr "RTP (широковещательный) транспорт" msgid "Address" msgstr "" msgid "TTL" msgstr "" msgid "Transmit always on" msgstr "" msgid "SAP announcements" msgstr "" msgid "Server announce broadcasts" msgstr "Сервер использует широковещание" msgid "HTTP transport for media files" msgstr "" msgid "Additional network services" msgstr "" msgid "HTTP server" msgstr "" msgid "HTTP clients can control VDR" msgstr "" msgid "RTSP server" msgstr "" msgid "RTSP clients can control VDR" msgstr "" msgid "Playlist settings" msgstr "" msgid "Show the track number" msgstr "" msgid "Show the name of the artist" msgstr "" msgid "Show the name of the album" msgstr "" msgid "Scan for metainfo" msgstr "" msgid "Cache metainfo" msgstr "" msgid "Arrow keys control DVD playback" msgstr "" msgid "Show hidden files" msgstr "" msgid "Allow removing files" msgstr "" msgid "Remember last playback position" msgstr "" msgid "Media Player" msgstr "Xine-lib" msgid "Play file" msgstr "Проиграть файл" msgid "Play music" msgstr "" msgid "View images" msgstr "Просмотреть изображения" msgid "Play DVD disc" msgstr "" msgid "Play audio CD" msgstr "" msgid "Play BluRay disc" msgstr "" msgid "Video settings" msgstr "" msgid "Audio settings" msgstr "" msgid "Grayscale" msgstr "Оттенки серого" msgid "Bitmap" msgstr "Битовая карта" msgid "OSD" msgstr "" msgid "Test Images" msgstr "Тестовые изображения" msgid "Play movie title" msgstr "" msgid "Play disc" msgstr "" msgid "Images" msgstr "Изображения" msgid "Add to playlist" msgstr "" msgid "Button$Queue" msgstr "" msgid "Media" msgstr "" msgid "Play only audio" msgstr "" msgid "Audio equalizer" msgstr "Аудио эквалайзер" msgid "Video aspect ratio" msgstr "" msgid "On" msgstr "" msgid "Default playlist not found" msgstr "" msgid "Default playlist is not symlink" msgstr "" msgid "Default playlist not defined" msgstr "" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib видео модуль" msgid "Frontend initialization failed" msgstr "" msgid "Server initialization failed" msgstr "" msgid "Playlist" msgstr "" msgid "Button$Random" msgstr "" msgid "Button$Normal" msgstr "" msgid "Button$Add files" msgstr "" msgid "Button$Remove" msgstr "" msgid "Button$Mark" msgstr "" msgid "Queued to playlist" msgstr "" msgid "Random play" msgstr "" msgid "Normal play" msgstr "" msgid "DVD Menu" msgstr "" msgid "Exit DVD menu" msgstr "" msgid "DVD Root menu" msgstr "" msgid "DVD Title menu" msgstr "" msgid "DVD SPU menu" msgstr "" msgid "DVD Audio menu" msgstr "" msgid "Close menu" msgstr "" msgid "BluRay Top menu" msgstr "" msgid "Toggle Pop-Up menu" msgstr "" msgid "Next title" msgstr "" msgid "Previous title" msgstr "" msgid "Delete image ?" msgstr "Удалить картинку ?" #~ msgid "normal" #~ msgstr "Нормальный" #~ msgid "inverted" #~ msgstr "Инвертировано" #~ msgid "Interlaced Field Order" #~ msgstr "Черезстрочный порядок полей" #~ msgid "Play file >>" #~ msgstr "Проиграть файл >>" #~ msgid "Play music >>" #~ msgstr "Проиграть файл >>" #~ msgid "View images >>" #~ msgstr "Просмотреть изображения >>" #~ msgid "Audio equalizer >>" #~ msgstr "Аудио эквалайзер >>" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/pl_PL.po���������������������������������������������������������������������0000644�0001750�0001750�00000032430�13061253422�014210� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # Tomasz Maciej Nowak, 2014 # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2014-11-16 20:03+0100\n" "Last-Translator: Tomasz Maciej Nowak <tomek_n@o2.pl>\n" "Language-Team: Polish <vdr@linuxtv.org>\n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.6.10\n" msgid "custom" msgstr "własny" msgid "tiny" msgstr "tycie" msgid "small" msgstr "małe" msgid "medium" msgstr "średnie" msgid "large" msgstr "duże" msgid "huge" msgstr "ogromne" msgid "automatic" msgstr "automatyczne" msgid "default" msgstr "standardowe" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "CenterCutOut" msgid "square" msgstr "kwadratowe" msgid "anamorphic" msgstr "anamorficzne" msgid "DVB" msgstr "DVB" msgid "off" msgstr "wyłączone" msgid "no audio" msgstr "brak dźwięku" msgid "no video" msgstr "brak obrazu" msgid "Off" msgstr "Wyłączone" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "Oscyloskop" msgid "FFT Scope" msgstr "Spektrum FFT" msgid "FFT Graph" msgstr "Graficzne FFT" msgid "Image" msgstr "Zdjęcie" msgid "Mono 1.0" msgstr "Mono 1.0" msgid "Stereo 2.0" msgstr "Stereo 2.0" msgid "Headphones 2.0" msgstr "Słuchawki 2.0" msgid "Stereo 2.1" msgstr "Stereo 2.1" msgid "Surround 3.0" msgstr "Surround 3.0" msgid "Surround 4.0" msgstr "Surround 4.0" msgid "Surround 4.1" msgstr "Surround 4.1" msgid "Surround 5.0" msgstr "Surround 5.0" msgid "Surround 5.1" msgstr "Surround 5.1" msgid "Surround 6.0" msgstr "Surround 6.0" msgid "Surround 6.1" msgstr "Surround 6.1" msgid "Surround 7.1" msgstr "Surround 7.1" msgid "Pass Through" msgstr "Przekaż dźwięk dalej" msgid "very large" msgstr "bardzo duże" msgid "Software" msgstr "Programowo" msgid "Hardware" msgstr "Sprzętowo" msgid "no" msgstr "nie" msgid "grayscale" msgstr "odcień szarości" msgid "transparent" msgstr "przeźroczyste" msgid "transparent grayscale" msgstr "przeźroczyste z odcieniem szarości" msgid "yes" msgstr "tak" msgid "nearest" msgstr "najbliższe" msgid "bilinear" msgstr "dwuliniowe" msgid "LUT8" msgstr "LUT8" msgid "TrueColor" msgstr "TrueColor" msgid "video stream" msgstr "strumień wideo" msgid "none" msgstr "żadne" msgid "nonref" msgstr "bez referencji" #, fuzzy msgid "bidir" msgstr "tylko ramki B" msgid "nonkey" msgstr "bez klucza" msgid "all" msgstr "wszystko" msgid "Audio" msgstr "Dźwięk" msgid "Volume control" msgstr "Kontrola głośności" msgid "Delay" msgstr "Opóźnienie" msgid "ms" msgstr "ms" msgid "Audio Compression" msgstr "Kompresja dźwięku" msgid "Upmix stereo to 5.1" msgstr "Miksuj stereo do 5.1" msgid "Downmix AC3 to surround" msgstr "Miksuj AC3 do Surround" msgid "Mix to headphones" msgstr "Przełącz na słuchawki" msgid "Visualization" msgstr "Wizualizacja" msgid "Width" msgstr "Szerokość" msgid "px" msgstr "px" msgid "Height" msgstr "Wysokość" msgid "Speed" msgstr "Prędkość" msgid "fps" msgstr "fps" msgid "Background image MRL" msgstr "MRL obrazu tła" msgid "Audio Equalizer" msgstr "Equalizer dźwięku" msgid "Use Video-Out Driver" msgstr "Użyj wyjścia Video-Out" msgid "vector" msgstr "wektor" msgid "full" msgstr "pełne" msgid "half (top)" msgstr "połowa (góra)" msgid "half (bottom)" msgstr "połowa (dół)" msgid "Video" msgstr "Obraz" msgid "Aspect ratio" msgstr "Proporcje obrazu" msgid "Crop letterbox 4:3 to 16:9" msgstr "Przytnij letterbox z 4:3 na 16:9" msgid "Use driver crop" msgstr "Użyj przycięcia" msgid "Autodetect letterbox" msgstr "Automatyczne wykrywanie Letterbox" msgid "Crop to" msgstr "Przytnij do" msgid "Autodetect rate" msgstr "Wykrywanie szybkości" msgid "Stabilize time" msgstr "Czas stabilizacji" msgid "Maximum logo width [%]" msgstr "Maksymalna szerokość loga [%]" msgid "Overscan compensate [%1000]" msgstr "Wyrównanie powiększenia [%1000]" msgid "Soft start" msgstr "Łagodny start" msgid "Soft start step" msgstr "Łagodny start przy przeskoku" msgid "Detect subtitles" msgstr "Wykryj napisy" #, fuzzy msgid "Subs detect stabilize time" msgstr "Czas stabilizacji napisów" #, fuzzy msgid "Subs detect lifetime" msgstr "Długość życia napisów" msgid "Use avards analysis" msgstr "Użyj analizy AVARDS" #, fuzzy msgid "Bar tone tolerance" msgstr "Tolerowanie kaszet" msgid "Software scaling" msgstr "Skalowanie programowe" msgid "Change aspect ratio" msgstr "Zmień proporcje" msgid "Change video size" msgstr "Zmień rozmiar obrazu" msgid "Allow downscaling" msgstr "Pozwól na zmniejszenie" msgid "Overscan (crop image borders)" msgstr "Przycięcie krawędzi obrazu" msgid "Post processing (ffmpeg)" msgstr "Przetwarzanie końcowe (ffmpeg)" msgid "Quality" msgstr "Jakość" msgid "Mode" msgstr "Tryb" msgid "Deinterlacing" msgstr "Usuwanie przeplotu" msgid "Method" msgstr "Metoda" msgid "Cheap mode" msgstr "Tanie" msgid "Pulldown" msgstr "Przez podział" msgid "Frame rate" msgstr "Zmiana ilości klatek na sekundę" msgid "Judder Correction" msgstr "Korekcja przesunięcia" msgid "Use progressive frame flag" msgstr "Użycie następnej flagi klatki" msgid "Chroma Filter" msgstr "Filter chrominancji" msgid "Sharpen / Blur" msgstr "Wyostrz / Rozmyj" msgid "Width of the luma matrix" msgstr "Szerokość matrycy luma" msgid "Height of the luma matrix" msgstr "Wysokość matrycy luma" msgid "Amount of luma sharpness/blur" msgstr "Suma wyostrzeń/rozmyć lumy" msgid "Width of the chroma matrix" msgstr "Szerokość matrycy chroma" msgid "Height of the chroma matrix" msgstr "Wysokość matrycy chroma" msgid "Amount of chroma sharpness/blur" msgstr "Suma wyostrzeń/rozmyć chromy" msgid "3D Denoiser" msgstr "Odszumiacz 3D" msgid "Spatial luma strength" msgstr "Przestrzenna siła lumy" msgid "Spatial chroma strength" msgstr "Przestrzenna siła chromy" msgid "Temporal strength" msgstr "Siła czasowego" msgid "HUE" msgstr "Ton kolorów" msgid "Saturation" msgstr "Nasycenie" msgid "Contrast" msgstr "Kontrast" msgid "Brightness" msgstr "Jasność" msgid "Sharpness" msgstr "Ostrość" msgid "Noise Reduction" msgstr "Redukcja szumu" msgid "Smooth fast forward" msgstr "Płynne przewijanie do przodu" #, fuzzy msgid "Fastest trick speed" msgstr "Prędkość postępu" msgid "On-Screen Display" msgstr "On-Screen Display" msgid "Hide main menu" msgstr "Ukryj pozycję w głównym menu" msgid "Resolution" msgstr "Rozdzielczość" msgid "Color depth" msgstr "Głębia koloru" msgid "Blending method" msgstr "Metoda wygładzania" msgid "Use hardware for low-res video" msgstr "Użyj sprzętu dla obrazu o niskiej jakości" msgid "Scaling method" msgstr "Metoda skalowania" msgid "Scale subtitles" msgstr "Skaluj napisy" msgid "Show all layers" msgstr "Pokaż wszystkie warstwy" msgid "Dynamic transparency correction" msgstr "Dynamiczna korekcja przeźroczystości" msgid "Static transparency correction" msgstr "Statyczna korekcja przeźroczystości" msgid "External subtitle size" msgstr "Rozmiar zewnętrznych napisów" msgid "DVB subtitle decoder" msgstr "Dekoder napisów DVB" msgid "Decoder" msgstr "Dekoder" msgid "Buffer size" msgstr "Rozmiar bufora" msgid "Number of PES packets" msgstr "Liczba pakietów PES" msgid "Local Frontend" msgstr "Klienci lokalni" msgid "Local Display Frontend" msgstr "Lokalny klient emisji" msgid "Use keyboard" msgstr "Używaj klawiatury" msgid "Driver" msgstr "Sterownik" msgid "Display address" msgstr "Adres wyświetlacza" msgid "Framebuffer device" msgstr "Urządzenie Framebuffer" msgid "Fullscreen mode" msgstr "Pełny ekran" msgid "Window width" msgstr "Szerokość okna" msgid "Window height" msgstr "Wysokość okna" msgid "Window aspect" msgstr "Proporcje okna" msgid "Scale to window size" msgstr "Skaluj do rozmiaru okna" msgid "Port" msgstr "Port" msgid "Speakers" msgstr "Głośniki" msgid "Remote Clients" msgstr "Zdalni klienci" msgid "Allow remote clients" msgstr "Zezwól na zdalnych klientów" msgid "Listen port (TCP and broadcast)" msgstr "Port nasłuchu (TCP i rozgłaszanie)" msgid "Listen address" msgstr "Adres nasłuchiwania" msgid "Remote keyboard" msgstr "Zdalna klawiatura" msgid "Max number of clients" msgstr "Maksymalna liczba klientów" msgid "PIPE transport" msgstr "Transport PIPE" msgid "TCP transport" msgstr "Transport TCP" msgid "UDP transport" msgstr "Transport UDP" msgid "RTP (multicast) transport" msgstr "Transport RTP (multicast)" msgid "Address" msgstr "Adres (multicast)" msgid "TTL" msgstr "TTL (multicast)" msgid "Transmit always on" msgstr "Ciągła transmisja" msgid "SAP announcements" msgstr "Obwieszczenie SAP" msgid "Server announce broadcasts" msgstr "Serwer obwieszcza nadawanie" msgid "HTTP transport for media files" msgstr "Transport HTTP dla multimediów" msgid "Additional network services" msgstr "Dodatkowe serwisy sieciowe" msgid "HTTP server" msgstr "Serwer HTTP" msgid "HTTP clients can control VDR" msgstr "Klienci HTTP mogą kontrolować VDR" msgid "RTSP server" msgstr "Serwer RTSP" msgid "RTSP clients can control VDR" msgstr "Klienci RTSP mogą kontrolować VDR" msgid "Playlist settings" msgstr "Ustawienia listy odtwarzania" msgid "Show the track number" msgstr "Pokaż numer ścieżki" msgid "Show the name of the artist" msgstr "Pokaż nazwę artysty" msgid "Show the name of the album" msgstr "Pokaż nazwę albumu" msgid "Scan for metainfo" msgstr "Skanuj informacje meta" msgid "Cache metainfo" msgstr "Buforuj informacje meta" msgid "Arrow keys control DVD playback" msgstr "Strzałki kontrolują odtwarzanie DVD" msgid "Show hidden files" msgstr "Pokazuj ukryte pliki" msgid "Allow removing files" msgstr "Pozwól na usuwanie plików" msgid "Remember last playback position" msgstr "Pamiętaj miejsce zatrzymania odtwarzania" msgid "Media Player" msgstr "Odtwarzacz multimediów" msgid "Play file" msgstr "Odtwarzaj plik" msgid "Play music" msgstr "Odtwarzaj muzykę" msgid "View images" msgstr "Wyświetlaj zdjęcia" msgid "Play DVD disc" msgstr "Odtwarzaj DVD" msgid "Play audio CD" msgstr "Odtwarzaj CD" msgid "Play BluRay disc" msgstr "Odtwarzaj BluRay" msgid "Video settings" msgstr "Ustawienia obrazu" msgid "Audio settings" msgstr "Ustawienia dźwięku" msgid "Grayscale" msgstr "Odcień szarości" msgid "Bitmap" msgstr "Bitmapa" msgid "OSD" msgstr "OSD" msgid "Test Images" msgstr "Obrazy testowe" msgid "Play movie title" msgstr "Odtwarzaj film" msgid "Play disc" msgstr "Odtwarzaj dysk" msgid "Images" msgstr "Zdjęcia" msgid "Add to playlist" msgstr "Dodaj do listy odtwarzania" msgid "Button$Queue" msgstr "Zakolejkuj" msgid "Media" msgstr "Media" msgid "Play only audio" msgstr "Odtwarzaj tylko dźwięk" msgid "Audio equalizer" msgstr "Equalizer dźwięku" msgid "Video aspect ratio" msgstr "Proporcje obrazu wideo" msgid "On" msgstr "Włączone" msgid "Default playlist not found" msgstr "Nie odnaleziono standardowej listy odtwarzania" msgid "Default playlist is not symlink" msgstr "Standardowa lista odtwarzania to nie symlink" msgid "Default playlist not defined" msgstr "Nie zdefiniowano standardowej listy odtwarzania" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput: klawisz %s nie jest przypisany" msgid "X11/xine-lib output plugin" msgstr "Emisja poprzez X11/xine-lib" msgid "Frontend initialization failed" msgstr "Inicjalizacja klienta nie powiodła się" msgid "Server initialization failed" msgstr "Inicjalizacja serwera nie powiodła się" msgid "Playlist" msgstr "Lista odtwarzania" msgid "Button$Random" msgstr "Losowo" msgid "Button$Normal" msgstr "Standardowe" msgid "Button$Add files" msgstr "Dodaj pliki" msgid "Button$Remove" msgstr "Usuń" msgid "Button$Mark" msgstr "Zaznacz" msgid "Queued to playlist" msgstr "Dodane do listy odtwarzania" msgid "Random play" msgstr "Odtwarzanie losowe" msgid "Normal play" msgstr "Odtwarzanie" msgid "DVD Menu" msgstr "Menu DVD" msgid "Exit DVD menu" msgstr "Wyjdź z menu DVD" msgid "DVD Root menu" msgstr "Menu główne DVD" msgid "DVD Title menu" msgstr "Menu tytułów DVD" msgid "DVD SPU menu" msgstr "Menu SPU DVD" msgid "DVD Audio menu" msgstr "Menu dźwięku DVD" msgid "Close menu" msgstr "Zamknij menu" msgid "BluRay Top menu" msgstr "Menu główne BluRay" msgid "Toggle Pop-Up menu" msgstr "Przełącz na menu Pop-Up" msgid "Next title" msgstr "Następny tytuł" msgid "Previous title" msgstr "Poprzedni tytuł" msgid "Delete image ?" msgstr "Usunąć zdjęcie?" #~ msgid "normal" #~ msgstr "Normalne" #~ msgid "inverted" #~ msgstr "Przestawny" #~ msgid "Interlaced Field Order" #~ msgstr "Interlaced Halbbild-Reihenfolge" #~ msgid "Button$Sort" #~ msgstr "Sortuj" #~ msgid "Play file >>" #~ msgstr "Odtwarzaj plik >>" #~ msgid "Play music >>" #~ msgstr "Odtwarzaj muzykę >>" #~ msgid "View images >>" #~ msgstr "Wyświetlaj zdjęcia >>" #~ msgid "Play DVD disc >>" #~ msgstr "Odtwarzaj DVD >>" #~ msgid "Play audio CD >>" #~ msgstr "Odtwarzaj CD >>" #~ msgid "Play BluRay disc >>" #~ msgstr "Odtwarzaj BluRay >>" #~ msgid "Play remote DVD >>" #~ msgstr "Odtwarzaj zdalnie DVD >>" #~ msgid "Play remote CD >>" #~ msgstr "Odtwarzaj CD >>" #~ msgid "Audio equalizer >>" #~ msgstr "Equalizer dźwięku >>" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/it_IT.po���������������������������������������������������������������������0000644�0001750�0001750�00000032035�13061253422�014213� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2011-07-10 02:27+0100\n" "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" "Language-Team: Italian <vdr@linuxtv.org>\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Italian\n" "X-Poedit-Country: ITALY\n" "X-Poedit-SourceCharset: utf-8\n" msgid "custom" msgstr "personalizza" msgid "tiny" msgstr "molto piccolo" msgid "small" msgstr "piccolo" msgid "medium" msgstr "medio" msgid "large" msgstr "grande" msgid "huge" msgstr "enorme" msgid "automatic" msgstr "automatica" msgid "default" msgstr "predefinita" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "CenterCutOut" msgid "square" msgstr "quadrato" msgid "anamorphic" msgstr "anamorfico" msgid "DVB" msgstr "DVB" msgid "off" msgstr "spento" msgid "no audio" msgstr "niente audio" msgid "no video" msgstr "niente video" msgid "Off" msgstr "Spento" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "Oscilloscopio" msgid "FFT Scope" msgstr "Spettro FFT" msgid "FFT Graph" msgstr "Grafico FFT" msgid "Image" msgstr "" msgid "Mono 1.0" msgstr "Mono 1.0" msgid "Stereo 2.0" msgstr "Stereo 2.0" msgid "Headphones 2.0" msgstr "Cuffie 2.0" msgid "Stereo 2.1" msgstr "Stereo 2.1" msgid "Surround 3.0" msgstr "Surround 3.0" msgid "Surround 4.0" msgstr "Surround 4.0" msgid "Surround 4.1" msgstr "Surround 4.1" msgid "Surround 5.0" msgstr "Surround 5.0" msgid "Surround 5.1" msgstr "Surround 5.1" msgid "Surround 6.0" msgstr "Surround 6.0" msgid "Surround 6.1" msgstr "Surround 6.1" msgid "Surround 7.1" msgstr "Surround 7.1" msgid "Pass Through" msgstr "Pass Through" msgid "very large" msgstr "molto grande" msgid "Software" msgstr "Software" msgid "Hardware" msgstr "Hardware" msgid "no" msgstr "no" msgid "grayscale" msgstr "scala di grigi" msgid "transparent" msgstr "trasparente" msgid "transparent grayscale" msgstr "scala di grigi trasparente" msgid "yes" msgstr "sì" msgid "nearest" msgstr "più vicino" msgid "bilinear" msgstr "bilineare" msgid "LUT8" msgstr "" msgid "TrueColor" msgstr "" msgid "video stream" msgstr "flusso video" msgid "none" msgstr "nessuno" msgid "nonref" msgstr "nessun riferimento" msgid "bidir" msgstr "bidirezionale" msgid "nonkey" msgstr "nessuna chiave" msgid "all" msgstr "tutti" msgid "Audio" msgstr "Audio" msgid "Volume control" msgstr "Controllo volume" msgid "Delay" msgstr "Ritardo" msgid "ms" msgstr "ms" msgid "Audio Compression" msgstr "Compressione audio" msgid "Upmix stereo to 5.1" msgstr "Suono da Stereo a 5.1" msgid "Downmix AC3 to surround" msgstr "Suono da AC3 a Surround" msgid "Mix to headphones" msgstr "Suono a cuffie" msgid "Visualization" msgstr "Visualizzazione" msgid "Width" msgstr "Larghezza" msgid "px" msgstr "px" msgid "Height" msgstr "Altezza" msgid "Speed" msgstr "Velocità" msgid "fps" msgstr "fps" msgid "Background image MRL" msgstr "" msgid "Audio Equalizer" msgstr "Equalizzatore audio" msgid "Use Video-Out Driver" msgstr "Utilizza driver uscita video" msgid "vector" msgstr "vettoriale" msgid "full" msgstr "intero" msgid "half (top)" msgstr "metà (superiore)" msgid "half (bottom)" msgstr "metà (inferiore)" msgid "Video" msgstr "Video" msgid "Aspect ratio" msgstr "Formato" msgid "Crop letterbox 4:3 to 16:9" msgstr "Ritaglia letterbox 4:3 a 16:9" msgid "Use driver crop" msgstr "Utilizza driver taglio" msgid "Autodetect letterbox" msgstr "Rileva letterbox in automatico" msgid "Crop to" msgstr "Ritaglia a" msgid "Autodetect rate" msgstr "Rileva frequenza in automatico" msgid "Stabilize time" msgstr "Ora stabilizzazione" msgid "Maximum logo width [%]" msgstr "Larghezza massima logo [%]" msgid "Overscan compensate [%1000]" msgstr "Compensazione Overscan [%1000]" msgid "Soft start" msgstr "Avvio leggero" msgid "Soft start step" msgstr "Passo avvio lento" msgid "Detect subtitles" msgstr "Rileva sottotitoli" msgid "Subs detect stabilize time" msgstr "Rileva ora stabilizzazione sottotitoli" msgid "Subs detect lifetime" msgstr "Rileva durata sottotitoli" msgid "Use avards analysis" msgstr "Utilizza analisi avards" msgid "Bar tone tolerance" msgstr "Tolleranza tonalità pan." msgid "Software scaling" msgstr "Ridimensionamento software" msgid "Change aspect ratio" msgstr "Cambia formato video" msgid "Change video size" msgstr "Cambia dimensione video" msgid "Allow downscaling" msgstr "Permetti ridimensionamento" msgid "Overscan (crop image borders)" msgstr "Overscan (ritaglia bordi immagine)" msgid "Post processing (ffmpeg)" msgstr "Codifica (ffmpeg)" msgid "Quality" msgstr "Qualità" msgid "Mode" msgstr "Modalità" msgid "Deinterlacing" msgstr "Deinterlacciamento" msgid "Method" msgstr "Metodo" msgid "Cheap mode" msgstr "Modalità economica" msgid "Pulldown" msgstr "Pulldown" msgid "Frame rate" msgstr "Frame rate" msgid "Judder Correction" msgstr "Correzione gamma" msgid "Use progressive frame flag" msgstr "Utilizza flag frame progressivo" msgid "Chroma Filter" msgstr "Filtro Chroma" msgid "Sharpen / Blur" msgstr "Nitido / Blur" msgid "Width of the luma matrix" msgstr "Larghezza della matrice luma" msgid "Height of the luma matrix" msgstr "Altezza della matrice luma" msgid "Amount of luma sharpness/blur" msgstr "Valore di nitidezza/blur luma" msgid "Width of the chroma matrix" msgstr "Larghezza della matrice chroma" msgid "Height of the chroma matrix" msgstr "Altezza della matrice chroma" msgid "Amount of chroma sharpness/blur" msgstr "Valore di nitidezza/blur chroma" msgid "3D Denoiser" msgstr "Denoiser 3D" msgid "Spatial luma strength" msgstr "Resistenza luma spaziale" msgid "Spatial chroma strength" msgstr "Resistenza chroma spaziale" msgid "Temporal strength" msgstr "Resistenza temporale" msgid "HUE" msgstr "Tonalità" msgid "Saturation" msgstr "Saturazione" msgid "Contrast" msgstr "Contrasto" msgid "Brightness" msgstr "Luminosità" msgid "Sharpness" msgstr "Nitidezza" msgid "Noise Reduction" msgstr "Riduzione rumore" msgid "Smooth fast forward" msgstr "Avanzamento veloce leggero" msgid "Fastest trick speed" msgstr "Trucco velocità più rapida" msgid "On-Screen Display" msgstr "Messaggi in sovrimpressione (OSD)" msgid "Hide main menu" msgstr "Nascondi voce menu principale" msgid "Resolution" msgstr "Risoluzione" msgid "Color depth" msgstr "" msgid "Blending method" msgstr "Metodo di sfocatura" msgid "Use hardware for low-res video" msgstr "Utilizza hardware per video bassa risoluzione" msgid "Scaling method" msgstr "Metodo ridimensione" msgid "Scale subtitles" msgstr "Scala sottotitoli" msgid "Show all layers" msgstr "Mostra tutti i livelli" msgid "Dynamic transparency correction" msgstr "Correzione trasparenza dinamica" msgid "Static transparency correction" msgstr "Correzione trasparenza statica" msgid "External subtitle size" msgstr "Dimensione sottotitoli esterni" msgid "DVB subtitle decoder" msgstr "Decoder sottotitoli DVB" msgid "Decoder" msgstr "Decoder" msgid "Buffer size" msgstr "Dimensione buffer" msgid "Number of PES packets" msgstr "Numero di pacchetti PES" msgid "Local Frontend" msgstr "Frontend locale" msgid "Local Display Frontend" msgstr "Frontend visualizzazione locale" msgid "Use keyboard" msgstr "Utilizza tastiera" msgid "Driver" msgstr "Driver" msgid "Display address" msgstr "Mostra indirizzo" msgid "Framebuffer device" msgstr "Periferica framebuffer" msgid "Fullscreen mode" msgstr "Mod. schermo intero" msgid "Window width" msgstr "Larghezza finestra" msgid "Window height" msgstr "Altezza finestra" msgid "Window aspect" msgstr "Aspetto finestra" msgid "Scale to window size" msgstr "Scala a dimensione finestra" msgid "Port" msgstr "Porta" msgid "Speakers" msgstr "Altoparlanti" msgid "Remote Clients" msgstr "Client remoti" msgid "Allow remote clients" msgstr "Permetti client remoti" msgid "Listen port (TCP and broadcast)" msgstr "Porta in ascolto (TCP e trasmissione)" msgid "Listen address" msgstr "Indirizzo in ascolto" msgid "Remote keyboard" msgstr "Tastiera remota" msgid "Max number of clients" msgstr "Numero massimo di client" msgid "PIPE transport" msgstr "Protocollo PIPE" msgid "TCP transport" msgstr "Protocollo TCP" msgid "UDP transport" msgstr "Protocollo UDP" msgid "RTP (multicast) transport" msgstr "Protocollo RTP (multicast)" msgid "Address" msgstr "Indirizzo" msgid "TTL" msgstr "TTL" msgid "Transmit always on" msgstr "Trasmetti sempre" msgid "SAP announcements" msgstr "Annunci SAP" msgid "Server announce broadcasts" msgstr "Annuncio trasmissioni dal server" msgid "HTTP transport for media files" msgstr "Protocollo HTTP per file multimediali" msgid "Additional network services" msgstr "Ulteriori servizi di rete" msgid "HTTP server" msgstr "Server HTTP" msgid "HTTP clients can control VDR" msgstr "I client HTTP possono controllare VDR" msgid "RTSP server" msgstr "Server RTSP" msgid "RTSP clients can control VDR" msgstr "I client RTSP possono controllare VDR" msgid "Playlist settings" msgstr "Impostazioni lista esec." msgid "Show the track number" msgstr "Mostra il numero della traccia" msgid "Show the name of the artist" msgstr "Mostra il nome dell'artista" msgid "Show the name of the album" msgstr "Mostra il nome dell'album" msgid "Scan for metainfo" msgstr "Scansione metainfo" msgid "Cache metainfo" msgstr "Cache metainfo" msgid "Arrow keys control DVD playback" msgstr "Riproduzione DVD con tasti freccia" msgid "Show hidden files" msgstr "" msgid "Allow removing files" msgstr "" msgid "Remember last playback position" msgstr "" msgid "Media Player" msgstr "Lettore multimediale" msgid "Play file" msgstr "Riproduci file" msgid "Play music" msgstr "Riproduci musica" msgid "View images" msgstr "Visualizza immagini" msgid "Play DVD disc" msgstr "Riproduci disco DVD" msgid "Play audio CD" msgstr "Riproduci CD audio" msgid "Play BluRay disc" msgstr "Riproduci disco BluRay" msgid "Video settings" msgstr "Impostazioni video" msgid "Audio settings" msgstr "Impostazioni audio" msgid "Grayscale" msgstr "Scala di grigi" msgid "Bitmap" msgstr "Bitmap" msgid "OSD" msgstr "OSD" msgid "Test Images" msgstr "Prova immagini" msgid "Play movie title" msgstr "" msgid "Play disc" msgstr "" msgid "Images" msgstr "Immagini" msgid "Add to playlist" msgstr "Aggiungi alla lista esec." msgid "Button$Queue" msgstr "Coda" msgid "Media" msgstr "Media" msgid "Play only audio" msgstr "Riproduci solo audio" msgid "Audio equalizer" msgstr "Equalizzatore audio" msgid "Video aspect ratio" msgstr "Formato video" msgid "On" msgstr "Attivo" msgid "Default playlist not found" msgstr "Lista esec. predefinita non trovata" msgid "Default playlist is not symlink" msgstr "La lista esec. predefinita non è un link simbolico" msgid "Default playlist not defined" msgstr "Lista esec. predefinita non definita" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput: tasto %s non associato" msgid "X11/xine-lib output plugin" msgstr "Plugin uscita X11/xine-lib" msgid "Frontend initialization failed" msgstr "Inizializzazione frontend fallita" msgid "Server initialization failed" msgstr "Inizializzazione server fallita" msgid "Playlist" msgstr "Lista esecuzione" msgid "Button$Random" msgstr "Casuale" msgid "Button$Normal" msgstr "Normale" msgid "Button$Add files" msgstr "Aggiungi files" msgid "Button$Remove" msgstr "Rimuovi" msgid "Button$Mark" msgstr "" msgid "Queued to playlist" msgstr "Accoda alla lista esecuzione" msgid "Random play" msgstr "Riproduzione casuale" msgid "Normal play" msgstr "Riproduzione normale" msgid "DVD Menu" msgstr "Menu DVD" msgid "Exit DVD menu" msgstr "Esci dal menu DVD" msgid "DVD Root menu" msgstr "Menu principale DVD" msgid "DVD Title menu" msgstr "Titolo menu DVD" msgid "DVD SPU menu" msgstr "SPU menu DVD" msgid "DVD Audio menu" msgstr "Audio menu DVD" msgid "Close menu" msgstr "Chiudi menu" msgid "BluRay Top menu" msgstr "" msgid "Toggle Pop-Up menu" msgstr "" msgid "Next title" msgstr "" msgid "Previous title" msgstr "" msgid "Delete image ?" msgstr "Eliminare immagine ?" #~ msgid "normal" #~ msgstr "normale" #~ msgid "inverted" #~ msgstr "invertito" #~ msgid "Interlaced Field Order" #~ msgstr "Ordine campo interlacciato" #~ msgid "Button$Sort" #~ msgstr "Ordina" #~ msgid "Play file >>" #~ msgstr "Riproduci file >>" #~ msgid "Play music >>" #~ msgstr "Riproduci musica >>" #~ msgid "View images >>" #~ msgstr "Visualizza immagini >>" #~ msgid "Play DVD disc >>" #~ msgstr "Riproduci disco DVD >>" #~ msgid "Play audio CD >>" #~ msgstr "Riproduci CD audio >>" #~ msgid "Play BluRay disc >>" #~ msgstr "Riproduci disco BluRay >>" #~ msgid "Play remote DVD >>" #~ msgstr "Riproduci DVD remoto >>" #~ msgid "Play remote CD >>" #~ msgstr "Riproduci CD remoto >>" #~ msgid "Headphone audio mode" #~ msgstr "Modalità cuffie audio" #~ msgid "Audio equalizer >>" #~ msgstr "Equalizzatore audio >>" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/fi_FI.po���������������������������������������������������������������������0000644�0001750�0001750�00000032177�13061253422�014166� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # Petri Hintukainen # Rolf Ahrenberg # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2008-10-06 11:19+0200\n" "Last-Translator: Rolf Ahrenberg\n" "Language-Team: Finnish <vdr@linuxtv.org>\n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "custom" msgstr "oma" msgid "tiny" msgstr "olematon" msgid "small" msgstr "pieni" msgid "medium" msgstr "keskikokoinen" msgid "large" msgstr "suuri" msgid "huge" msgstr "valtava" msgid "automatic" msgstr "automaattinen" msgid "default" msgstr "oletus" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "CenterCutOut" msgid "square" msgstr "neliö" msgid "anamorphic" msgstr "anamorfinen" msgid "DVB" msgstr "DVB" msgid "off" msgstr "ei käytössä" msgid "no audio" msgstr "ei ääntä" msgid "no video" msgstr "ei kuvaa" msgid "Off" msgstr "ei käytössä" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "oskilloskooppi" msgid "FFT Scope" msgstr "spektri" msgid "FFT Graph" msgstr "spektrogrammi" msgid "Image" msgstr "Kuva" msgid "Mono 1.0" msgstr "Mono 1.0" msgid "Stereo 2.0" msgstr "Stereo 2.0" msgid "Headphones 2.0" msgstr "Kuulokkeet 2.0" msgid "Stereo 2.1" msgstr "Stereo 2.1" msgid "Surround 3.0" msgstr "Surround 3.0" msgid "Surround 4.0" msgstr "Surround 4.0" msgid "Surround 4.1" msgstr "Surround 4.1" msgid "Surround 5.0" msgstr "Surround 5.0" msgid "Surround 5.1" msgstr "Surround 5.1" msgid "Surround 6.0" msgstr "Surround 6.0" msgid "Surround 6.1" msgstr "Surround 6.1" msgid "Surround 7.1" msgstr "Surround 7.1" msgid "Pass Through" msgstr "läpivienti" msgid "very large" msgstr "erittäin suuri" msgid "Software" msgstr "ohjelmallisesti" msgid "Hardware" msgstr "laitteistolla" msgid "no" msgstr "ei" msgid "grayscale" msgstr "harmaasävy" msgid "transparent" msgstr "läpinäkyvä" msgid "transparent grayscale" msgstr "läpinäkyvä harmaasävy" msgid "yes" msgstr "kyllä" msgid "nearest" msgstr "lähin" msgid "bilinear" msgstr "bilineaarinen" msgid "LUT8" msgstr "LUT8" msgid "TrueColor" msgstr "TrueColor" msgid "video stream" msgstr "videolähete" msgid "none" msgstr "ei" msgid "nonref" msgstr "ei referoituja" msgid "bidir" msgstr "vain B-ruudut" msgid "nonkey" msgstr "ei avainruutuja" msgid "all" msgstr "kaikki" msgid "Audio" msgstr "Ääni" msgid "Volume control" msgstr "Äänenvoimakkuuden säätö" msgid "Delay" msgstr "Viive" msgid "ms" msgstr "ms" msgid "Audio Compression" msgstr "Voimista hiljaisia ääniä" msgid "Upmix stereo to 5.1" msgstr "Miksaa stereoääni 5.1-kanavaiseksi" msgid "Downmix AC3 to surround" msgstr "Miksaa AC3-ääni surroundiksi" msgid "Mix to headphones" msgstr "Miksaa kuulokkeille" msgid "Visualization" msgstr "Visualisointi" msgid "Width" msgstr "Leveys" msgid "px" msgstr "px" msgid "Height" msgstr "Korkeus" msgid "Speed" msgstr "Nopeus" msgid "fps" msgstr "fps" msgid "Background image MRL" msgstr "Taustakuvan MRL" msgid "Audio Equalizer" msgstr "Taajuuskorjain" msgid "Use Video-Out Driver" msgstr "videoajuri" msgid "vector" msgstr "vektoroitu" msgid "full" msgstr "täysi" msgid "half (top)" msgstr "alempi puolisko" msgid "half (bottom)" msgstr "ylempi puolisko" msgid "Video" msgstr "Kuva" msgid "Aspect ratio" msgstr "Kuvasuhde" msgid "Crop letterbox 4:3 to 16:9" msgstr "Leikkaa 4:3-letterbox 16:9:ksi" msgid "Use driver crop" msgstr "Leikkaa videoajurilla" msgid "Autodetect letterbox" msgstr "Tunnista letterbox automaattisesti" msgid "Crop to" msgstr "Leikkaa kokoon" msgid "Autodetect rate" msgstr "Tunnistusnopeus" msgid "Stabilize time" msgstr "Stabilisointiaika" msgid "Maximum logo width [%]" msgstr "Suurin logon leveys [%]" msgid "Overscan compensate [%1000]" msgstr "Overscan-kompensointi [%1000]" msgid "Soft start" msgstr "Portaittainen aloitus" msgid "Soft start step" msgstr "Portaittaisen aloituksen askel" msgid "Detect subtitles" msgstr "Huomioi tekstitys" msgid "Subs detect stabilize time" msgstr "Stabilisointiaika tekstitykselle" msgid "Subs detect lifetime" msgstr "Elinaika tekstitykselle" msgid "Use avards analysis" msgstr "Analysoi kuvasuhde automaattisesti (avards)" msgid "Bar tone tolerance" msgstr "Toleranssi palkkien värisävylle" msgid "Software scaling" msgstr "Skaalaa ohjelmistolla" msgid "Change aspect ratio" msgstr "Muuta kuvasuhdetta" msgid "Change video size" msgstr "Muuta videokuvan kokoa" msgid "Allow downscaling" msgstr "Salli skaalaus pienemmäksi" msgid "Overscan (crop image borders)" msgstr "Leikkaa kuvan reunoja (overscan)" msgid "Post processing (ffmpeg)" msgstr "Käytä jälkikäsittelyä (ffmpeg)" msgid "Quality" msgstr "Laatu" msgid "Mode" msgstr "Moodi" msgid "Deinterlacing" msgstr "Lomituksen poisto" msgid "Method" msgstr "Menetelmä" msgid "Cheap mode" msgstr "Käytä Cheap-moodia" msgid "Pulldown" msgstr "Pulldown-moodi" msgid "Frame rate" msgstr "Ruudunpäivitys" msgid "Judder Correction" msgstr "Käytä tärinänkorjausta" msgid "Use progressive frame flag" msgstr "Tunnista progressiivinen kuva" msgid "Chroma Filter" msgstr "Käytä Chroma-suodinta" msgid "Sharpen / Blur" msgstr "Terävöinti / sumennus" msgid "Width of the luma matrix" msgstr "Luma-matriisin leveys" msgid "Height of the luma matrix" msgstr "Luma-matriisin korkeus" msgid "Amount of luma sharpness/blur" msgstr "Luma-terävöinti/-sumennus" msgid "Width of the chroma matrix" msgstr "Chroma-matriisin leveys" msgid "Height of the chroma matrix" msgstr "Chroma-matriisin korkeus" msgid "Amount of chroma sharpness/blur" msgstr "Chroma-terävöinti/-sumennus" msgid "3D Denoiser" msgstr "3D-kohinanpoisto" msgid "Spatial luma strength" msgstr "Luman tilavoimakkuus" msgid "Spatial chroma strength" msgstr "Chroman tilavoimakkuus" msgid "Temporal strength" msgstr "Ajallinen voimakkuus" msgid "HUE" msgstr "Värisävy" msgid "Saturation" msgstr "Saturaatio" msgid "Contrast" msgstr "Kontrasti" msgid "Brightness" msgstr "Kirkkaus" msgid "Sharpness" msgstr "Terävöinti" msgid "Noise Reduction" msgstr "Kohinanpoisto" msgid "Smooth fast forward" msgstr "Tasainen kuvakelaus" msgid "Fastest trick speed" msgstr "Suurin kelausnopeus" msgid "On-Screen Display" msgstr "Kuvaruutunäyttö" msgid "Hide main menu" msgstr "Piilota valinta päävalikossa" msgid "Resolution" msgstr "Tarkkuus" msgid "Color depth" msgstr "Värisyvyys" msgid "Blending method" msgstr "Piirtotapa" msgid "Use hardware for low-res video" msgstr "Laitteisto matalaresoluutioisella videolla" msgid "Scaling method" msgstr "Skaalaustapa" msgid "Scale subtitles" msgstr "Skaalaa tekstitys" msgid "Show all layers" msgstr "Näytä kaikki kerrokset" msgid "Dynamic transparency correction" msgstr "Dynaaminen läpinäkyvyyden korjaus" msgid "Static transparency correction" msgstr "Läpinäkyvyyden korjaus" msgid "External subtitle size" msgstr "Erillisen tekstityksen koko" msgid "DVB subtitle decoder" msgstr "DVB-tekstityksen dekooderi" msgid "Decoder" msgstr "Dekooderi" msgid "Buffer size" msgstr "Puskurin koko" msgid "Number of PES packets" msgstr "PES-pakettien lukumäärä" msgid "Local Frontend" msgstr "Paikallinen näyttö" msgid "Local Display Frontend" msgstr "Paikallinen näyttö" msgid "Use keyboard" msgstr "Käytä näppäimistöä" msgid "Driver" msgstr "Ohjain" msgid "Display address" msgstr "Näytön osoite" msgid "Framebuffer device" msgstr "Framebuffer-laite" msgid "Fullscreen mode" msgstr "Kokoruututila" msgid "Window width" msgstr "Ikkunan leveys" msgid "Window height" msgstr "Ikkunan korkeus" msgid "Window aspect" msgstr "Ikkunan kuvasuhde" msgid "Scale to window size" msgstr "Skaalaa ikkunan kokoiseksi" msgid "Port" msgstr "Portti" msgid "Speakers" msgstr "Kaiuttimet" msgid "Remote Clients" msgstr "Etäkäyttö" msgid "Allow remote clients" msgstr "Salli etäkäyttö" msgid "Listen port (TCP and broadcast)" msgstr "Kuuntele TCP-porttia" msgid "Listen address" msgstr "Kuuntele osoitteessa" msgid "Remote keyboard" msgstr "Käytä etänäppäimistöä" msgid "Max number of clients" msgstr "Asiakkaiden maksimimäärä" msgid "PIPE transport" msgstr "PIPE-siirto" msgid "TCP transport" msgstr "TCP-siirto" msgid "UDP transport" msgstr "UDP-siirto" msgid "RTP (multicast) transport" msgstr "RTP (multicast) -siirto" msgid "Address" msgstr "Osoite" msgid "TTL" msgstr "TTL-aika" msgid "Transmit always on" msgstr "Pidä lähetys aina päällä" msgid "SAP announcements" msgstr "SAP-ilmoitukset" msgid "Server announce broadcasts" msgstr "Palvelimen broadcast-ilmoitukset" msgid "HTTP transport for media files" msgstr "HTTP -siirto mediatiedostoille" msgid "Additional network services" msgstr "Muut verkkopalvelut" msgid "HTTP server" msgstr "HTTP-palvelin" msgid "HTTP clients can control VDR" msgstr "Anna HTTP-asiakkaiden ohjata VDR:ää" msgid "RTSP server" msgstr "RTSP-palvelin" msgid "RTSP clients can control VDR" msgstr "Anna RTSP-asiakkaiden ohjata VDR:ää" msgid "Playlist settings" msgstr "Soittolistan asetukset" msgid "Show the track number" msgstr "Näytä raidan numero" msgid "Show the name of the artist" msgstr "Näytä esittäjän nimi" msgid "Show the name of the album" msgstr "Näytä levyn nimi" msgid "Scan for metainfo" msgstr "Tutki kappaleiden metatiedot" msgid "Cache metainfo" msgstr "Tallenna metatieto" msgid "Arrow keys control DVD playback" msgstr "Ohjaa nuolinäppäimillä DVD-toistoa" msgid "Show hidden files" msgstr "Näytä piilotiedostot" msgid "Allow removing files" msgstr "Salli tiedostojen poistaminen" msgid "Remember last playback position" msgstr "Muista edellinen toistokohta" msgid "Media Player" msgstr "Mediasoitin" msgid "Play file" msgstr "Toista tiedosto" msgid "Play music" msgstr "Toista musiikkia" msgid "View images" msgstr "Katsele kuvia" msgid "Play DVD disc" msgstr "Toista DVD-levy" msgid "Play audio CD" msgstr "Toista CD-levy" msgid "Play BluRay disc" msgstr "Toista BluRay-levy" msgid "Video settings" msgstr "Videoasetukset" msgid "Audio settings" msgstr "Ääniasetukset" msgid "Grayscale" msgstr "Harmaasävy" msgid "Bitmap" msgstr "Bittikartta" msgid "OSD" msgstr "Kuvaruutunäyttö" msgid "Test Images" msgstr "Testikuvat" msgid "Play movie title" msgstr "Toista elokuva" msgid "Play disc" msgstr "Toista levy" msgid "Images" msgstr "Kuvat" msgid "Add to playlist" msgstr "Lisää soittolistalle" msgid "Button$Queue" msgstr "Soittolistalle" msgid "Media" msgstr "Media" msgid "Play only audio" msgstr "Toista pelkkä ääni" msgid "Audio equalizer" msgstr "Taajuuskorjain" msgid "Video aspect ratio" msgstr "Videon kuvasuhde" msgid "On" msgstr "Käytössä" msgid "Default playlist not found" msgstr "Oletussoittolistaa ei löydetä" msgid "Default playlist is not symlink" msgstr "Oletussoittolista ei ole symbolinen linkki" msgid "Default playlist not defined" msgstr "Oletussoittolistaa ei ole määritelty" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput: pikanäppäintä %s ei ole kytketty" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib näyttölaite" msgid "Frontend initialization failed" msgstr "Näyttölaitteen alustus epäonnistui" msgid "Server initialization failed" msgstr "Palvelimen käynnistys epäonnistui" msgid "Playlist" msgstr "Soittolista" msgid "Button$Random" msgstr "Satunnaistoisto" msgid "Button$Normal" msgstr "Normaali toisto" msgid "Button$Add files" msgstr "Lisää" msgid "Button$Remove" msgstr "Poista" msgid "Button$Mark" msgstr "Merkitse" msgid "Queued to playlist" msgstr "Lisätty soittolistalle" msgid "Random play" msgstr "Satunnaistoisto" msgid "Normal play" msgstr "Normaali toisto" msgid "DVD Menu" msgstr "DVD-valikot" msgid "Exit DVD menu" msgstr "Sulje DVD:n valikko" msgid "DVD Root menu" msgstr "DVD:n päävalikko" msgid "DVD Title menu" msgstr "DVD:n ohjelmavalikko" msgid "DVD SPU menu" msgstr "DVD:n tekstitysvalikko" msgid "DVD Audio menu" msgstr "DVD:n äänivalikko" msgid "Close menu" msgstr "Sulje valikko" msgid "BluRay Top menu" msgstr "BluRay-levyn päävalikko" msgid "Toggle Pop-Up menu" msgstr "" msgid "Next title" msgstr "" msgid "Previous title" msgstr "" msgid "Delete image ?" msgstr "Poistetaanko kuva ?" #~ msgid "normal" #~ msgstr "normaali" #~ msgid "inverted" #~ msgstr "käänteinen" #~ msgid "Interlaced Field Order" #~ msgstr "Lomitettujen kenttien järjestys" #~ msgid "Button$Sort" #~ msgstr "Järjestä" #~ msgid "Play file >>" #~ msgstr "Toista tiedosto >>" #~ msgid "Play music >>" #~ msgstr "Toista musiikkia >>" #~ msgid "View images >>" #~ msgstr "Katsele kuvia >>" #~ msgid "Play DVD disc >>" #~ msgstr "Toista DVD-levy >>" #~ msgid "Play audio CD >>" #~ msgstr "Toista CD-levy >>" #~ msgid "Play BluRay disc >>" #~ msgstr "Toista BluRay-levy >>" #~ msgid "Play remote DVD >>" #~ msgstr "Toista DVD-levy etäkoneesta >>" #~ msgid "Play remote CD >>" #~ msgstr "Toista CD-levy etäkoneesta >>" #~ msgid "Headphone audio mode" #~ msgstr "Kuulokkeiden äänimoodi" #~ msgid "Audio equalizer >>" #~ msgstr "Taajuuskorjain >>" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/et_EE.po���������������������������������������������������������������������0000644�0001750�0001750�00000030407�13061253422�014165� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # Arthur Konovalov # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2008-10-06 11:19+0200\n" "Last-Translator: Arthur Konovalov\n" "Language-Team: Estonian <vdr@linuxtv.org>\n" "Language: et\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "custom" msgstr "oma" msgid "tiny" msgstr "tilluke" msgid "small" msgstr "väike" msgid "medium" msgstr "keskmine" msgid "large" msgstr "suur" msgid "huge" msgstr "ülisuur" msgid "automatic" msgstr "automaatne" msgid "default" msgstr "vaikimisi" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "CenterCutOut" msgid "square" msgstr "nelinurkne" msgid "anamorphic" msgstr "anamorfne" msgid "DVB" msgstr "DVB" msgid "off" msgstr "väljas" msgid "no audio" msgstr "helita" msgid "no video" msgstr "pildita" msgid "Off" msgstr "väljas" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "ostsillograaf" msgid "FFT Scope" msgstr "spekter" msgid "FFT Graph" msgstr "spektrogramm" msgid "Image" msgstr "" msgid "Mono 1.0" msgstr "Mono 1.0" msgid "Stereo 2.0" msgstr "Stereo 2.0" msgid "Headphones 2.0" msgstr "Kuularid 2.0" msgid "Stereo 2.1" msgstr "Stereo 2.1" msgid "Surround 3.0" msgstr "Surround 3.0" msgid "Surround 4.0" msgstr "Surround 4.0" msgid "Surround 4.1" msgstr "Surround 4.1" msgid "Surround 5.0" msgstr "Surround 5.0" msgid "Surround 5.1" msgstr "Surround 5.1" msgid "Surround 6.0" msgstr "Surround 6.0" msgid "Surround 6.1" msgstr "Surround 6.1" msgid "Surround 7.1" msgstr "Surround 7.1" msgid "Pass Through" msgstr "läbiv" msgid "very large" msgstr "väga suur" msgid "Software" msgstr "tarkvaraline" msgid "Hardware" msgstr "raualine" msgid "no" msgstr "ei" msgid "grayscale" msgstr "halltoonid" msgid "transparent" msgstr "läbipaistev" msgid "transparent grayscale" msgstr "läbipaistev halltoon" msgid "yes" msgstr "jah" msgid "nearest" msgstr "lähim" msgid "bilinear" msgstr "bilineaarne" msgid "LUT8" msgstr "" msgid "TrueColor" msgstr "" msgid "video stream" msgstr "" msgid "none" msgstr "ei" msgid "nonref" msgstr "viiteta" msgid "bidir" msgstr "" msgid "nonkey" msgstr "" msgid "all" msgstr "kõik" msgid "Audio" msgstr "Heli" msgid "Volume control" msgstr "Helitugevuse juhtimine" msgid "Delay" msgstr "Viivitus" msgid "ms" msgstr "ms" msgid "Audio Compression" msgstr "Heli kompresseerimine" msgid "Upmix stereo to 5.1" msgstr "Stereo 5.1-ks miksimine" msgid "Downmix AC3 to surround" msgstr "AC3 surroundiks miksimine" msgid "Mix to headphones" msgstr "Miksimine kuularitesse" msgid "Visualization" msgstr "Visualiseerimine" msgid "Width" msgstr "Laius" msgid "px" msgstr "px" msgid "Height" msgstr "Kõrgus" msgid "Speed" msgstr "Kiirus" msgid "fps" msgstr "fps" msgid "Background image MRL" msgstr "" msgid "Audio Equalizer" msgstr "Heli ekvalaiser" msgid "Use Video-Out Driver" msgstr "videodraiver" msgid "vector" msgstr "vektor" msgid "full" msgstr "täis" msgid "half (top)" msgstr "pool (ülemine)" msgid "half (bottom)" msgstr "pool (alumine)" msgid "Video" msgstr "Video" msgid "Aspect ratio" msgstr "Külgsuhe" msgid "Crop letterbox 4:3 to 16:9" msgstr "4:3-letterbox 16:9:ks kärpimine" msgid "Use driver crop" msgstr "" msgid "Autodetect letterbox" msgstr "Letterbox autotuvastamine" msgid "Crop to" msgstr "Kärpimine" msgid "Autodetect rate" msgstr "" msgid "Stabilize time" msgstr "" msgid "Maximum logo width [%]" msgstr "" msgid "Overscan compensate [%1000]" msgstr "" msgid "Soft start" msgstr "Pehme start" msgid "Soft start step" msgstr "" msgid "Detect subtitles" msgstr "Subtiitrite tuvastamine" msgid "Subs detect stabilize time" msgstr "" msgid "Subs detect lifetime" msgstr "" msgid "Use avards analysis" msgstr "" msgid "Bar tone tolerance" msgstr "" msgid "Software scaling" msgstr "Tarkvaraline skaleerimine" msgid "Change aspect ratio" msgstr "Külgsuhte muutmine" msgid "Change video size" msgstr "Videosuuruse muutmine" msgid "Allow downscaling" msgstr "Allaskaleerimise lubamine" msgid "Overscan (crop image borders)" msgstr "Pildiservade kärpimine (overscan)" msgid "Post processing (ffmpeg)" msgstr "Järeltöötlus (ffmpeg)" msgid "Quality" msgstr "Kvaliteet" msgid "Mode" msgstr "Mudel" msgid "Deinterlacing" msgstr "Deinterlace" msgid "Method" msgstr "Meetod" msgid "Cheap mode" msgstr "Cheap-mudel" msgid "Pulldown" msgstr "Pulldown" msgid "Frame rate" msgstr "Kaadrisagedus" msgid "Judder Correction" msgstr "Vibratsiooni korrektsioon" msgid "Use progressive frame flag" msgstr "Progressiivpildi kasutamine" msgid "Chroma Filter" msgstr "Chroma-filter" msgid "Sharpen / Blur" msgstr "Teravus / hägustus" msgid "Width of the luma matrix" msgstr "Luma-maatiksi laius" msgid "Height of the luma matrix" msgstr "Luma-maatriksi kõrgus" msgid "Amount of luma sharpness/blur" msgstr "Luma-teravus/-hägusus" msgid "Width of the chroma matrix" msgstr "Chroma-maatriksi laius" msgid "Height of the chroma matrix" msgstr "Chroma-maatriksi kõrgus" msgid "Amount of chroma sharpness/blur" msgstr "Chroma-teravus/-hägusus" msgid "3D Denoiser" msgstr "3D-mürasummutus" msgid "Spatial luma strength" msgstr "Ruumilise luma tugevus" msgid "Spatial chroma strength" msgstr "Ruumilise chroma tugevus" msgid "Temporal strength" msgstr "Ajaline tugevus" msgid "HUE" msgstr "Värvus" msgid "Saturation" msgstr "Küllastus" msgid "Contrast" msgstr "Kontrast" msgid "Brightness" msgstr "Heledus" msgid "Sharpness" msgstr "Teravus" msgid "Noise Reduction" msgstr "Mürasummutus" msgid "Smooth fast forward" msgstr "Sujuv edasikerimine" msgid "Fastest trick speed" msgstr "Suurim kerimiskiirus" msgid "On-Screen Display" msgstr "Ekraanimenüü" msgid "Hide main menu" msgstr "Peitmine peamenüü valikus" msgid "Resolution" msgstr "Resolutsioon" msgid "Color depth" msgstr "" msgid "Blending method" msgstr "Segunemise meetod" msgid "Use hardware for low-res video" msgstr "Riistvara kasutus madalresol. videol" msgid "Scaling method" msgstr "Skaleerimise meetod" msgid "Scale subtitles" msgstr "" msgid "Show all layers" msgstr "Kõikide kihtide kuvamine" msgid "Dynamic transparency correction" msgstr "Dünaamilne läbipaistv. korrektsioon" msgid "Static transparency correction" msgstr "Staatiline läbipaistv. korrektsioon" msgid "External subtitle size" msgstr "Väliste subtiitrite suurus" msgid "DVB subtitle decoder" msgstr "DVB-subtiitrite dekooder" msgid "Decoder" msgstr "Dekooder" msgid "Buffer size" msgstr "Puhvri suurus" msgid "Number of PES packets" msgstr "PES-pakettide määr" msgid "Local Frontend" msgstr "Lokaalne liides" msgid "Local Display Frontend" msgstr "Lokaalne ekraaniliides" msgid "Use keyboard" msgstr "Klahvistiku kasutamine" msgid "Driver" msgstr "Draiver" msgid "Display address" msgstr "Ekraani aadress" msgid "Framebuffer device" msgstr "Framebuffer-seade" msgid "Fullscreen mode" msgstr "Täisekraan" msgid "Window width" msgstr "Akna laius" msgid "Window height" msgstr "Akna kõrgus" msgid "Window aspect" msgstr "Akna külgsuhe" msgid "Scale to window size" msgstr "Akna skaleeritud suurus" msgid "Port" msgstr "Port" msgid "Speakers" msgstr "Kõlarid" msgid "Remote Clients" msgstr "Kaugkliendid" msgid "Allow remote clients" msgstr "Kaugklientide lubamine" msgid "Listen port (TCP and broadcast)" msgstr "TCP-port" msgid "Listen address" msgstr "Aadress" msgid "Remote keyboard" msgstr "Kaugklahvistik" msgid "Max number of clients" msgstr "Klientide max hulk" msgid "PIPE transport" msgstr "PIPE-transport" msgid "TCP transport" msgstr "TCP-transport" msgid "UDP transport" msgstr "UDP-transport" msgid "RTP (multicast) transport" msgstr "RTP (multicast) -transport" msgid "Address" msgstr "Aadress" msgid "TTL" msgstr "TTL" msgid "Transmit always on" msgstr "Edastus alati sees" msgid "SAP announcements" msgstr "SAP-teadustused" msgid "Server announce broadcasts" msgstr "Severi broadcast-teadustused" msgid "HTTP transport for media files" msgstr "Meediafailide HTTP-transport" msgid "Additional network services" msgstr "Muud võrguteenused" msgid "HTTP server" msgstr "HTTP-sever" msgid "HTTP clients can control VDR" msgstr "HTTP-klientide VDR juhtimine" msgid "RTSP server" msgstr "RTSP-server" msgid "RTSP clients can control VDR" msgstr "RTSP-klientide VDR juhtimine" msgid "Playlist settings" msgstr "Esitusloendi seaded" msgid "Show the track number" msgstr "Rajanumbri kuvamine" msgid "Show the name of the artist" msgstr "Esitaja nime kuvamine" msgid "Show the name of the album" msgstr "Albumi nime kuvamine" msgid "Scan for metainfo" msgstr "Metaandmete skaneermine" msgid "Cache metainfo" msgstr "Metaandmete vahemälu" msgid "Arrow keys control DVD playback" msgstr "Noolte kasutamine DVD juhtimisel" msgid "Show hidden files" msgstr "" msgid "Allow removing files" msgstr "" msgid "Remember last playback position" msgstr "" msgid "Media Player" msgstr "Meediapleier" msgid "Play file" msgstr "Faili esitamine" msgid "Play music" msgstr "Muusika esitamine" msgid "View images" msgstr "Piltide kuvamine" msgid "Play DVD disc" msgstr "DVD plaadi esitamine" msgid "Play audio CD" msgstr "CD plaadi esitamine" msgid "Play BluRay disc" msgstr "BluRay plaadi esitamine" msgid "Video settings" msgstr "Videoseaded" msgid "Audio settings" msgstr "Heliseaded" msgid "Grayscale" msgstr "Halltoonid" msgid "Bitmap" msgstr "Bitmap" msgid "OSD" msgstr "Ekraanimenüü" msgid "Test Images" msgstr "Testipildid" msgid "Play movie title" msgstr "" msgid "Play disc" msgstr "" msgid "Images" msgstr "Pildid" msgid "Add to playlist" msgstr "Lisada esitusnimekirja" msgid "Button$Queue" msgstr "Esitusnimekiri" msgid "Media" msgstr "Meedia" msgid "Play only audio" msgstr "Ainult heli esitamine" msgid "Audio equalizer" msgstr "Ekvalaiser" msgid "Video aspect ratio" msgstr "Video külgsuhe" msgid "On" msgstr "sees" msgid "Default playlist not found" msgstr "Vaikimisi esitusnimekiri ei ole leitav" msgid "Default playlist is not symlink" msgstr "Vaikimisi esitusnimekiri ei ole symbollink" msgid "Default playlist not defined" msgstr "Vaikimisi esitusnimekiri määritlemata" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput: kiirklahv %s sidumata" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib kuvar" msgid "Frontend initialization failed" msgstr "Liidese lähtestamine nurjus" msgid "Server initialization failed" msgstr "Serveri lätestamine nurjus" msgid "Playlist" msgstr "Esitusnimekiri" msgid "Button$Random" msgstr "Juhuslik" msgid "Button$Normal" msgstr "Normaalne" msgid "Button$Add files" msgstr "Lisada" msgid "Button$Remove" msgstr "Eemaldada" msgid "Button$Mark" msgstr "" msgid "Queued to playlist" msgstr "Lisatud esitusnimekirja" msgid "Random play" msgstr "Juhuesitus" msgid "Normal play" msgstr "Normaalne esitus" msgid "DVD Menu" msgstr "DVD menüü" msgid "Exit DVD menu" msgstr "Väljumine DVD menüüst" msgid "DVD Root menu" msgstr "DVD juurmenüü" msgid "DVD Title menu" msgstr "DVD peamemüü" msgid "DVD SPU menu" msgstr "DVD SPU menüü" msgid "DVD Audio menu" msgstr "DVD audio menüü" msgid "Close menu" msgstr "Menüü sulgemine" msgid "BluRay Top menu" msgstr "" msgid "Toggle Pop-Up menu" msgstr "" msgid "Next title" msgstr "" msgid "Previous title" msgstr "" msgid "Delete image ?" msgstr "Kustutada pilt ?" #~ msgid "normal" #~ msgstr "normaalne" #~ msgid "inverted" #~ msgstr "pööratud" #~ msgid "Interlaced Field Order" #~ msgstr "Ridade järjestus" #~ msgid "Button$Sort" #~ msgstr "Sorteerimine" #~ msgid "Play file >>" #~ msgstr "Faili mängimine >>" #~ msgid "Play music >>" #~ msgstr "Muusika mängimine >>" #~ msgid "View images >>" #~ msgstr "Piltide vaatamine >>" #~ msgid "Play DVD disc >>" #~ msgstr "DVD mängimine >>" #~ msgid "Play audio CD >>" #~ msgstr "CD mängimine >>" #~ msgid "Play BluRay disc >>" #~ msgstr "BluRay mängimine >>" #~ msgid "Play remote DVD >>" #~ msgstr "Kaug-DVD mängimine >>" #~ msgid "Play remote CD >>" #~ msgstr "Kaug-CD mängimine >>" #~ msgid "Headphone audio mode" #~ msgstr "Kuulari helimudel" #~ msgid "Audio equalizer >>" #~ msgstr "Heli ekvalaiser >>" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/de_DE.po���������������������������������������������������������������������0000644�0001750�0001750�00000026543�13061253422�014152� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # Udo Richter # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2007-11-23 10:17+0200\n" "Last-Translator: Udo Richter\n" "Language-Team: German <vdr@linuxtv.org>\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "custom" msgstr "Benutzerdefiniert" msgid "tiny" msgstr "Winzig" msgid "small" msgstr "Klein" msgid "medium" msgstr "Mittel" msgid "large" msgstr "Groß" msgid "huge" msgstr "Riesig" msgid "automatic" msgstr "Automatik" msgid "default" msgstr "Standard" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "CenterCutOut" msgid "square" msgstr "" msgid "anamorphic" msgstr "" msgid "DVB" msgstr "" msgid "off" msgstr "Aus" msgid "no audio" msgstr "Kein Audio" msgid "no video" msgstr "Kein Video" msgid "Off" msgstr "Aus" msgid "Goom" msgstr "Goom" msgid "Oscilloscope" msgstr "Oszilloskop" msgid "FFT Scope" msgstr "FFT Spektrum" msgid "FFT Graph" msgstr "FFT Graph" msgid "Image" msgstr "Bilder" msgid "Mono 1.0" msgstr "" msgid "Stereo 2.0" msgstr "" msgid "Headphones 2.0" msgstr "Kopfhörer 2.0" msgid "Stereo 2.1" msgstr "" msgid "Surround 3.0" msgstr "" msgid "Surround 4.0" msgstr "" msgid "Surround 4.1" msgstr "" msgid "Surround 5.0" msgstr "" msgid "Surround 5.1" msgstr "" msgid "Surround 6.0" msgstr "" msgid "Surround 6.1" msgstr "" msgid "Surround 7.1" msgstr "" msgid "Pass Through" msgstr "" msgid "very large" msgstr "" msgid "Software" msgstr "" msgid "Hardware" msgstr "" msgid "no" msgstr "nein" msgid "grayscale" msgstr "" msgid "transparent" msgstr "" msgid "transparent grayscale" msgstr "" msgid "yes" msgstr "ja" msgid "nearest" msgstr "" msgid "bilinear" msgstr "" msgid "LUT8" msgstr "" msgid "TrueColor" msgstr "" msgid "video stream" msgstr "" msgid "none" msgstr "" msgid "nonref" msgstr "" msgid "bidir" msgstr "" msgid "nonkey" msgstr "" msgid "all" msgstr "alle" msgid "Audio" msgstr "Audio" msgid "Volume control" msgstr "" msgid "Delay" msgstr "Verzögerung" msgid "ms" msgstr "ms" msgid "Audio Compression" msgstr "Audio-Komprimierung" msgid "Upmix stereo to 5.1" msgstr "Stereo zu 5.1 hoch mischen" msgid "Downmix AC3 to surround" msgstr "AC3 zu Surround herunter mischen" msgid "Mix to headphones" msgstr "" msgid "Visualization" msgstr "Visualisierung" msgid "Width" msgstr "Breite" msgid "px" msgstr "px" msgid "Height" msgstr "Höhe" msgid "Speed" msgstr "Bildrate" msgid "fps" msgstr "" msgid "Background image MRL" msgstr "" msgid "Audio Equalizer" msgstr "Audio Equalizer" msgid "Use Video-Out Driver" msgstr "" msgid "vector" msgstr "" msgid "full" msgstr "" msgid "half (top)" msgstr "" msgid "half (bottom)" msgstr "" msgid "Video" msgstr "Video" msgid "Aspect ratio" msgstr "Seitenverhältnis" msgid "Crop letterbox 4:3 to 16:9" msgstr "Schneide letterbox 4:3 zu 16:9" msgid "Use driver crop" msgstr "" msgid "Autodetect letterbox" msgstr "Letterbox automatisch erkennen" msgid "Crop to" msgstr "Schneide auf" msgid "Autodetect rate" msgstr "" msgid "Stabilize time" msgstr "" msgid "Maximum logo width [%]" msgstr "" msgid "Overscan compensate [%1000]" msgstr "" msgid "Soft start" msgstr "Weich starten" msgid "Soft start step" msgstr "" msgid "Detect subtitles" msgstr "Erkenne Untertitel" msgid "Subs detect stabilize time" msgstr "" msgid "Subs detect lifetime" msgstr "" msgid "Use avards analysis" msgstr "" msgid "Bar tone tolerance" msgstr "" msgid "Software scaling" msgstr "" msgid "Change aspect ratio" msgstr "" msgid "Change video size" msgstr "" msgid "Allow downscaling" msgstr "Verkleinern zulassen" msgid "Overscan (crop image borders)" msgstr "Overscan (Bildränder abschneiden)" msgid "Post processing (ffmpeg)" msgstr "Nachbearbeitung (ffmpeg)" msgid "Quality" msgstr "Qualität" msgid "Mode" msgstr "Modus" msgid "Deinterlacing" msgstr "Deinterlacing" msgid "Method" msgstr "Methode" msgid "Cheap mode" msgstr "einfacher Modus" msgid "Pulldown" msgstr "Pulldown" msgid "Frame rate" msgstr "Bildrate" msgid "Judder Correction" msgstr "Ruckel-Korrektur" msgid "Use progressive frame flag" msgstr "Nutze progressive frame flag" msgid "Chroma Filter" msgstr "Chrominanz-Filter" msgid "Sharpen / Blur" msgstr "" msgid "Width of the luma matrix" msgstr "" msgid "Height of the luma matrix" msgstr "" msgid "Amount of luma sharpness/blur" msgstr "" msgid "Width of the chroma matrix" msgstr "" msgid "Height of the chroma matrix" msgstr "" msgid "Amount of chroma sharpness/blur" msgstr "" msgid "3D Denoiser" msgstr "" msgid "Spatial luma strength" msgstr "" msgid "Spatial chroma strength" msgstr "" msgid "Temporal strength" msgstr "" msgid "HUE" msgstr "Farbton" msgid "Saturation" msgstr "Sättigung" msgid "Contrast" msgstr "Kontrast" msgid "Brightness" msgstr "Helligkeit" msgid "Sharpness" msgstr "" msgid "Noise Reduction" msgstr "" msgid "Smooth fast forward" msgstr "" msgid "Fastest trick speed" msgstr "" msgid "On-Screen Display" msgstr "On-Screen Display" msgid "Hide main menu" msgstr "Verstecke Hauptmenü" msgid "Resolution" msgstr "" msgid "Color depth" msgstr "" msgid "Blending method" msgstr "" msgid "Use hardware for low-res video" msgstr "" msgid "Scaling method" msgstr "" msgid "Scale subtitles" msgstr "" msgid "Show all layers" msgstr "" msgid "Dynamic transparency correction" msgstr "Dynamische Transparenz-Korrektur" msgid "Static transparency correction" msgstr "Statische Transparenz-Korrektur" msgid "External subtitle size" msgstr "Untertitel größe" msgid "DVB subtitle decoder" msgstr "" msgid "Decoder" msgstr "Dekoder" msgid "Buffer size" msgstr "Puffergröße" msgid "Number of PES packets" msgstr "Anzahl PES-Pakete" msgid "Local Frontend" msgstr "Lokale Anzeige" msgid "Local Display Frontend" msgstr "Lokale Bildschirmanzeige" msgid "Use keyboard" msgstr "Tastatur benutzen" msgid "Driver" msgstr "Treiber" msgid "Display address" msgstr "Bildschirm-Adresse" msgid "Framebuffer device" msgstr "Framebuffer-Device" msgid "Fullscreen mode" msgstr "Vollbild-Modus" msgid "Window width" msgstr "Fensterbreite" msgid "Window height" msgstr "Fensterhöhe" msgid "Window aspect" msgstr "Fenster-Seitenverhältnis" msgid "Scale to window size" msgstr "Skaliere auf Fenster-Größe" msgid "Port" msgstr "Port" msgid "Speakers" msgstr "Lautsprecher" msgid "Remote Clients" msgstr "Entfernte Clients" msgid "Allow remote clients" msgstr "Erlaube entfernte Clients" msgid "Listen port (TCP and broadcast)" msgstr "Empfangender Port (TCP und Broadcast)" msgid "Listen address" msgstr "" msgid "Remote keyboard" msgstr "Tastaturfernsteuerung" msgid "Max number of clients" msgstr "" msgid "PIPE transport" msgstr "Pipe-Übertragung" msgid "TCP transport" msgstr "TCP-Übertragung" msgid "UDP transport" msgstr "UDP-Übertragung" msgid "RTP (multicast) transport" msgstr "RTP (multicast) Übertragung" msgid "Address" msgstr "Multicast-Adresse" msgid "TTL" msgstr "Multicast-TTL" msgid "Transmit always on" msgstr "Immer senden" msgid "SAP announcements" msgstr "SAP-Ankündigungen" msgid "Server announce broadcasts" msgstr "Server-Bekanntmachung Broadcast" msgid "HTTP transport for media files" msgstr "HTTP-Verbindung für Medien-Dateien" msgid "Additional network services" msgstr "Zusätzliche Netzwerk-Services" msgid "HTTP server" msgstr "HTTP-Server" msgid "HTTP clients can control VDR" msgstr "HTTP-Clients können VDR kontrollieren" msgid "RTSP server" msgstr "RTSP-Server" msgid "RTSP clients can control VDR" msgstr "RTSP-Clients können VDR kontrollieren" msgid "Playlist settings" msgstr "" msgid "Show the track number" msgstr "" msgid "Show the name of the artist" msgstr "" msgid "Show the name of the album" msgstr "" msgid "Scan for metainfo" msgstr "" msgid "Cache metainfo" msgstr "" msgid "Arrow keys control DVD playback" msgstr "" msgid "Show hidden files" msgstr "" msgid "Allow removing files" msgstr "" msgid "Remember last playback position" msgstr "" msgid "Media Player" msgstr "Medien..." msgid "Play file" msgstr "Datei abspielen" msgid "Play music" msgstr "Musik abspielen" msgid "View images" msgstr "Bilder ansehen" msgid "Play DVD disc" msgstr "DVD abspielen" msgid "Play audio CD" msgstr "Musik-CD abspielen" msgid "Play BluRay disc" msgstr "BluRay abspielen" msgid "Video settings" msgstr "Video-Einstellungen" msgid "Audio settings" msgstr "Audio-Einstellungen" msgid "Grayscale" msgstr "Graustufen" msgid "Bitmap" msgstr "Bitmap" msgid "OSD" msgstr "" msgid "Test Images" msgstr "Testbilder" msgid "Play movie title" msgstr "" msgid "Play disc" msgstr "" msgid "Images" msgstr "Bilder" msgid "Add to playlist" msgstr "Füge zur Wiedergabeliste hinzu" msgid "Button$Queue" msgstr "Warteschlange" msgid "Media" msgstr "Medien" msgid "Play only audio" msgstr "Nur Audio spielen" msgid "Audio equalizer" msgstr "Audio Equalizer" msgid "Video aspect ratio" msgstr "Video Seitenverhältnis" msgid "On" msgstr "an" msgid "Default playlist not found" msgstr "Std. Wiedergabeliste nicht gefunden" msgid "Default playlist is not symlink" msgstr "Std Wiedergabeliste ist kein sym .Link" msgid "Default playlist not defined" msgstr "Std Wiedergabeliste nicht definiert" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib Ausgabe-Plugin" msgid "Frontend initialization failed" msgstr "Initialisierung des Frontends fehlgeschlagen" msgid "Server initialization failed" msgstr "Initialisierung des Servers fehlgeschlagen" msgid "Playlist" msgstr "Wiedergabeliste" msgid "Button$Random" msgstr "Zufall" msgid "Button$Normal" msgstr "Normal" msgid "Button$Add files" msgstr "Füge Dateien hinzu" msgid "Button$Remove" msgstr "Entferne" msgid "Button$Mark" msgstr "Markieren" msgid "Queued to playlist" msgstr "Hänge an Wiedergabeliste an" msgid "Random play" msgstr "Zufallswiedergabe" msgid "Normal play" msgstr "Normale Wiedergabe" msgid "DVD Menu" msgstr "DVD Menü" msgid "Exit DVD menu" msgstr "DVD Menü verlassen" msgid "DVD Root menu" msgstr "DVD Hauptmenü" msgid "DVD Title menu" msgstr "DVD Titelmenü" msgid "DVD SPU menu" msgstr "DVD SPU Menü" msgid "DVD Audio menu" msgstr "DVD Audiomenü" msgid "Close menu" msgstr "Menü schließen" msgid "BluRay Top menu" msgstr "" msgid "Toggle Pop-Up menu" msgstr "" msgid "Next title" msgstr "" msgid "Previous title" msgstr "" msgid "Delete image ?" msgstr "Bild löschen?" #~ msgid "normal" #~ msgstr "Normal" #~ msgid "inverted" #~ msgstr "Invertiert" #~ msgid "Interlaced Field Order" #~ msgstr "Interlaced Halbbild-Reihenfolge" #~ msgid "Button$Sort" #~ msgstr "Sortiere" #~ msgid "Play file >>" #~ msgstr "Datei abspielen >>" #~ msgid "Play music >>" #~ msgstr "Musik abspielen >>" #~ msgid "View images >>" #~ msgstr "Bilder ansehen >>" #~ msgid "Play DVD disc >>" #~ msgstr "DVD abspielen >>" #~ msgid "Play audio CD >>" #~ msgstr "Musik-CD abspielen >>" #~ msgid "Play BluRay disc >>" #~ msgstr "BluRay abspielen >>" #~ msgid "Play remote DVD >>" #~ msgstr "Entfernte DVD abspielen >>" #~ msgid "Play remote CD >>" #~ msgstr "Entfernte CD abspielen >>" #~ msgid "Audio equalizer >>" #~ msgstr "Audio-Equalizer >>" �������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/po/cs_CZ.po���������������������������������������������������������������������0000644�0001750�0001750�00000027354�13061253422�014214� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger <kls@cadsoft.de> # This file is distributed under the same license as the VDR package. # msgid "" msgstr "" "Project-Id-Version: Xineliboutput 1.1.0\n" "Report-Msgid-Bugs-To: <phintuka@users.sourceforge.net>\n" "POT-Creation-Date: 2017-03-12 16:11+0200\n" "PO-Revision-Date: 2008-03-20 23:57+0100\n" "Last-Translator: Maya <maja373@gmail.com>\n" "Language-Team: Czech <vdr@linuxtv.org>\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Czech\n" "X-Poedit-Country: CZECH REPUBLIC\n" msgid "custom" msgstr "uživatelský" msgid "tiny" msgstr "nejmenší" msgid "small" msgstr "malý" msgid "medium" msgstr "střední" msgid "large" msgstr "velký" msgid "huge" msgstr "největší" msgid "automatic" msgstr "automaticky" msgid "default" msgstr "výchozí" msgid "Pan&Scan" msgstr "Pan&Scan" msgid "CenterCutOut" msgstr "" msgid "square" msgstr "čtvercový" msgid "anamorphic" msgstr "anamorfní" msgid "DVB" msgstr "DVB" msgid "off" msgstr "vypnuto" msgid "no audio" msgstr "bez zvuku" msgid "no video" msgstr "bez obrazu" msgid "Off" msgstr "vypnuto" msgid "Goom" msgstr "Soutěska" msgid "Oscilloscope" msgstr "Osciloskop" msgid "FFT Scope" msgstr "FFT spektrum" msgid "FFT Graph" msgstr "FFT graf" msgid "Image" msgstr "" msgid "Mono 1.0" msgstr "Mono 1.0" msgid "Stereo 2.0" msgstr "Stereo 2.0" msgid "Headphones 2.0" msgstr "Sluchátka 2.0" msgid "Stereo 2.1" msgstr "Stereo 2.1" msgid "Surround 3.0" msgstr "Surround 3.0" msgid "Surround 4.0" msgstr "Surround 4.0" msgid "Surround 4.1" msgstr "Surround 4.1" msgid "Surround 5.0" msgstr "Surround 5.0" msgid "Surround 5.1" msgstr "Surround 5.1" msgid "Surround 6.0" msgstr "Surround 6.0" msgid "Surround 6.1" msgstr "Surround 6.1" msgid "Surround 7.1" msgstr "Surround 7.1" msgid "Pass Through" msgstr "Průchozí" msgid "very large" msgstr "velmi velký" msgid "Software" msgstr "softwarové" msgid "Hardware" msgstr "hardwarové" msgid "no" msgstr "ne" msgid "grayscale" msgstr "odstíny šedi" msgid "transparent" msgstr "průhledný" msgid "transparent grayscale" msgstr "" msgid "yes" msgstr "ano" msgid "nearest" msgstr "" msgid "bilinear" msgstr "" msgid "LUT8" msgstr "" msgid "TrueColor" msgstr "" msgid "video stream" msgstr "" msgid "none" msgstr "" msgid "nonref" msgstr "" msgid "bidir" msgstr "" msgid "nonkey" msgstr "" msgid "all" msgstr "" msgid "Audio" msgstr "Zvuk" msgid "Volume control" msgstr "Ovládání hlasitosti" msgid "Delay" msgstr "Zpoždění" msgid "ms" msgstr "ms" msgid "Audio Compression" msgstr "" msgid "Upmix stereo to 5.1" msgstr "Převzorkovat stereo na 5.1" msgid "Downmix AC3 to surround" msgstr "Převzorkovat AC3 na surround" msgid "Mix to headphones" msgstr "" msgid "Visualization" msgstr "Vizualizace" msgid "Width" msgstr "Šířka" msgid "px" msgstr "bodů" msgid "Height" msgstr "Výška" msgid "Speed" msgstr "Rychlost" msgid "fps" msgstr "snímků/sek." msgid "Background image MRL" msgstr "" msgid "Audio Equalizer" msgstr "Korekce zvuku" msgid "Use Video-Out Driver" msgstr "" msgid "vector" msgstr "" msgid "full" msgstr "" msgid "half (top)" msgstr "" msgid "half (bottom)" msgstr "" msgid "Video" msgstr "Obraz" msgid "Aspect ratio" msgstr "Poměr stran" msgid "Crop letterbox 4:3 to 16:9" msgstr "Oříznout letterbox 4:3 na 16:9" msgid "Use driver crop" msgstr "" msgid "Autodetect letterbox" msgstr "Automaticky detekovat letterbox" msgid "Crop to" msgstr "Oříznout na" msgid "Autodetect rate" msgstr "" msgid "Stabilize time" msgstr "" msgid "Maximum logo width [%]" msgstr "" msgid "Overscan compensate [%1000]" msgstr "" msgid "Soft start" msgstr "Postupné zvětšení" msgid "Soft start step" msgstr "" msgid "Detect subtitles" msgstr "Detekovat titulky" msgid "Subs detect stabilize time" msgstr "" msgid "Subs detect lifetime" msgstr "" msgid "Use avards analysis" msgstr "" msgid "Bar tone tolerance" msgstr "" msgid "Software scaling" msgstr "Softwarové škálování" msgid "Change aspect ratio" msgstr "Změna poměru stran obrazu" msgid "Change video size" msgstr "Změnit velikost obrazu" msgid "Allow downscaling" msgstr "Povolit zmenšení" msgid "Overscan (crop image borders)" msgstr "Overscan (ořez okrajů obrazu)" msgid "Post processing (ffmpeg)" msgstr "Post processing (ffmpeg)" msgid "Quality" msgstr "Kvalita" msgid "Mode" msgstr "Mód" msgid "Deinterlacing" msgstr "Odstranění prokládání" msgid "Method" msgstr "Metoda" msgid "Cheap mode" msgstr "Zjednodušený mód" msgid "Pulldown" msgstr "" msgid "Frame rate" msgstr "Snímková rychlost" msgid "Judder Correction" msgstr "Korekce chvění" msgid "Use progressive frame flag" msgstr "" msgid "Chroma Filter" msgstr "" msgid "Sharpen / Blur" msgstr "Zaostření / rozmazání" msgid "Width of the luma matrix" msgstr "" msgid "Height of the luma matrix" msgstr "" msgid "Amount of luma sharpness/blur" msgstr "" msgid "Width of the chroma matrix" msgstr "" msgid "Height of the chroma matrix" msgstr "" msgid "Amount of chroma sharpness/blur" msgstr "" msgid "3D Denoiser" msgstr "3D odstranění šumu" msgid "Spatial luma strength" msgstr "" msgid "Spatial chroma strength" msgstr "" msgid "Temporal strength" msgstr "" msgid "HUE" msgstr "" msgid "Saturation" msgstr "" msgid "Contrast" msgstr "Kontrast" msgid "Brightness" msgstr "Jas" msgid "Sharpness" msgstr "" msgid "Noise Reduction" msgstr "" msgid "Smooth fast forward" msgstr "Plynulé přetáčení" msgid "Fastest trick speed" msgstr "" msgid "On-Screen Display" msgstr "Obrazovkové menu" msgid "Hide main menu" msgstr "Nezobrazovat v hlavním menu" msgid "Resolution" msgstr "" msgid "Color depth" msgstr "" msgid "Blending method" msgstr "" msgid "Use hardware for low-res video" msgstr "" msgid "Scaling method" msgstr "" msgid "Scale subtitles" msgstr "" msgid "Show all layers" msgstr "Zobrazit všechny vrstvy" msgid "Dynamic transparency correction" msgstr "Úprava dynamické průhlednosti" msgid "Static transparency correction" msgstr "Úprava statické průhlednosti" msgid "External subtitle size" msgstr "Velikost externích titulků" msgid "DVB subtitle decoder" msgstr "" msgid "Decoder" msgstr "Dekodér" msgid "Buffer size" msgstr "Velikost vyrovnávací paměti" msgid "Number of PES packets" msgstr "Počet PES paketů" msgid "Local Frontend" msgstr "Lokální rozhraní" msgid "Local Display Frontend" msgstr "Lokální zobrazovací rozhraní" msgid "Use keyboard" msgstr "Používat klávesnici" msgid "Driver" msgstr "Ovladač" msgid "Display address" msgstr "" msgid "Framebuffer device" msgstr "" msgid "Fullscreen mode" msgstr "Celoobrazovkový režim" msgid "Window width" msgstr "Šířka okna" msgid "Window height" msgstr "Výška okna" msgid "Window aspect" msgstr "Poměr stran okna" msgid "Scale to window size" msgstr "Škálovat do velikosti okna" msgid "Port" msgstr "Port" msgid "Speakers" msgstr "Reproduktory" msgid "Remote Clients" msgstr "Vzdálení klienti" msgid "Allow remote clients" msgstr "Povolit vzdálené klienty" msgid "Listen port (TCP and broadcast)" msgstr "" msgid "Listen address" msgstr "" msgid "Remote keyboard" msgstr "" msgid "Max number of clients" msgstr "" msgid "PIPE transport" msgstr "PIPE transport" msgid "TCP transport" msgstr "TCP transport" msgid "UDP transport" msgstr "UDP transport" msgid "RTP (multicast) transport" msgstr "RTP (multicast) transport" msgid "Address" msgstr "Adresa" msgid "TTL" msgstr "" msgid "Transmit always on" msgstr "Přenos stále zapnut" msgid "SAP announcements" msgstr "" msgid "Server announce broadcasts" msgstr "" msgid "HTTP transport for media files" msgstr "HTTP transport pro média" msgid "Additional network services" msgstr "Další síťové služby" msgid "HTTP server" msgstr "HTTP server" msgid "HTTP clients can control VDR" msgstr "HTTP klienti mohou ovládat VDR" msgid "RTSP server" msgstr "RTSP server" msgid "RTSP clients can control VDR" msgstr "RTSP klienti mohou ovládat VDR" msgid "Playlist settings" msgstr "Nastavení seznamu stop" msgid "Show the track number" msgstr "Zobrazovat číslo stopy" msgid "Show the name of the artist" msgstr "Zobrazovat jméno autora" msgid "Show the name of the album" msgstr "Zobrazovat název alba" msgid "Scan for metainfo" msgstr "Vyhledávat metainfo" msgid "Cache metainfo" msgstr "Uchovávat metainfo" msgid "Arrow keys control DVD playback" msgstr "" msgid "Show hidden files" msgstr "" msgid "Allow removing files" msgstr "" msgid "Remember last playback position" msgstr "" msgid "Media Player" msgstr "Přehrávač médií" msgid "Play file" msgstr "Přehrát soubor" msgid "Play music" msgstr "Přehrát hudbu" msgid "View images" msgstr "Prohlížet obrázky" msgid "Play DVD disc" msgstr "Přehrát DVD" msgid "Play audio CD" msgstr "Přehrát zvukové CD" msgid "Play BluRay disc" msgstr "Přehrát BluRay" msgid "Video settings" msgstr "Nastavení obrazu" msgid "Audio settings" msgstr "Nastavení zvuku" msgid "Grayscale" msgstr "Odstíny šedi" msgid "Bitmap" msgstr "Rastr" msgid "OSD" msgstr "OSD" msgid "Test Images" msgstr "Zkušební obrazce" msgid "Play movie title" msgstr "" msgid "Play disc" msgstr "" msgid "Images" msgstr "Obrázky" msgid "Add to playlist" msgstr "Přidat do seznamu stop" msgid "Button$Queue" msgstr "Fronta" msgid "Media" msgstr "Média" msgid "Play only audio" msgstr "Přehrávat pouze zvuk" msgid "Audio equalizer" msgstr "Korekce zvuku (ekvalizér)" msgid "Video aspect ratio" msgstr "Poměr stran obrazu" msgid "On" msgstr "zapnuto" msgid "Default playlist not found" msgstr "Výchozí seznam stop nenalezen" msgid "Default playlist is not symlink" msgstr "Výchozí seznam stop není symbolický odkaz" msgid "Default playlist not defined" msgstr "Výchozí seznam stop není definován" #, c-format msgid "xineliboutput: hotkey %s not binded" msgstr "xineliboutput: horká klávesa %s není přiřazena" msgid "X11/xine-lib output plugin" msgstr "X11/xine-lib výstupní plugin" msgid "Frontend initialization failed" msgstr "Inicializace rozhraní selhala" msgid "Server initialization failed" msgstr "Inicializace serveru selhala" msgid "Playlist" msgstr "Seznam stop" msgid "Button$Random" msgstr "Náhodné" msgid "Button$Normal" msgstr "Normální" msgid "Button$Add files" msgstr "Přidat soubory" msgid "Button$Remove" msgstr "Odstranit" msgid "Button$Mark" msgstr "" msgid "Queued to playlist" msgstr "Přidáno do seznamu stop" msgid "Random play" msgstr "Náhodné přehrávání" msgid "Normal play" msgstr "Normální přehrávání" msgid "DVD Menu" msgstr "" msgid "Exit DVD menu" msgstr "" msgid "DVD Root menu" msgstr "" msgid "DVD Title menu" msgstr "" msgid "DVD SPU menu" msgstr "" msgid "DVD Audio menu" msgstr "" msgid "Close menu" msgstr "" msgid "BluRay Top menu" msgstr "" msgid "Toggle Pop-Up menu" msgstr "" msgid "Next title" msgstr "" msgid "Previous title" msgstr "" msgid "Delete image ?" msgstr "Smazat obrázek ?" #~ msgid "normal" #~ msgstr "normální" #~ msgid "inverted" #~ msgstr "inverzní" #~ msgid "Interlaced Field Order" #~ msgstr "Pořadí půlsnímků" #~ msgid "Button$Sort" #~ msgstr "Třídění" #~ msgid "Play file >>" #~ msgstr "Přehrát soubor >>" #~ msgid "Play music >>" #~ msgstr "Přehrát hudbu >>" #~ msgid "View images >>" #~ msgstr "Prohlížet obrázky >>" #~ msgid "Play DVD disc >>" #~ msgstr "Přehrát DVD >>" #~ msgid "Play audio CD >>" #~ msgstr "Přehrát zvukové CD >>" #~ msgid "Play BluRay disc >>" #~ msgstr "Přehrát BluRay >>" #~ msgid "Play remote DVD >>" #~ msgstr "Přehrát vzdálené DVD >>" #~ msgid "Play remote CD >>" #~ msgstr "Přehrát vzdálené CD >>" #~ msgid "Audio equalizer >>" #~ msgstr "Korekce zvuku (ekvalizér) >>" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/patches/������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13061253352�013653� 5����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/patches/xinelib-1.2.0_vdpau_black_frame.patch�����������������������������������0000644�0001750�0001750�00000003621�13061253352�022471� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������diff --git a/src/video_out/video_out_vdpau.c b/src/video_out/video_out_vdpau.c --- a/src/video_out/video_out_vdpau.c +++ b/src/video_out/video_out_vdpau.c @@ -113,7 +113,9 @@ VdpOutputSurfaceRenderBlendState blend = VdpDevice vdp_device; VdpPresentationQueue vdp_queue; +VdpPresentationQueue prev_vdp_queue = VDP_INVALID_HANDLE; VdpPresentationQueueTarget vdp_queue_target; +VdpPresentationQueueTarget prev_vdp_queue_target = VDP_INVALID_HANDLE; VdpDeviceDestroy *vdp_device_destroy; @@ -2234,8 +2236,16 @@ static int vdpau_gui_data_exchange (vo_d pthread_mutex_lock(&this->drawable_lock); /* wait for other thread which is currently displaying */ DO_LOCKDISPLAY this->drawable = (Drawable) data; - vdp_queue_destroy( vdp_queue ); - vdp_queue_target_destroy( vdp_queue_target ); + + // Do not immideatly destory queue as the window would display a black frame + // Patch for xineliboutput to allow opening the hud without black frame + if (prev_vdp_queue!=VDP_INVALID_HANDLE) { + vdp_queue_destroy(prev_vdp_queue); + vdp_queue_target_destroy(prev_vdp_queue_target); + } + prev_vdp_queue=vdp_queue; + prev_vdp_queue_target=vdp_queue_target; + st = vdp_queue_target_create_x11( vdp_device, this->drawable, &vdp_queue_target ); if ( st != VDP_STATUS_OK ) { fprintf(stderr, "vo_vdpau: FATAL !! Can't recreate presentation queue target after drawable change !!\n" ); @@ -2298,6 +2308,10 @@ static void vdpau_dispose (vo_driver_t * if ( vdp_queue_target != VDP_INVALID_HANDLE ) vdp_queue_target_destroy( vdp_queue_target ); + if ( prev_vdp_queue != VDP_INVALID_HANDLE ) + vdp_queue_destroy( prev_vdp_queue ); + if ( prev_vdp_queue_target != VDP_INVALID_HANDLE ) + vdp_queue_target_destroy( prev_vdp_queue_target ); if ( this->video_mixer!=VDP_INVALID_HANDLE ) vdp_video_mixer_destroy( this->video_mixer ); ���������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/osd.h���������������������������������������������������������������������������0000644�0001750�0001750�00000001344�13061253352�013164� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * osd.h: Xinelib On Screen Display control * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_OSD_H #define __XINELIB_OSD_H #include <vdr/osd.h> class cXinelibDevice; class cXinelibOsdProvider : public cOsdProvider { protected: cXinelibDevice *m_Device; virtual bool ProvidesTrueColor(void); virtual cOsd *CreateOsd(int Left, int Top, uint Level); public: cXinelibOsdProvider(cXinelibDevice *Device); virtual ~cXinelibOsdProvider(); static void RefreshOsd(void); // VDR < 1.5.9 compability virtual cOsd *CreateOsd(int Left, int Top) { return CreateOsd(Left, Top, 0); } }; #endif //__XINELIB_OSD_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/osd.c���������������������������������������������������������������������������0000644�0001750�0001750�00000041161�13061253352�013160� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * osd.c: Xinelib On Screen Display control * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include <vdr/config.h> #include <vdr/tools.h> #include <vdr/thread.h> #include "logdefs.h" #include "config.h" #include "device.h" #include "tools/osd_command.h" #include "osd.h" #ifndef OSD_LEVEL_TTXTSUBS #define OSD_LEVEL_TTXTSUBS 20 // from ttxtsubs plugin #endif //#define LIMIT_OSD_REFRESH_RATE #define LOGOSD(x...) // // tools // static inline int saturate(int x, int min, int max) { return x < min ? min : (x > max ? max : x); } static inline void prepare_palette(osd_clut_t *clut, const unsigned int *palette, int colors, bool top, bool rgb) { if (colors) { int c; // Apply alpha layer correction and convert ARGB -> AYCrCb for(c=0; c<colors; c++) { int A = (palette[c] & 0xff000000) >> 24; int R = (palette[c] & 0x00ff0000) >> 16; int G = (palette[c] & 0x0000ff00) >> 8; int B = (palette[c] & 0x000000ff); A = A + xc.alpha_correction*A/100 + xc.alpha_correction_abs; if(rgb) { clut[c].r = R; clut[c].g = G; clut[c].b = B; clut[c].alpha = saturate( A, 0, 255); } else { int Y = (( + 66*R + 129*G + 25*B + 0x80) >> 8) + 16; int CR = (( + 112*R - 94*G - 18*B + 0x80) >> 8) + 128; int CB = (( - 38*R - 74*G + 112*B + 0x80) >> 8) + 128; clut[c].y = saturate( Y, 16, 235); clut[c].cb = saturate(CB, 16, 240); clut[c].cr = saturate(CR, 16, 240); clut[c].alpha = saturate( A, 0, 255); } } // Apply OSD mixer settings if(!top) { if(xc.osd_mixer & OSD_MIXER_ALPHA) for(c=0; c<colors; c++) clut[c].alpha >>= 1; /* fade */ if(xc.osd_mixer & OSD_MIXER_GRAY) for(c=0; c<colors; c++) { if(rgb) clut[c].r = clut[c].g = clut[c].b = (clut[c].r + clut[c].g + clut[c].b)/3; else clut[c].cb = clut[c].cr = 0x80; } } } } // // cXinelibOsd // class cXinelibOsd : public cOsd, public cListObject { private: cXinelibOsd(); cXinelibOsd(cXinelibOsd&); // no copy cXinelibDevice *m_Device; void CloseWindows(void); void CmdSize(int Width, int Height); void CmdVideoWindow(int X, int Y, int W, int H); void CmdArgb(int X, int Y, int W, int H, const unsigned char *Data, int DataLen); void CmdLut8(int Wnd, int X0, int Y0, int W, int H, unsigned char *Data, int Colors, unsigned int *Palette, osd_rect_t *DirtyArea); void CmdPalette(int Wnd, int Colors, unsigned int *Palette); void CmdMove(int Wnd, int Width, int Height); void CmdClose(int Wnd); void CmdFlush(void); void SetCmdFlags(osd_command_t& cmd); /* map single OSD window indexes to unique xine-side window handles */ static uint64_t m_HandlesBitmap; int *m_WindowHandles; int AllocWindowHandles(int NumWindows); void FreeWindowHandles(void); protected: static cMutex m_Lock; static cList<cXinelibOsd> m_OsdStack; bool m_IsVisible; bool m_Refresh; uint m_Layer; uint16_t m_ExtentWidth; uint16_t m_ExtentHeight; virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); virtual void Flush(void); // Messages from cXinelibOsdProvider void Show(void); void Hide(void); void Refresh(void); void Detach(void); friend class cXinelibOsdProvider; public: cXinelibOsd(cXinelibDevice *Device, int x, int y, uint Level = 0); virtual ~cXinelibOsd(); }; cList<cXinelibOsd> cXinelibOsd::m_OsdStack; cMutex cXinelibOsd::m_Lock; uint64_t cXinelibOsd::m_HandlesBitmap; int cXinelibOsd::AllocWindowHandles(int NumWindows) { uint64_t bit = 1; int index = 0, wnd = 0; FreeWindowHandles(); m_WindowHandles = new int[NumWindows+1]; for (index = 0; index < MAX_OSD_OBJECT; index++) { if (! (m_HandlesBitmap & bit)) { m_WindowHandles[wnd++] = index; m_HandlesBitmap |= bit; } if (wnd >= NumWindows) break; bit <<= 1; } m_WindowHandles[NumWindows] = -1; if (wnd < NumWindows) { LOGMSG("cXinelibOsd::AllocOsdHandles(): Too many open OSD windows !"); while(wnd < NumWindows) m_WindowHandles[wnd++] = -1; return 0; } return NumWindows; } void cXinelibOsd::FreeWindowHandles(void) { if (m_WindowHandles) { int wnd = 0; while (m_WindowHandles[wnd] >= 0) { m_HandlesBitmap &= ~( ((uint64_t)1) << m_WindowHandles[wnd]); wnd++; } delete [] m_WindowHandles; m_WindowHandles = NULL; } } void cXinelibOsd::CmdSize(int Width, int Height) { TRACEF("cXinelibOsd::CmdSize"); if (m_Device && m_WindowHandles) { osd_command_t osdcmd = {0}; osdcmd.cmd = OSD_Size; osdcmd.w = Width; osdcmd.h = Height; if (Prev() == NULL) osdcmd.flags |= OSDFLAG_TOP_LAYER; for (int Wnd = 0; m_WindowHandles[Wnd] >= 0; Wnd++) { osdcmd.wnd = m_WindowHandles[Wnd]; m_Device->OsdCmd((void*)&osdcmd); } } } void cXinelibOsd::CmdVideoWindow(int X, int Y, int W, int H) { TRACEF("cXinelibOsd::CmdVideoWindow"); if (m_Device && m_WindowHandles) { osd_command_t osdcmd = {0}; for (int Wnd = 0; m_WindowHandles[Wnd] >= 0; Wnd++) { osdcmd.cmd = OSD_VideoWindow; osdcmd.wnd = m_WindowHandles[Wnd]; osdcmd.x = X; osdcmd.y = Y; osdcmd.w = W; osdcmd.h = H; m_Device->OsdCmd((void*)&osdcmd); } } } void cXinelibOsd::CmdMove(int Wnd, int NewX, int NewY) { TRACEF("cXinelibOsd::CmdMove"); if (m_Device && m_WindowHandles) { osd_command_t osdcmd = {0}; osdcmd.cmd = OSD_Move; osdcmd.wnd = m_WindowHandles[Wnd]; osdcmd.x = NewX; osdcmd.y = NewY; m_Device->OsdCmd((void*)&osdcmd); } } void cXinelibOsd::CmdPalette(int Wnd, int Colors, unsigned int *Palette) { TRACEF("cXinelibOsd::CmdPalette"); if (m_Device) { osd_clut_t clut[Colors]; osd_command_t osdcmd = {0}; osdcmd.cmd = OSD_SetPalette; osdcmd.wnd = m_WindowHandles[Wnd]; osdcmd.palette = clut; osdcmd.colors = Colors; prepare_palette(&clut[0], Palette, Colors, /*Top*/(Prev() == NULL), true); m_Device->OsdCmd((void*)&osdcmd); } } void cXinelibOsd::CmdClose(int Wnd) { TRACEF("cXinelibOsd::CmdClose"); if (m_Device) { osd_command_t osdcmd = {0}; osdcmd.cmd = OSD_Close; osdcmd.wnd = m_WindowHandles[Wnd]; if (m_Refresh) osdcmd.flags |= OSDFLAG_REFRESH; if (Prev() == NULL) osdcmd.flags |= OSDFLAG_TOP_LAYER; m_Device->OsdCmd((void*)&osdcmd); } } void cXinelibOsd::CmdFlush(void) { TRACEF("cXinelibOsd::CmdFlush"); if (m_Device) { osd_command_t osdcmd = {0}; osdcmd.cmd = OSD_Flush; m_Device->OsdCmd((void*)&osdcmd); } } void cXinelibOsd::SetCmdFlags(osd_command_t& cmd) { if (m_Refresh) cmd.flags |= OSDFLAG_REFRESH; if (xc.osd_blending == OSD_BLENDING_HARDWARE) cmd.flags |= OSDFLAG_UNSCALED; if (xc.osd_blending_lowresvideo == OSD_BLENDING_HARDWARE) cmd.flags |= OSDFLAG_UNSCALED_LOWRES; if (Prev() == NULL) cmd.flags |= OSDFLAG_TOP_LAYER; cmd.scaling = xc.osd_scaling; if (m_Layer == OSD_LEVEL_TTXTSUBS) cmd.scaling = xc.osd_spu_scaling; } void cXinelibOsd::CmdArgb(int X, int Y, int W, int H, const unsigned char *Data, int DataLen) { TRACEF("cXinelibOsd::CmdArgb"); if (m_Device) { osd_command_t osdcmd = {0}; osdcmd.cmd = OSD_Set_ARGB; osdcmd.wnd = m_WindowHandles[0]; osdcmd.layer = saturate(m_Layer, 0, 0xffff); osdcmd.x = X; osdcmd.y = Y; osdcmd.w = W; osdcmd.h = H; osdcmd.dirty_area.x2 = W - 1; osdcmd.dirty_area.y2 = H - 1; SetCmdFlags(osdcmd); osdcmd.raw_data = (uint8_t*)Data; osdcmd.num_rle = 0; osdcmd.datalen = DataLen; m_Device->OsdCmd((void*)&osdcmd); } } void cXinelibOsd::CmdLut8(int Wnd, int X0, int Y0, int W, int H, unsigned char *Data, int Colors, unsigned int *Palette, osd_rect_t *DirtyArea) { TRACEF("cXinelibOsd::CmdLut8"); if (m_Device) { osd_clut_t clut[Colors]; osd_command_t osdcmd = {0}; osdcmd.cmd = OSD_Set_LUT8; osdcmd.wnd = m_WindowHandles[Wnd]; osdcmd.layer = saturate(m_Layer, 0, 0xffff); osdcmd.x = X0; osdcmd.y = Y0; osdcmd.w = W; osdcmd.h = H; osdcmd.colors = Colors; osdcmd.palette = clut; if (DirtyArea) memcpy(&osdcmd.dirty_area, DirtyArea, sizeof(osd_rect_t)); SetCmdFlags(osdcmd); prepare_palette(&clut[0], Palette, Colors, /*Top*/(Prev() == NULL), true); osdcmd.raw_data = Data; osdcmd.num_rle = 0; osdcmd.datalen = W * H; m_Device->OsdCmd((void*)&osdcmd); } } cXinelibOsd::cXinelibOsd(cXinelibDevice *Device, int x, int y, uint Level) : cOsd(x, y, Level) { TRACEF("cXinelibOsd::cXinelibOsd"); m_Device = Device; m_Refresh = false; m_IsVisible = true; m_Layer = Level; m_ExtentWidth = 720; m_ExtentHeight = 576; m_WindowHandles = NULL; } cXinelibOsd::~cXinelibOsd() { TRACEF("cXinelibOsd::~cXinelibOsd"); cMutexLock ml(&m_Lock); CloseWindows(); FreeWindowHandles(); m_OsdStack.Del(this, false); if(m_OsdStack.First()) m_OsdStack.First()->Show(); } eOsdError cXinelibOsd::SetAreas(const tArea *Areas, int NumAreas) { TRACEF("cXinelibOsd::SetAreas"); cMutexLock ml(&m_Lock); LOGOSD("cXinelibOsd::SetAreas"); // Close all existing windows CloseWindows(); FreeWindowHandles(); eOsdError Result = cOsd::SetAreas(Areas, NumAreas); if (Result != oeOk) return Result; // Allocate xine OSD window handles if (!AllocWindowHandles(NumAreas)) { FreeWindowHandles(); return oeTooManyAreas; } if (xc.osd_spu_scaling && m_Layer == OSD_LEVEL_TTXTSUBS) { m_ExtentWidth = 720; m_ExtentHeight = 576; } else { double Aspect; int W, H; m_Device->GetOsdSize(W, H, Aspect); m_ExtentWidth = W; m_ExtentHeight = H; } CmdSize(m_ExtentWidth, m_ExtentHeight); return Result; } eOsdError cXinelibOsd::CanHandleAreas(const tArea *Areas, int NumAreas) { TRACEF("cXinelibOsd::CanHandleAreas"); eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas); if (Result != oeOk) return Result; if (NumAreas > MAX_OSD_OBJECT) return oeTooManyAreas; // bpp if (NumAreas == 1 && Areas[0].bpp == 32) { if (!m_Device->SupportsTrueColorOSD()) { LOGDBG("cXinelibOsd::CanHandleAreas(): Device does not support ARGB"); return oeBppNotSupported; } } else { for (int i = 0; i < NumAreas; i++) { if (Areas[i].bpp < 1 || Areas[i].bpp > 8) { LOGMSG("cXinelibOsd::CanHandleAreas(): invalid bpp (%d)", Areas[i].bpp); return oeBppNotSupported; } } } // enough free xine OSD windows ? uint64_t bit = 1; int windows = NumAreas; for (int index = 0; index < MAX_OSD_OBJECT && windows > 0; index++) { if (! (m_HandlesBitmap & bit)) windows--; bit <<= 1; } if (windows > 0) { LOGMSG("cXinelibOsd::CanHandleAreas(): not enough free window handles !"); return oeTooManyAreas; } return oeOk; } void cXinelibOsd::Flush(void) { TRACEF("cXinelibOsd::Flush"); cMutexLock ml(&m_Lock); cBitmap *Bitmap; if(!m_IsVisible) return; const cRect& videoWindow = m_Device->GetVideoWindow(); if (videoWindow != cRect::Null) { CmdVideoWindow(videoWindow.X(), videoWindow.Y(), videoWindow.Width(), videoWindow.Height()); } if (IsTrueColor()) { LOCK_PIXMAPS; while (cPixmapMemory *pm = dynamic_cast<cPixmapMemory*>(RenderPixmaps())) { int w = pm->ViewPort().Width(); int h = pm->ViewPort().Height(); int d = w * sizeof(tColor); CmdArgb(Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y(), w, h, pm->Data(), h * d); #if VDRVERSNUM >= 20200 DestroyPixmap(pm); #else delete pm; #endif } return; } int SendDone = 0, XOffset = 0, YOffset = 0; if (!xc.osd_spu_scaling && m_Layer == OSD_LEVEL_TTXTSUBS) { double Aspect; int W, H; m_Device->GetOsdSize(W, H, Aspect); YOffset = (H - 576) > 0 ? (H - 576) : 0; XOffset = ((W - 720) / 2) ? ((W - 720) / 2) : 0; } for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { int x1 = 0, y1 = 0, x2 = x1+Bitmap->Width()-1, y2 = y1+Bitmap->Height()-1; if (m_Refresh || Bitmap->Dirty(x1, y1, x2, y2)) { /* XXX what if only palette has been changed ? */ int NumColors; const tColor *Colors = Bitmap->Colors(NumColors); if (Colors) { osd_rect_t DirtyArea = {x1:(uint16_t)x1, y1:(uint16_t)y1, x2:(uint16_t)x2, y2:(uint16_t)y2}; CmdLut8(i, Left() + Bitmap->X0() + XOffset, Top() + Bitmap->Y0() + YOffset, Bitmap->Width(), Bitmap->Height(), (unsigned char *)Bitmap->Data(0,0), NumColors, (unsigned int *)Colors, &DirtyArea); SendDone++; } } Bitmap->Clean(); } #ifdef LIMIT_OSD_REFRESH_RATE if(SendDone) { static int64_t last_refresh = 0LL; int64_t now = cTimeMs::Now(); if(now - last_refresh < 100) { /* too fast refresh rate, delay ... */ cCondWait::SleepMs(40); /* Can't update faster anyway ... */ # if 0 LOGDBG("cXinelibOsd::Flush: OSD refreshing too fast ! (>10Hz) -> Sleeping 50ms"); # endif } last_refresh = now; } #endif } void cXinelibOsd::Refresh(void) { TRACEF("cXinelibOsd::Refresh"); cMutexLock ml(&m_Lock); m_Refresh = true; CloseWindows(); CmdSize(m_ExtentWidth, m_ExtentHeight); Flush(); m_Refresh = false; } void cXinelibOsd::Show(void) { TRACEF("cXinelibOsd::Show"); cMutexLock ml(&m_Lock); m_IsVisible = true; Refresh(); } void cXinelibOsd::CloseWindows(void) { TRACEF("cXinelibOsd::CloseWindows"); if (m_IsVisible && m_WindowHandles) { for (int i = 0; m_WindowHandles[i] >= 0; i++) { LOGOSD("Close OSD %d.%d", Index(), i); CmdClose(i); } } if (!m_Refresh) CmdFlush(); } void cXinelibOsd::Hide(void) { TRACEF("cXinelibOsd::Hide"); cMutexLock ml(&m_Lock); CloseWindows(); m_IsVisible = false; } void cXinelibOsd::Detach(void) { TRACEF("cXinelibOsd::Detach"); cMutexLock ml(&m_Lock); Hide(); m_Device = NULL; } // // cXinelibOsdProvider // cXinelibOsdProvider::cXinelibOsdProvider(cXinelibDevice *Device) { m_Device = Device; } cXinelibOsdProvider::~cXinelibOsdProvider() { LOGMSG("cXinelibOsdProvider: shutting down !"); cMutexLock ml(&cXinelibOsd::m_Lock); m_Device = NULL; if(cXinelibOsd::m_OsdStack.First()) { LOGMSG("cXinelibOsdProvider: OSD open while OSD provider shutting down !"); // Detach all OSD instances from device cXinelibOsd *osd; while(NULL != (osd = cXinelibOsd::m_OsdStack.First())) { osd->Detach(); cXinelibOsd::m_OsdStack.Del(osd, false); } } } bool cXinelibOsdProvider::ProvidesTrueColor(void) { return m_Device && m_Device->SupportsTrueColorOSD(); } cOsd *cXinelibOsdProvider::CreateOsd(int Left, int Top, uint Level) { TRACEF("cXinelibOsdProvider::CreateOsd"); cMutexLock ml(&cXinelibOsd::m_Lock); cXinelibOsd *m_OsdInstance = new cXinelibOsd(m_Device, Left, Top, Level); // sorted insert cXinelibOsd *it = cXinelibOsd::m_OsdStack.First(); while(it) { if(it->m_Layer >= Level) { cXinelibOsd::m_OsdStack.Ins(m_OsdInstance, it); break; } it = cXinelibOsd::m_OsdStack.Next(it); } if(!it) cXinelibOsd::m_OsdStack.Add(m_OsdInstance); LOGOSD("New OSD: index %d, layer %d [now %d OSDs]", m_OsdInstance->Index(), Level, cXinelibOsd::m_OsdStack.Count()); if(xc.osd_mixer == OSD_MIXER_NONE) { // hide all but top-most OSD LOGOSD(" OSD mixer off"); it = cXinelibOsd::m_OsdStack.Last(); while(cXinelibOsd::m_OsdStack.Prev(it)) { LOGOSD(" -> hide OSD %d", it->Index()); it->Hide(); it = cXinelibOsd::m_OsdStack.Prev(it); } } else /*if(xc.osd_mixer > OSD_MIXER_NONE)*/ { LOGOSD("OSD mixer on (%d)", xc.osd_mixer); it = cXinelibOsd::m_OsdStack.Last(); while (cXinelibOsd::m_OsdStack.Prev(it)) { LOGOSD(" -> show OSD %d", it->Index()); it->Show(); it = cXinelibOsd::m_OsdStack.Prev(it); } } it->Show(); return m_OsdInstance; } void cXinelibOsdProvider::RefreshOsd(void) { TRACEF("cXinelibOsdProvider::RefreshOsd"); cMutexLock ml(&cXinelibOsd::m_Lock); // bottom --> top (draw lower layer OSDs first) cXinelibOsd *it = cXinelibOsd::m_OsdStack.Last(); while(it) { it->Refresh(); it = cXinelibOsd::m_OsdStack.Prev(it); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/nosignal_720x576.mpg������������������������������������������������������������0000644�0001750�0001750�00000031613�13061253352�015571� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,@3p�������# �����@���������3hѣ 74aF04hцƍ0xѣFk,hѣ 74aF04hцƍ0xѣFo4h d4aF04hцƍ0xѣFo4h Fahѣ 54hцƍ0xѣFo4h Fahѣ 74aF0(ѣFo4h Fahѣ 74aF04hц4h Fahѣ 74aF04hцƍ0xѣF`�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣ}sRl/4%-cr샭 n4~\euD5qlXof%(U* )heƍ,.[%x ,ƌߧ͖X#4eFW?6q;rc1"w;7 -[E̺xmXYC\n\eľ-FPƖPAFPrؖOF #xѥU1</.vXKdeQ7 %e!axѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74a�� hѣ 74aF04hцƍ0b<k{{h y` ,@H@A 4<7�?m@ t�-o8iS9@�e߽݉㍨L�'wupi�/p8meT+5&I`=TO~?ά@ �z�ZX�]y2{h'?�G�@,q<PR} �[A`@0ngd`@@@,"|uG�OH%opCR,]>7�71�,�\��WC�z���>�@�I�wPIAU>P mA �}@ӈ ֙S:A� � � &a؏phYM@ r�R\�`�_O� }}9wfהwP 8=����@K߻|`�@w`g@)ԸSn�V�7p|h@z~;6ZO!�Px|` |0 |px�R `@��/�l�n'J?3(x^KT{!V y`�<�-w "@_avlt)`0$��`$b f]m`@B( }xXa>h?��-x}iPO*S}��:�1�773�]x)0�b�u�u!?�:۴Ԃ�[�0�D�^��_А!�@{uClo��d�B�t�[�g1�!s+ޠ`�}$PJ-@  }X ~@@ ~_O,(duKA@�*�zF̄b8xf?']:Eo4�^�@ � Iz}�~/uMRXh���(?7��/W�7�-�>� H�0�ԋ_Hnem 0B{ER! @0 #h>R'}ymsbS�S`�@�F�nE�|�(Q |'vL|_9AQ@X ~+l� %m�B�D H>A� @W}@ {�r+KH;=x�N�z��G a`ndx�#4�.�m���W?_ A�?_@@h*6` @�1�\�;B�nP <uRO9c�@`@ @ .�o}6$(�n޹u)fBDTo^�v�  {H;�PGϿ&}T8 P@`@P r�+# \ $��@h!Q;T�f_5P@�@�PA?ހ�� (&"�D巕�L�B�?TR7}}pg]ӌ@  `O0n!7 \*{{@0@��@ �$�anq Y{a0oE�$9{�n�?3�t�aB7��eZႢ$ PJ�v/@#}�=2 P+.ªylK>,>�X� ~� @p@x�,]_�!#?t𷢛sA� @�9D$A7]oWr7PN<<X rA�M` &oN��t�F�PspF`F$K�>&c-A;�|@nD�@Tr��`_=@jѡ1 V@���7�R�S�k�@@�M|(6�K PT HdgK�4J @P&/!Cx&oʁ �4��DF[ƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fa�� hѣ 74aF04hцF!.h ʽj[BX޴`� x�l�GO@t�XC�m�<<{*7\J읋ya͈} �zF�:#ق��}~�b�٧ #m �1�Z�>9#{ <x5��G�O�/|�fD#�]FA�܈E&ǂ`8�vr7DqEB0|6@$(󉚤&?_ 6�`5+!g6�?� "ׂ�#�Cv`"�.� � ="�> #RB~"Z�}@ A� @˶s'/qAT$� `]P/> >ci}ԤEB#|3$K %wj[4qt��E�pn�9wCF��t�_`@Z� �"�*�+�n��/pBR.@BwfJ5i�E���,�e점�GE(�84ݠ@/@@ =<br(elOP6�պhQΏyaKkx��` st@XG�|�:~V"ّ[{@@�@@ ވo�~ �9xju X  ~'X?D>>?(` (a@ PqokͦOq"E NB8U;D8[�[?P@x#ofN,opD }?D?<?D�E�~���P$}�'" # t�X@Iq?p@DpAb>yoN~Y6�,& n �D��$翶�$�F��mkݦ�YLTX~x@jJ9  }p/ A�~>�@ q P~vm![��@��{i�@P�&Q"��@�c � �!l�"�c�P@`A�?_8y"l@*Ro�r�G�;�R��07�DP  �=xBIV[q@ �:�)j�kn9:�&l $n|�X(L0 0`  @ ` ~{|:d-@[܋h 5P7xS � 6ni��CA� �&}O } ~#���#ئ] m@ >B�F�\~Z�l'+�$�9Q@)p̩Q��2�X_?� OEO ZoȚ"FO/7-mɏUpyhzbV?4F�`�@ &'tz-p@ P@ Aq �Mx�p@ @a�o?bm`t�F�yQ� >R9Eu><ѣb�"�%�Th�DM/p@w׿ƀMHl̊>i;ɱd%"UczDVR6@�e3@{3�Oy3�=@!?"��91Οos\-KT�i#:L�HC+H27;mo: `H4hћ&F04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣF`�� hѣ 74aF04hцƍ0x@a44h H@ $1U4!Fo$2I F o$$HT0y CI4a2z44hŴ5~II4aƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74a�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц��! hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц��" hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц��# hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц��$ hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�����������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/mpg2c.c�������������������������������������������������������������������������0000644�0001750�0001750�00000001762�13061253352�013406� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2003-2006 Petri Hintukainen <phintuka@cc.hut.fi> * * This code is distributed under the terms and conditions of the * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. * * mpg2.c: * * $Id$ * */ #include <stdio.h> #define LINELEN 20 int main(int argc, char *argv[]) { int ch; int pos=1; if(argc != 4) { printf("%s - convert binary file to C code\n\n" "usage: %s variable inputfile outputfile\n", argv[0],argv[0]); return -1; } FILE *fi = fopen(argv[2],"rb"); FILE *fo = fopen(argv[3],"wt"); if(!fi ||!fo) { printf("Error opening files\n"); return -1; } fprintf(fo, "extern const unsigned char v_mpg_%s[] = \n \"", argv[1]); while(EOF != (ch = fgetc(fi))) { fprintf(fo, "\\x%02x", ch); if(pos++ > LINELEN) { fprintf(fo, "\"\n \""); pos=1; } } fprintf(fo, "\";\n\nextern const int v_mpg_%s_length = sizeof(v_mpg_%s);\n\n", argv[1], argv[1]); fclose(fi); fclose(fo); return 0; } ��������������xineliboutput-2.0.0/menuitems.h���������������������������������������������������������������������0000644�0001750�0001750�00000006614�13061253352�014412� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * menuitems.h: New menu item types * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_MENUITEMS_H_ #define __XINELIB_MENUITEMS_H_ #include <vdr/menuitems.h> static inline cString Label_SubMenu(const char *Label) { return cString::sprintf("%s >>", Label); } static inline cString Label_Ident(const char *Label) { return cString::sprintf(" %s", Label); } static inline cString Label_Separator(const char *Label) { return cString::sprintf("----- %s -----", Label); } static inline cOsdItem *SubMenuItem(const char *Label, eOSState state) { return new cOsdItem(Label_SubMenu(Label), state); } static inline cOsdItem *SeparatorItem(const char *Label) { cOsdItem *Item = new cOsdItem(Label_Separator(Label)); Item->SetSelectable(false); return Item; } // --- cMenuEditTypedIntItem ------------------------------------------------- class cMenuEditTypedIntItem : public cMenuEditIntItem { protected: cString type; cString zeroString; virtual void Set(void); public: cMenuEditTypedIntItem(const char *Name, const char *Type, int *Value, int Min = 0, int Max = INT_MAX, const char *ZeroString = NULL, const char *MinString = NULL, const char *MaxString = NULL); }; // --- cMenuEditOddIntItem ------------------------------------------------- class cMenuEditOddIntItem : public cMenuEditIntItem { public: cMenuEditOddIntItem(const char *Name, int *Value, int Min = 1, int Max = INT_MAX, const char *MinString = NULL, const char *MaxString = NULL); eOSState ProcessKey(eKeys Key); }; // --- cMenuEditFpIntItem ------------------------------------------------- // Fixed-point decimal number class cMenuEditFpIntItem : public cMenuEditIntItem { protected: int decimals; cString zeroString; virtual void Set(void); public: cMenuEditFpIntItem(const char *Name, int *Value, int Min = 1, int Max = INT_MAX, int Decimals = 1, const char *ZeroString = NULL, const char *MinString = NULL, const char *MaxString = NULL); }; // --- cMenuEditStraI18nItem ------------------------------------------------- class cMenuEditStraI18nItem : public cMenuEditIntItem { private: const char * const *strings; protected: virtual void Set(void); public: cMenuEditStraI18nItem(const char *Name, int *Value, int NumStrings, const char * const *Strings); }; // --- cFileListItem --------------------------------------------------------- class cFileListItem : public cOsdItem { private: cString m_Name; cString m_SubFile; bool m_IsDir, m_HasResume, m_ShowFlags, m_Up; bool m_IsDvd, m_IsBluRay; protected: virtual void Set(void); public: cFileListItem(const char *name, bool isDir, bool HasResume, const char *subfile, bool IsDvd = false, bool IsBluRay = false); cFileListItem(const char *name, bool isDir); const char *Name(void) { return m_Name; } const char *SubFile(void) { return m_SubFile; } bool IsDir(void) { return m_IsDir; } bool IsDvd(void) { return m_IsDvd; } bool IsBluRay(void) { return m_IsBluRay; } bool HasResume(void) { return m_HasResume; } virtual bool operator< (const cListObject &ListObject); virtual int Compare(const cListObject &ListObject) const; }; #endif //__XINELIB_MENUITEMS_H_ ��������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/menuitems.c���������������������������������������������������������������������0000644�0001750�0001750�00000013545�13061253352�014406� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * menuitems.c: New menu item types * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include <vdr/i18n.h> #include "menuitems.h" // --- cMenuEditTypedIntItem ------------------------------------------------- cMenuEditTypedIntItem::cMenuEditTypedIntItem(const char *Name, const char *Type, int *Value, int Min, int Max, const char *ZeroString, const char *MinString, const char *MaxString) :cMenuEditIntItem(Name,Value,Min,Max,MinString,MaxString) { type = Type ? Type : ""; zeroString = ZeroString ? ZeroString : NULL; Set(); } void cMenuEditTypedIntItem::Set(void) { if(*value == 0 && *zeroString) SetValue(zeroString); else if (minString && *value == min) SetValue(minString); else if (maxString && *value == max) SetValue(maxString); else SetValue(cString::sprintf("%d %s", *value, *type)); } // --- cMenuEditOddIntItem ------------------------------------------------------ cMenuEditOddIntItem::cMenuEditOddIntItem(const char *Name, int *Value, int Min, int Max, const char *MinString, const char *MaxString) :cMenuEditIntItem(Name,Value,Min,Max,MinString,MaxString) { value = Value; min = Min; max = Max; minString = MinString; maxString = MaxString; if (*value < min) *value = min; else if (*value > max) *value = max; Set(); } eOSState cMenuEditOddIntItem::ProcessKey(eKeys Key) { eOSState state = cMenuEditItem::ProcessKey(Key); if (state == osUnknown) { int newValue = *value; bool IsRepeat = Key & k_Repeat; Key = NORMALKEY(Key); switch (Key) { case kNone: break; case kLeft: newValue = *value - 2; fresh = true; if (!IsRepeat && newValue < min && max != INT_MAX) newValue = max; break; case kRight: newValue = *value + 2; fresh = true; if (!IsRepeat && newValue > max && min != INT_MIN) newValue = min; break; default: if (*value < min) { *value = min; Set(); } if (*value > max) { *value = max; Set(); } return state; } if (newValue != *value && (!fresh || min <= newValue) && newValue <= max) { *value = newValue; Set(); } state = osContinue; } return state; } // --- cMenuEditFpIntItem ---------------------------------------------------- cMenuEditFpIntItem::cMenuEditFpIntItem(const char *Name, int *Value, int Min, int Max, int Decimals, const char *ZeroString, const char *MinString, const char *MaxString) :cMenuEditIntItem(Name,Value,Min,Max,MinString,MaxString) { decimals = Decimals; zeroString = ZeroString ? ZeroString : NULL; Set(); } static int my_exp10(int x) { int r = 1; for (; x > 0; x--, r *= 10) ; return r; } void cMenuEditFpIntItem::Set(void) { if(*value == 0 && *zeroString) SetValue(zeroString); else if (minString && *value == min) SetValue(minString); else if (maxString && *value == max) SetValue(maxString); else SetValue(cString::sprintf("%1.1f", ((float)(*value)) / (float)my_exp10(decimals))); } // --- cMenuEditStraI18nItem ------------------------------------------------- cMenuEditStraI18nItem::cMenuEditStraI18nItem(const char *Name, int *Value, int NumStrings, const char * const *Strings) :cMenuEditIntItem(Name, Value, 0, NumStrings - 1) { strings = Strings; Set(); } void cMenuEditStraI18nItem::Set(void) { SetValue(tr(strings[*value])); } // --- cFileListItem ------------------------------------------------- cFileListItem::cFileListItem(const char *name, bool isDir) { m_Name = name; m_IsDir = isDir; m_IsDvd = false; m_IsBluRay = false; m_HasResume = false; m_SubFile = NULL; m_ShowFlags = false; m_Up = m_IsDir && !strcmp(m_Name, ".."); Set(); } cFileListItem::cFileListItem(const char *name, bool IsDir, bool HasResume, const char *subfile, bool IsDvd, bool IsBluRay) { m_Name = name; m_IsDir = IsDir; m_IsDvd = IsDvd; m_IsBluRay = IsBluRay; m_HasResume = HasResume; m_SubFile = subfile; m_ShowFlags = true; m_Up = m_IsDir && !strcmp(m_Name, ".."); Set(); } void cFileListItem::Set(void) { cString txt; const char *pt; if(m_ShowFlags) { if(m_IsDir) { if(m_IsDvd) txt = cString::sprintf("\tD\t[%s] ", *m_Name); // text2skin requires space at end of string to display item correctly ... else if (m_IsBluRay) txt = cString::sprintf("\tB\t[%s] ", *m_Name); else txt = cString::sprintf("\t\t[%s] ", *m_Name); // text2skin requires space at end of string to display item correctly ... } else { txt = cString::sprintf("%c\t%c\t%s", m_HasResume ? ' ' : '*', *m_SubFile ? 'S' : m_IsDvd ? 'D' : m_IsBluRay ? 'B' : ' ', *m_Name); if(NULL != (pt = strrchr(txt,'.'))) txt.Truncate(pt - txt); } } else { if(m_IsDir) { txt = cString::sprintf("[%s] ", *m_Name); // text2skin requires space at end of string to display item correctly ... } else { txt = m_Name; if(NULL != (pt = strrchr(txt,'.'))) txt.Truncate(pt - txt); } } SetText(txt); } int cFileListItem::Compare(const cListObject &ListObject) const { cFileListItem *other = (cFileListItem *)&ListObject; if(m_IsDir && !other->m_IsDir) return -1; if(!m_IsDir && other->m_IsDir) return 1; if(m_Up && !other->m_Up) return -1; if(!m_Up && other->m_Up) return 1; return strcmp(m_Name, other->m_Name); } bool cFileListItem::operator< (const cListObject &ListObject) { cFileListItem *other = (cFileListItem *)&ListObject; if(m_IsDir && !other->m_IsDir) return true; if(!m_IsDir && other->m_IsDir) return false; if(m_Up && !other->m_Up) return true; if(!m_Up && other->m_Up) return false; return strcmp(m_Name, other->m_Name) < 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/menu.h��������������������������������������������������������������������������0000644�0001750�0001750�00000001671�13061253352�013346� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * menu.h: Main Menu * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_MENU_H #define __XINELIB_MENU_H #include <vdr/menuitems.h> class cXinelibDevice; class cMenuXinelib : public cMenuSetupPage { private: cXinelibDevice *m_Dev; int compression; int autocrop; int overscan; int novideo; // Hotkeys enum { hkInit, hkSeen, hkNone } hotkey_state; virtual eOSState ProcessHotkey(eKeys Key); cOsdItem *audio_ctrl_compress; cOsdItem *ctrl_autocrop; cOsdItem *ctrl_overscan; cOsdItem *ctrl_novideo; protected: virtual void Store(void); public: cMenuXinelib(cXinelibDevice *Dev); virtual ~cMenuXinelib(); virtual eOSState ProcessKey(eKeys Key); static cOsdMenu *CreateMenuBrowseFiles(cXinelibDevice *Dev, eMainMenuMode mode, bool Queue=true); }; #endif //__XINELIB_SETUP_MENU_H �����������������������������������������������������������������������xineliboutput-2.0.0/menu.c��������������������������������������������������������������������������0000644�0001750�0001750�00000073237�13061253352�013350� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * menu.c: Main Menu * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "features.h" #include <dirent.h> #ifdef HAVE_LIBBLURAY # include <libbluray/bluray.h> #endif #include <vdr/config.h> #include <vdr/interface.h> #include <vdr/menu.h> #include <vdr/plugin.h> #include <vdr/videodir.h> #include <vdr/i18n.h> #include "logdefs.h" #include "config.h" #include "menu.h" #include "menuitems.h" #include "tools/metainfo_menu.h" #include "tools/playlist.h" #include "device.h" #include "media_player.h" #include "equalizer.h" #ifndef HOTKEY_START # define HOTKEY_START kRed # define HOTKEY_DVD k0 /* */ # define HOTKEY_DVD_TRACK1 k1 /* */ # define HOTKEY_RESERVED k2 /* */ # define HOTKEY_NEXT_ASPECT k3 /* auto, 4:3, 16:9 */ # define HOTKEY_TOGGLE_CROP k4 /* off, force, auto */ # define HOTKEY_UPMIX k5 /* off, on */ # define HOTKEY_DOWNMIX k6 /* off, on */ # define HOTKEY_DEINTERLACE k7 /* off, on */ # define HOTKEY_LOCAL_FE k8 /* off, on */ # define HOTKEY_PLAYLIST k9 /* Start replaying playlist or file pointed by symlink $(CONFDIR)/plugins/xineliboutput/default_playlist */ # define HOTKEY_ADELAY_UP kUp /* audio delay up */ # define HOTKEY_ADELAY_DOWN kDown /* audio delay down */ # define HOTKEY_TOGGLE_VO_ASPECT kRight #endif //#define OLD_TOGGLE_FE #define ISNUMBERKEY(k) (RAWKEY(k) >= k0 && RAWKEY(k) <= k9) //----------------------------- cMenuBluray ---------------------------------- static bool BlurayMenuSupported(const cString& Path) { bool result = false; #ifdef HAVE_LIBBLURAY BLURAY *bdh = bd_open(Path, NULL); if (bdh) { const BLURAY_DISC_INFO *di = bd_get_disc_info(bdh); if (di->bluray_detected && !di->num_unsupported_titles) result = true; bd_close(bdh); } #endif return result; } class cMenuBluray : public cOsdMenu { private: cXinelibDevice *m_Dev; cString m_Path; public: cMenuBluray(cXinelibDevice *Dev, const char *Path); virtual ~cMenuBluray() {}; virtual eOSState ProcessKey(eKeys Key); }; cMenuBluray::cMenuBluray(cXinelibDevice *Dev, const char *Path) : cOsdMenu("BluRay") { m_Dev = Dev; m_Path = Path; Add(new cOsdItem(tr("Play movie title"), osUser1)); Add(new cOsdItem(tr("Play disc"), osUser2)); Display(); } eOSState cMenuBluray::ProcessKey(eKeys Key) { eOSState state = cOsdMenu::ProcessKey(Key); switch (state) { case osUser1: cPlayerFactory::Launch(m_Dev, pmAudioVideo, cPlaylist::BuildMrl("bluray", *m_Path), NULL, true); return osEnd; case osUser2: cPlayerFactory::Launch(m_Dev, pmAudioVideo, cPlaylist::BuildMrl("bd", *m_Path), NULL, true); return osEnd; case osBack: case osEnd: return osEnd; default: break; } return state; } //--------------------------- cMenuBrowseFiles ------------------------------- class cMenuBrowseFiles : public cOsdMenu { protected: cXinelibDevice *m_Dev; const eMainMenuMode m_Mode; bool m_OnlyQueue; cString m_CurrentDir; char *m_ConfigLastDir; const char *help[4]; virtual bool ScanDir(const char *DirName); virtual eOSState Open(bool ForceOpen = false, bool Queue = false, bool Rewind = false); virtual eOSState Delete(void); virtual eOSState Info(void); virtual void Set(void); virtual void SetHelpButtons(void); cFileListItem *GetCurrent(void) { return (cFileListItem *)Get(Current()); } void StoreConfig(void); char *GetLastDir(void); public: cMenuBrowseFiles(cXinelibDevice *Dev, eMainMenuMode mode = ShowFiles, bool OnlyQueue=false); ~cMenuBrowseFiles(); virtual eOSState ProcessKey(eKeys Key); }; static cString ParentDir(const char *dir) { char *result = strdup(dir); char *pt = strrchr(result, '/'); if (pt) { *(pt+1)=0; if (pt != result) *pt = 0; } return cString(result, true); } static cString LastDir(const char *dir) { const char *pt = strrchr(dir, '/'); if (pt && pt[0] && pt[1]) return cString(pt+1); return cString(NULL); } cMenuBrowseFiles::cMenuBrowseFiles(cXinelibDevice *Dev, eMainMenuMode mode, bool OnlyQueue) : cOsdMenu( ( mode==ShowImages ? tr("Images") : mode==ShowMusic ? (!OnlyQueue ? tr("Play music") : tr("Add to playlist")) : /*mode==ShowFiles ?*/ tr("Play file")), 2, 4), m_Mode(mode) { m_Dev = Dev; m_OnlyQueue = OnlyQueue; m_ConfigLastDir = GetLastDir(); Set(); } cMenuBrowseFiles::~cMenuBrowseFiles() { cPlugin *p = cPluginManager::GetPlugin(PLUGIN_NAME_I18N); if (p) { p->SetupStore("Media.RootDir", xc.media_root_dir); } Setup.Save(); } char *cMenuBrowseFiles::GetLastDir(void) { switch (m_Mode) { case ShowMusic: return xc.browse_music_dir; case ShowImages: return xc.browse_images_dir; default: case ShowFiles: return xc.browse_files_dir; } return xc.browse_files_dir; } void cMenuBrowseFiles::Set(void) { Clear(); if (!*m_CurrentDir) m_CurrentDir = m_ConfigLastDir; int RootDirLen = strlen(xc.media_root_dir); if (strncmp(m_CurrentDir, xc.media_root_dir, RootDirLen)) { LOGMSG("Not allowing browsing to %s (root is %s)", *m_CurrentDir, xc.media_root_dir); m_CurrentDir = xc.media_root_dir; } if (m_CurrentDir[0] != '/') { #if defined(APIVERSNUM) && (APIVERSNUM < 20102) m_CurrentDir = VideoDirectory; #else m_CurrentDir = cVideoDirectory::Name(); #endif } // find deepest accessible directory from path while (!ScanDir(m_CurrentDir) && strlen(m_CurrentDir) > 1) { m_CurrentDir = ParentDir(m_CurrentDir); } // add link to parent folder int CurrentDirLen = strlen(m_CurrentDir); if (CurrentDirLen > 1 && CurrentDirLen > RootDirLen) Add(new cFileListItem("..",true)); Sort(); SetCurrent(Get(Count()>1 && CurrentDirLen>1 ? 1 : 0)); // select last selected item cString lastParent = ParentDir(m_ConfigLastDir); if (!strncmp(m_CurrentDir, lastParent, CurrentDirLen)) { cString item = LastDir(m_ConfigLastDir); if (*item) { for (cFileListItem *it = (cFileListItem*)First(); it; it = (cFileListItem*)Next(it)) if (!strcmp(it->Name(), item)) SetCurrent(it); } } strn0cpy(m_ConfigLastDir, m_CurrentDir, sizeof(xc.browse_files_dir)); StoreConfig(); SetHelpButtons(); Display(); } void cMenuBrowseFiles::StoreConfig(void) { cPlugin *p = cPluginManager::GetPlugin(PLUGIN_NAME_I18N); if (p) { p->SetupStore("Media.BrowseMusicDir", xc.browse_music_dir); p->SetupStore("Media.BrowseFilesDir", xc.browse_files_dir); p->SetupStore("Media.BrowseImagesDir", xc.browse_images_dir); #if 1 // delete old keys (<1.0.0) p->SetupStore("BrowseMusicDir"); p->SetupStore("BrowseFilesDir"); p->SetupStore("BrowseImagesDir"); #endif } } void cMenuBrowseFiles::SetHelpButtons(void) { bool isDir = !GetCurrent() || GetCurrent()->IsDir(); bool isFile = !isDir; bool bDel = isFile && xc.media_enable_delete; if (isDir && !strcmp("..", GetCurrent()->Name())) { help[0] = help[1] = help[2] = help[3] = NULL; } else if (m_Mode == ShowMusic) { help[0] = isDir ? trVDR("Button$Play") : NULL; help[1] = tr ("Button$Queue"); help[2] = bDel ? trVDR("Button$Delete") : NULL; help[3] = isFile ? trVDR("Button$Info") : NULL; } else if (m_Mode == ShowImages) { help[0] = isDir ? trVDR("Button$Play") : NULL; help[1] = NULL; help[2] = bDel ? trVDR("Button$Delete") : NULL; help[3] = isFile ? trVDR("Button$Info") : NULL; } else { bool isDvd = GetCurrent() && (GetCurrent()->IsDvd() || GetCurrent()->IsBluRay()); bool hasResume = GetCurrent() && GetCurrent()->HasResume(); help[0] = isDir && isDvd ? trVDR("Button$Open") : NULL; help[1] = hasResume ? trVDR("Button$Rewind") : NULL; help[2] = bDel ? trVDR("Button$Delete") : NULL; help[3] = isFile ? trVDR("Button$Info") : NULL; } SetHelp(help[0], help[1], help[2], help[3]); } eOSState cMenuBrowseFiles::Delete(void) { cFileListItem *it = GetCurrent(); if (!it->IsDir()) { if (Interface->Confirm(trVDR("Delete recording?"))) { cString name = cString::sprintf("%s/%s", (const char *)m_CurrentDir, it->Name()); if (!unlink(name)) { isyslog("file %s deleted", *name); if (m_Mode != ShowImages) { name = cString::sprintf("%s.resume", *name); unlink(name); } cOsdMenu::Del(Current()); SetHelpButtons(); Display(); } else { Skins.Message(mtError, trVDR("Error while deleting recording!")); isyslog("Error deleting file %s", *name); } } } return osContinue; } eOSState cMenuBrowseFiles::Open(bool ForceOpen, bool Queue, bool Rewind) { if (!GetCurrent()) { return osContinue; } /* parent directory */ if (!strcmp("..", GetCurrent()->Name())) { m_CurrentDir = ParentDir(m_CurrentDir); Set(); return osContinue; /* directory */ } else if (GetCurrent()->IsDir()) { if (!ForceOpen && GetCurrent()->IsDvd()) { /* play dvd */ cPlayerFactory::Launch(m_Dev, pmAudioVideo, cPlaylist::BuildMrl("dvd", *m_CurrentDir, "/", GetCurrent()->Name()), NULL, true); return osEnd; } if (!ForceOpen && GetCurrent()->IsBluRay()) { cString bd_path = cString::sprintf("%s/%s/", *m_CurrentDir, GetCurrent()->Name()); if (BlurayMenuSupported(bd_path)) { AddSubMenu(new cMenuBluray(m_Dev, bd_path)); return osContinue; } /* play BluRay disc/image */ cPlayerFactory::Launch(m_Dev, pmAudioVideo, cPlaylist::BuildMrl("bluray", *m_CurrentDir, "/", GetCurrent()->Name(), "/"), NULL, true); return osEnd; } if (ForceOpen && GetCurrent()->IsDir() && !GetCurrent()->IsDvd() && !GetCurrent()->IsBluRay()) { /* play all files */ if (m_Mode != ShowImages) { if (m_OnlyQueue && !Queue) return osContinue; cString f = cString::sprintf("%s/%s/", *m_CurrentDir, GetCurrent()->Name()); if (!Queue || !cPlayerFactory::IsOpen()) cControl::Shutdown(); if (Queue) cPlayerFactory::Queue(m_Dev, f); else cPlayerFactory::Launch(m_Dev, m_Mode == ShowFiles ? pmAudioVideo : pmAudioOnly, f, NULL, true); return Queue ? osContinue : osEnd; } else { // TODO: show all images } } /* go to directory */ const char *d = GetCurrent()->Name(); char *buffer = NULL; if (asprintf(&buffer, "%s/%s", *m_CurrentDir, d) >= 0) { while (buffer[0] == '/' && buffer[1] == '/') memmove(buffer, buffer+1, strlen(buffer)); m_CurrentDir = cString(buffer, true); } Set(); return osContinue; /* regular file */ } else { cString f = cString::sprintf("%s/%s", *m_CurrentDir, GetCurrent()->Name()); strn0cpy(m_ConfigLastDir, f, sizeof(xc.browse_files_dir)); StoreConfig(); if (m_Mode != ShowImages) { /* video/audio */ if (m_OnlyQueue && !Queue) return osContinue; if (!Queue || !cPlayerFactory::IsOpen()) cControl::Shutdown(); if (Queue) cPlayerFactory::Queue(m_Dev, f); if (!cPlayerFactory::IsOpen()) { if (Rewind) unlink(cString::sprintf("%s.resume", *f)); if (GetCurrent()->IsBluRay()) { AddSubMenu(new cMenuBluray(m_Dev, f)); return osContinue; } if (GetCurrent()->IsDvd()) cPlayerFactory::Launch(m_Dev, pmAudioVideo, cPlaylist::BuildMrl("dvd", f), NULL, true); else cPlayerFactory::Launch(m_Dev, m_Mode == ShowFiles ? pmAudioVideo : pmAudioOnly, f, GetCurrent()->SubFile(), true); } if (Queue) return osContinue; } else { /* image */ cPlaylist *Playlist = new cPlaylist(); for (cFileListItem *it = (cFileListItem*)First(); it; it=(cFileListItem*)Next(it)) { if (!it->IsDir()) Playlist->Read(cString::sprintf("%s/%s", *m_CurrentDir, it->Name())); if (it == Get(Current())) Playlist->SetCurrent(Playlist->Last()); } cPlayerFactory::Launch(m_Dev, pmVideoOnly, Playlist, true); } return osEnd; } return osContinue; } eOSState cMenuBrowseFiles::Info(void) { if (GetCurrent() && !GetCurrent()->IsDir()) { cString filename = cString::sprintf("%s/%s", *m_CurrentDir, GetCurrent()->Name()); return AddSubMenu(new cMetainfoMenu(filename)); } return osContinue; } bool cMenuBrowseFiles::ScanDir(const char *DirName) { DIR *d = opendir(DirName); if (d) { struct dirent *e; while ((e = readdir(d)) != NULL) { if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..") && (e->d_name[0] != '.' || xc.show_hidden_files)) { cString buffer = cString::sprintf("%s/%s", DirName, e->d_name); struct stat st; if (stat(buffer, &st) == 0) { // check symlink destination if (S_ISLNK(st.st_mode)) { buffer = ReadLink(buffer); if (!*buffer || stat(buffer, &st)) continue; } // folders if (S_ISDIR(st.st_mode)) { if (m_Mode == ShowImages || m_Mode == ShowMusic) Add(new cFileListItem(e->d_name, true)); else Add(new cFileListItem(e->d_name, true, false, NULL, xc.IsDvdFolder(buffer), xc.IsBluRayFolder(buffer))); // regular files } else if (e->d_name[0] != '.') { // audio if (m_Mode == ShowMusic && xc.IsAudioFile(buffer)) { Add(new cFileListItem(e->d_name, false)); // images } else if (m_Mode == ShowImages && xc.IsImageFile(buffer)) { Add(new cFileListItem(e->d_name, false)); // BluRay image (.iso) } else if (m_Mode == ShowFiles && xc.IsBluRayImage(buffer)) { Add(new cFileListItem(e->d_name, false, false, NULL, false, true)); // DVD image (.iso) } else if (m_Mode == ShowFiles && xc.IsDvdImage(buffer)) { Add(new cFileListItem(e->d_name, false, false, NULL, true)); // video } else if (m_Mode == ShowFiles && xc.IsVideoFile(buffer)) { cString subfile; cString resumefile; // separate subtitles ? cString basename = cString::sprintf("%s/%s", DirName, e->d_name); const char *p = strrchr(basename, '.'); if (p) basename.Truncate(p - basename); int i; for (i=0; xc.s_subExts[i] && !*subfile; i++) { cString tmp = cString::sprintf("%s%s", *basename, xc.s_subExts[i]); if (stat(tmp, &st) == 0) subfile = tmp; } // resume file ? resumefile = cString::sprintf("%s/%s.resume", DirName, e->d_name); if (stat(resumefile, &st) != 0) resumefile = NULL; Add(new cFileListItem(e->d_name, false, *resumefile, subfile)); } } } } } closedir(d); return true; } return false; } eOSState cMenuBrowseFiles::ProcessKey(eKeys Key) { eOSState state = cOsdMenu::ProcessKey(Key); if (state == osUnknown) { switch (Key) { case kPlay: case kOk: return Open(false, m_OnlyQueue); case kRed: if (help[0]) return Open(true); break; case kGreen: if (help[1]) return Open(true, m_Mode == ShowMusic ? m_OnlyQueue=true : false, m_Mode != ShowMusic); break; case kYellow: if (help[2]) return Delete(); break; case kBlue: if (help[3]) return Info(); break; default: break; } } if (state == osUnknown) state = osContinue; if (!HasSubMenu() && Key != kNone) SetHelpButtons(); return state; } //----------------------------- cMenuXinelib --------------------------------- #include "tools/display_message.h" cMenuXinelib::cMenuXinelib(cXinelibDevice *Dev) { m_Dev = Dev; compression = xc.audio_compression; autocrop = xc.autocrop; overscan = xc.overscan; hotkey_state = hkInit; novideo = m_Dev->GetPlayMode() == pmAudioOnlyBlack ? 1 : 0; audio_ctrl_compress = NULL; ctrl_autocrop = NULL; ctrl_overscan = NULL; ctrl_novideo = NULL; Add(SeparatorItem(tr("Media"))); if (xc.media_menu_items & MEDIA_MENU_FILES) Add(SubMenuItem(tr("Play file"), osUser1)); if (xc.media_menu_items & MEDIA_MENU_MUSIC) Add(SubMenuItem(tr("Play music"), osUser2)); if (xc.media_menu_items & MEDIA_MENU_IMAGES) Add(SubMenuItem(tr("View images"), osUser3)); if (xc.media_menu_items & MEDIA_MENU_DVD) Add(SubMenuItem(tr("Play DVD disc"), osUser4)); if (xc.media_menu_items & MEDIA_MENU_BLURAY) Add(SubMenuItem(tr("Play BluRay disc"), osUser5)); if (xc.media_menu_items & MEDIA_MENU_CD) Add(SubMenuItem(tr("Play audio CD"), osUser6)); if (xc.media_menu_items & MEDIA_MENU_VIDEO_SETUP) { Add(SeparatorItem(tr("Video settings"))); Add(ctrl_novideo = new cMenuEditBoolItem(tr("Play only audio"), &novideo)); Add(ctrl_autocrop = new cMenuEditBoolItem(tr("Crop letterbox 4:3 to 16:9"), &autocrop)); Add(ctrl_overscan = new cMenuEditTypedIntItem(tr("Overscan (crop image borders)"), "%", &overscan, 0, 10, tr("Off"))); } if (xc.media_menu_items & MEDIA_MENU_AUDIO_SETUP) { Add(SeparatorItem(tr("Audio settings"))); Add(audio_ctrl_compress = new cMenuEditTypedIntItem(tr("Audio Compression"), "%", &compression, 100, 500, NULL, tr("Off"))); Add(SubMenuItem(tr("Audio equalizer"), osUser7)); } switch (xc.main_menu_mode) { case ShowFiles: case ShowMusic: case ShowImages: AddSubMenu(new cMenuBrowseFiles(m_Dev, xc.main_menu_mode)); break; default: break; } xc.main_menu_mode = ShowMenu; } cMenuXinelib::~cMenuXinelib() { if (xc.audio_compression != compression) m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); if (xc.overscan != overscan) m_Dev->ConfigureVideo(xc.hue, xc.saturation, xc.brightness, xc.sharpness, xc.noise_reduction, xc.contrast, xc.overscan, xc.vo_aspect_ratio); if (xc.autocrop != autocrop) m_Dev->ConfigurePostprocessing("autocrop", xc.autocrop ? true : false, xc.AutocropOptions()); int dev_novideo = m_Dev->GetPlayMode() == pmAudioOnlyBlack ? 1 : 0; if (dev_novideo != novideo) m_Dev->SetPlayMode(novideo ? pmAudioOnlyBlack : pmNone); } cOsdMenu *cMenuXinelib::CreateMenuBrowseFiles(cXinelibDevice *Dev, eMainMenuMode mode, bool Queue) { return new cMenuBrowseFiles(Dev, mode, true); } eOSState cMenuXinelib::ProcessKey(eKeys Key) { /* Hot key support */ if (hotkey_state == hkInit && Key == kNone) return osContinue; if (hotkey_state == hkInit && Key == HOTKEY_START) { hotkey_state = hkSeen; return osContinue; } else if (hotkey_state == hkSeen && Key != kNone) { hotkey_state = hkNone; return ProcessHotkey(Key); } hotkey_state = hkNone; cOsdItem *item = Get(Current()); eOSState state = cMenuSetupPage::ProcessKey(Key); if (HasSubMenu()) return state; switch (state) { case osUser1: AddSubMenu(new cMenuBrowseFiles(m_Dev, ShowFiles)); return osUnknown; case osUser2: AddSubMenu(new cMenuBrowseFiles(m_Dev, ShowMusic)); return osUnknown; case osUser3: AddSubMenu(new cMenuBrowseFiles(m_Dev, ShowImages)); return osContinue; case osUser4: cPlayerFactory::Launch(m_Dev, "dvd:/"); return osEnd; case osUser5: AddSubMenu(new cMenuBluray(m_Dev, NULL)); return osContinue; case osUser6: cPlayerFactory::Launch(m_Dev, "cdda:/"); return osEnd; case osUser7: if (!xc.pending_menu_action) { xc.pending_menu_action = new cEqualizer(m_Dev); return osPlugin; } return osContinue; default: ; } Key = NORMALKEY(Key); if (Key == kLeft || Key == kRight || ISNUMBERKEY(Key)) { if (item == audio_ctrl_compress) m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); else if (item == ctrl_overscan) m_Dev->ConfigureVideo(xc.hue, xc.saturation, xc.brightness, xc.sharpness, xc.noise_reduction, xc.contrast, overscan, xc.vo_aspect_ratio); } if (Key == kLeft || Key == kRight) { if (item == ctrl_autocrop) m_Dev->ConfigurePostprocessing("autocrop", autocrop?true:false, xc.AutocropOptions()); else if (item == ctrl_novideo) m_Dev->SetPlayMode(novideo ? pmAudioOnlyBlack : pmNone); } return state; } void cMenuXinelib::Store(void) { xc.audio_compression = compression; xc.autocrop = autocrop; xc.overscan = overscan; } eOSState cMenuXinelib::ProcessHotkey(eKeys Key) { eOSState NewState = osEnd; cString Message; time_t now = time(NULL); bool OnlyInfo = ((xc.last_hotkey_time < now-3) || xc.last_hotkey != Key); switch (Key) { case HOTKEY_DVD: cPlayerFactory::Launch(m_Dev, "dvd:/"); break; case HOTKEY_DVD_TRACK1: cPlayerFactory::Launch(m_Dev, "dvd:/1"); break; case HOTKEY_LOCAL_FE: /* off, on */ { int local_frontend = strstra(xc.local_frontend, xc.s_frontends, 0); #ifndef OLD_TOGGLE_FE if (local_frontend==FRONTEND_NONE) // no need to show current frontend if there is no output device ... OnlyInfo = false; #endif if (!OnlyInfo) { #ifndef OLD_TOGGLE_FE static int orig_frontend = -1; if (orig_frontend < 0) orig_frontend = local_frontend; if (orig_frontend == FRONTEND_NONE) { // no frontends were loaded at startup -> loop thru all frontends local_frontend++; } else { // frontend was loaded at startup -> toggle it on/off if (local_frontend == FRONTEND_NONE) local_frontend = orig_frontend; else local_frontend = FRONTEND_NONE; } #else local_frontend++; #endif if (local_frontend >= FRONTEND_count) local_frontend = 0; strn0cpy(xc.local_frontend, xc.s_frontends[local_frontend], sizeof(xc.local_frontend)); m_Dev->ConfigureWindow( xc.fullscreen, xc.width, xc.height, xc.modeswitch, xc.modeline, xc.display_aspect, xc.scale_video); } Message = cString::sprintf("%s %s %s", tr("Local Frontend"), OnlyInfo ? ":" : "->", xc.s_frontendNames[local_frontend]); } break; case HOTKEY_NEXT_ASPECT: /* auto, 4:3, 16:9, ... */ if (!OnlyInfo) { xc.display_aspect = (xc.display_aspect < ASPECT_count-1) ? xc.display_aspect+1 : 0; m_Dev->ConfigureWindow(xc.fullscreen, xc.width, xc.height, xc.modeswitch, xc.modeline, xc.display_aspect, xc.scale_video); } Message = cString::sprintf("%s %s %s", tr("Aspect ratio"), OnlyInfo ? ":" : "->", tr(xc.s_aspects[xc.display_aspect])); break; case HOTKEY_TOGGLE_VO_ASPECT: /* auto, square, 4:3, anamorphic or DVB */ if (!OnlyInfo) { xc.vo_aspect_ratio = (xc.vo_aspect_ratio < VO_ASPECT_count-1) ? xc.vo_aspect_ratio + 1 : 0; m_Dev->ConfigureVideo(xc.hue, xc.saturation, xc.brightness, xc.sharpness, xc.noise_reduction, xc.contrast, xc.overscan, xc.vo_aspect_ratio); } Message = cString::sprintf("%s %s %s", tr("Video aspect ratio"), OnlyInfo ? ":" : "->", tr(xc.s_vo_aspects[xc.vo_aspect_ratio])); break; case HOTKEY_TOGGLE_CROP: /* off, force, auto */ if (!OnlyInfo) { if (!xc.autocrop) { xc.autocrop = 1; xc.autocrop_autodetect = 1; } else if (xc.autocrop_autodetect) { xc.autocrop_autodetect = 0; } else { xc.autocrop = 0; } m_Dev->ConfigurePostprocessing("autocrop", xc.autocrop ? true : false, xc.AutocropOptions()); } Message = cString::sprintf("%s %s %s", tr("Crop letterbox 4:3 to 16:9"), OnlyInfo ? ":" : "->", !xc.autocrop ? tr("Off") : xc.autocrop_autodetect ? tr("automatic") : tr("On")); break; case HOTKEY_DEINTERLACE: { /* off, on */ int off = !strcmp(xc.deinterlace_method, "none"); if (!OnlyInfo) { off = !off; if (off) strcpy(xc.deinterlace_method, "none"); else strcpy(xc.deinterlace_method, "tvtime"); m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); } Message = cString::sprintf("%s %s %s", tr("Deinterlacing"), OnlyInfo ? ":" : "->", tr(off ? "Off":"On")); } break; case HOTKEY_UPMIX: /* off, on */ if (!OnlyInfo) { xc.audio_upmix = xc.audio_upmix ? 0 : 1; m_Dev->ConfigurePostprocessing("upmix", xc.audio_upmix ? true : false, NULL); } Message = cString::sprintf("%s %s %s", tr("Upmix stereo to 5.1"), OnlyInfo ? ":" : "->", tr(xc.audio_upmix ? "On" : "Off")); break; case HOTKEY_DOWNMIX: /* off, on */ if (!OnlyInfo) { xc.audio_surround = xc.audio_surround ? 0 : 1; m_Dev->ConfigurePostprocessing( xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); } Message = cString::sprintf("%s %s %s", tr("Downmix AC3 to surround"), OnlyInfo ? ":" : "->", tr(xc.audio_surround ? "On":"Off")); break; case HOTKEY_PLAYLIST: /* Start replaying playlist or file pointed by symlink $(CONFDIR)/plugins/xineliboutput/default_playlist */ { struct stat st; cString file = cString::sprintf("%s%s", cPlugin::ConfigDirectory("xineliboutput"), "/default_playlist"); if (lstat(file, &st) == 0) { if (S_ISLNK(st.st_mode)) { cString buffer(ReadLink(file), true); if (!*buffer || stat(buffer, &st)) { Message = tr("Default playlist not found"); } else { LOGDBG("Replaying default playlist: %s", *file); cPlayerFactory::Launch(m_Dev, buffer); } } else { Message = tr("Default playlist is not symlink"); } } else { Message = tr("Default playlist not defined"); } } break; case HOTKEY_ADELAY_UP: /* audio delay up */ if (!OnlyInfo) { xc.audio_delay++; m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); } Message = cString::sprintf("%s %s %d %s", tr("Delay"), OnlyInfo ? ":" : "->", xc.audio_delay, tr("ms")); break; case HOTKEY_ADELAY_DOWN: /* audio delay up */ if (!OnlyInfo) { xc.audio_delay--; m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); } Message = cString::sprintf("%s %s %d %s", tr("Delay"), OnlyInfo ? ":" : "->", xc.audio_delay, tr("ms")); break; default: Message = cString::sprintf(tr("xineliboutput: hotkey %s not binded"), cKey::ToString(Key)); break; } if (*Message) { if (!xc.pending_menu_action && !cRemote::HasKeys() && cRemote::CallPlugin("xineliboutput")) xc.pending_menu_action = new cDisplayMessage(Message); } xc.last_hotkey_time = now; xc.last_hotkey = Key; return NewState; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/media_player.h������������������������������������������������������������������0000644�0001750�0001750�00000001557�13061253352�015040� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * media_player.h: Media and image players * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_PLAYER_H #define __XINELIB_PLAYER_H class cPlaylist; class cXinelibDevice; class cPlayerFactory { public: // interact with current player static bool IsOpen(void); static void Queue (cXinelibDevice *Dev, const char *Mrl); // launch new media player static bool Launch(cXinelibDevice *Dev, const char *Mrl, const char *SubFile = NULL) { return Launch(Dev, pmNone, Mrl, SubFile); }; static bool Launch(cXinelibDevice *Dev, ePlayMode PlayMode, const char *Mrl, const char *SubFile = NULL, bool BackToMenu = false); static bool Launch(cXinelibDevice *Dev, ePlayMode PlayMode, cPlaylist *Playlist, bool BackToMenu = false); }; #endif // __XINELIB_PLAYER_H �������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/media_player.c������������������������������������������������������������������0000644�0001750�0001750�00000140732�13061253352�015032� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * media_player.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include <unistd.h> #include <vdr/config.h> #include <vdr/status.h> #include <vdr/interface.h> #include <vdr/tools.h> #include "config.h" #include "media_player.h" #include "device.h" #include "tools/playlist.h" #include "tools/metainfo_menu.h" #include "menu.h" #include "logdefs.h" static void BackToMenu(eMainMenuMode Menu) { xc.main_menu_mode = Menu; cRemote::CallPlugin("xineliboutput"); } // // cXinelibPlayer // class cXinelibPlayer : public cPlayer { private: cString m_File; cString m_ResumeFile; cString m_SubFile; cPlaylist m_Playlist; bool m_Error; bool m_UseResumeFile; int m_Speed; void UpdateNumTracks(void); cXinelibDevice *m_Dev; protected: virtual void Activate(bool On); public: cXinelibPlayer(cXinelibDevice *Dev, const char *File, bool Queue = false, const char *SubFile = NULL); virtual ~cXinelibPlayer(); // cPlayer virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId); virtual void SetSubtitleTrack(eTrackType Type, const tTrackId *TrackId); virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false); virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed); // cXinelibPlayer void Control(const char *s) { (void)m_Dev->PlayFileCtrl(s); } void Control(const char *s, int i) { cString cmd = cString::sprintf(s, i); Control(cmd); } void SetSpeed(int Speed); int Speed(void) { return m_Speed; }; int GetPos(void) { return m_Dev->PlayFileCtrl("GETPOS"); } int GetLength(void) { return m_Dev->PlayFileCtrl("GETLENGTH"); } const char *GetMetaInfo(eMetainfoType mi) { return m_Dev->GetMetaInfo(mi); } bool NextFile(int step); bool Playing(void) { return !(m_Error || m_Dev->EndOfStreamReached()); } bool Error(void) { return m_Error; } void UseResumeFile(bool Val) { m_UseResumeFile = Val; } /* Playlist access */ cPlaylist& Playlist(void) { return m_Playlist; } const cString& File(void) { return m_File; } int CurrentFile(void) { return m_Playlist.Current()->Index(); } int Files(void) { return m_Playlist.Count(); } bool UpdateMetaInfo(bool Force = false); }; cXinelibPlayer::cXinelibPlayer(cXinelibDevice *Dev, const char *File, bool Queue, const char *SubFile) { m_ResumeFile = NULL; m_UseResumeFile = xc.media_enable_resume; m_Error = false; m_Speed = 1; m_Dev = Dev; if(File) { size_t len = strlen(File); if(len && File[len-1] == '/') { // whole directory, create temporary playlist m_Playlist.Read(File, true); m_Playlist.Sort(); } else if(xc.IsPlaylistFile(File)) { m_Playlist.Read(File); } else { // a single file but not a playlist file, create playlist with only one item m_Playlist.Read(File); } if(m_Playlist.Count() < 1) LOGMSG("cXinelibPlayer: nothing to play !"); if(m_Playlist.Count() > 0) m_Playlist.StartScanner(); m_File = m_Playlist.Current()->Filename; m_SubFile = SubFile; } } cXinelibPlayer::~cXinelibPlayer() { Activate(false); Detach(); } void cXinelibPlayer::SetAudioTrack(eTrackType Type, const tTrackId *TrackId) { if(IS_DOLBY_TRACK(Type)) Control("AUDIOSTREAM AC3 %d", (int)(Type - ttDolbyFirst)); if(IS_AUDIO_TRACK(Type)) Control("AUDIOSTREAM AC3 %d", (int)(Type - ttAudioFirst)); } void cXinelibPlayer::SetSubtitleTrack(eTrackType Type, const tTrackId *TrackId) { m_Dev->SetSubtitleTrackDevice(Type); } bool cXinelibPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame) { // Returns the current and total frame index, optionally snapped to the // nearest I-frame. int msCurrent = GetPos(); int msTotal = GetLength(); if(msCurrent>=0 && msTotal>=0) { Current = msCurrent * 25 / 1000; Total = msTotal * 25 / 1000; return true; } return false; } bool cXinelibPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed) { // Returns the current replay mode (if applicable). // 'Play' tells whether we are playing or pausing, 'Forward' tells whether // we are going forward or backward and 'Speed' is -1 if this is normal // play/pause mode, 0 if it is single speed fast/slow forward/back mode // and >0 if this is multi speed mode. Play = (m_Speed>0); Forward = true; Speed = abs(m_Speed) - 2; if(Speed<-1) Speed=-1; return true; } void cXinelibPlayer::SetSpeed(int Speed) { m_Speed = Speed; switch(Speed) { case -4: Control("TRICKSPEED 8"); break; case -3: Control("TRICKSPEED 4"); break; case -2: Control("TRICKSPEED 2"); break; case 0: Control("TRICKSPEED 0"); break; default: m_Speed = 1; case 1: Control("TRICKSPEED 1"); break; case 2: Control("TRICKSPEED -2"); break; case 3: Control("TRICKSPEED -4"); break; case 4: Control("TRICKSPEED -12"); break; } } bool cXinelibPlayer::UpdateMetaInfo(bool Force) { // update playlist metainfo const char *ti = GetMetaInfo(miTitle); bool TitleChanged = ti && ti[0] && (!*Playlist().Current()->Title || !strstr(Playlist().Current()->Title, ti)); if (Force || TitleChanged) { const char *tr = GetMetaInfo(miTracknumber); const char *al = GetMetaInfo(miAlbum); const char *ar = GetMetaInfo(miArtist); if (!Force) LOGDBG("metainfo changed: %s->%s %s->%s %s->%s %s->%s", *Playlist().Current()->Artist ?: "-", ar ?: "-", *Playlist().Current()->Album ?: "-", al ?: "-", *Playlist().Current()->Tracknumber ?: "-", tr ?: "-", *Playlist().Current()->Title ?: "-", ti ?: "-"); if (ti && ti[0]) m_Playlist.Current()->Title = ti; if (tr && tr[0]) m_Playlist.Current()->Tracknumber = tr; if (al && al[0]) m_Playlist.Current()->Album = al; if (ar && ar[0]) m_Playlist.Current()->Artist = ar; return true; } return false; } bool cXinelibPlayer::NextFile(int step) { if (m_Playlist.Count() > 0) { for (;step < 0; step++) m_Playlist.Prev(); for (;step > 0; step--) m_Playlist.Next(); if (!m_Playlist.Current()) LOGERR("!m_Playlist.Get(m_CurrInd)"); m_File = *m_Playlist.Current()->Filename; m_ResumeFile = NULL; m_SubFile = NULL; Activate(true); return !m_Error; } return false; } void cXinelibPlayer::UpdateNumTracks(void) { // cdda tracks if(m_Playlist.Count() == 1 && !strcmp("cdda:/", m_Playlist.First()->Filename)) { int count = m_Dev->PlayFileCtrl("GETAUTOPLAYSIZE CD", 10000); if(count>0) { for(int i=0; i<count; i++) { m_Playlist.Read(cString::sprintf("cdda:/%d", i+1)); m_Playlist.Last()->Title = cString::sprintf("Track %d", i + 1); m_Playlist.Last()->Tracknumber = cString::sprintf("%d/%d", i + 1, count); } m_Playlist.Del(m_Playlist.First()); } } } void cXinelibPlayer::Activate(bool On) { int pos = 0, len = 0, fd = -1; if(On) { if(m_UseResumeFile && !*m_ResumeFile) m_ResumeFile = cString::sprintf("%s.resume", *m_File); if(m_UseResumeFile && 0 <= (fd = open(m_ResumeFile, O_RDONLY))) { if(read(fd, &pos, sizeof(int)) != sizeof(int)) pos = 0; close(fd); } // escape raw file names and join subtitle file to the mrl cString mrl = m_File[0] == '/' ? cPlaylist::BuildMrl("file", m_File) : m_File; if (*m_SubFile) mrl = cString::sprintf("%s#subtitle:%s", *mrl, m_SubFile[0] == '/' ? *cPlaylist::BuildMrl("file", m_SubFile) : *m_SubFile); // Start replay UpdateNumTracks(); m_Error = !m_Dev->PlayFile(mrl, pos); LOGDBG("cXinelibPlayer playing %s (%s)", *m_File, m_Error ? "FAIL" : "OK"); if (!m_Error) { UpdateMetaInfo(true); UpdateNumTracks(); } } else { if(m_UseResumeFile && *m_ResumeFile) { pos = GetPos(); len = GetLength(); if(pos>10000 && pos < (len-10000)) { pos = (pos/1000) - 10; // skip back 10 seconds ("VDR style") if(0 <= (fd = open(m_ResumeFile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { if(write(fd, &pos, sizeof(int)) != sizeof(int)) { Skins.QueueMessage(mtInfo, "Error writing resume position !", 5, 30); } close(fd); } else { Skins.QueueMessage(mtInfo, "Error creating resume file !", 5, 30); } } else { unlink(m_ResumeFile); } m_ResumeFile = NULL; } m_Dev->PlayFile(NULL); m_Error = false; } } // // cPlaylistMenu // class cPlaylistMenu : public cOsdMenu, cPlaylistChangeNotify { private: int m_Marked; protected: cXinelibDevice *m_Dev; cPlaylist& m_Playlist; bool m_NeedsUpdate; bool& m_RandomPlay; cCharSetConv m_IC; public: cPlaylistMenu(cXinelibDevice *Dev, cPlaylist &Playlist, bool& RandomPlay); virtual ~cPlaylistMenu(); void Set(bool setCurrentPlaying = false); void SetCurrentExt(int i); void SetHelpButtons(void); // cOsdMenu virtual eOSState ProcessKey(eKeys Key); // cPlaylistChangeNotify virtual void PlaylistChanged(const cPlaylistItem *item); }; cPlaylistMenu::cPlaylistMenu(cXinelibDevice *Dev, cPlaylist &Playlist, bool& RandomPlay) : cOsdMenu(tr("Playlist")), m_Playlist(Playlist), m_RandomPlay(RandomPlay), m_IC("UTF-8", cCharSetConv::SystemCharacterTable()) { m_Dev = Dev; m_Marked = -1; SetTitle(cString::sprintf("%s: %s", tr("Playlist"), m_IC.Convert(*Playlist.Name()))); Playlist.Listen(this); Set(true); } cPlaylistMenu::~cPlaylistMenu() { m_Playlist.Listen(NULL); } void cPlaylistMenu::PlaylistChanged(const cPlaylistItem *item) { m_NeedsUpdate = true; } eOSState cPlaylistMenu::ProcessKey(eKeys Key) { bool hadSubMenu = HasSubMenu(); if(m_NeedsUpdate) Set(); eOSState state = cOsdMenu::ProcessKey(Key); if (m_Marked >= 0) { switch(Key) { case kOk: m_Playlist.Move(m_Marked, Current()); Set(); m_Marked = -1; return osContinue; case kBack: m_Marked = -1; return osEnd; default:; } return osContinue; } if(state == osUnknown) { switch(Key) { case kBack: return osEnd; case kRed: m_RandomPlay = !m_RandomPlay; SetHelpButtons(); return osContinue; case kGreen: return AddSubMenu(cMenuXinelib::CreateMenuBrowseFiles(m_Dev, ShowMusic)); case kYellow: if(m_Playlist.Count() > 1) { eOSState result = osContinue; cPlaylistItem *i = m_Playlist.Current(); if(i->Index() == Current()) { if(i->Next()) result = (eOSState)(os_User + i->Index()); /* forces jump to next item */ else result = (eOSState)(os_User + i->Index() - 1);/* forces jump to last item */ } for(i = m_Playlist.First(); i && i->Index() != Current(); i = m_Playlist.Next(i)); if(i) m_Playlist.Del(i); if(Current() == Count()-1) SetCurrent(Get(Current()-1)); Set(); return result; } case kBlue: Mark(); m_Marked = Current(); return osContinue; case k0: m_Playlist.Sort(); Set(); return osContinue; default: break; } } if(hadSubMenu && !HasSubMenu()) Set(); return state; } void cPlaylistMenu::SetCurrentExt(int i) { SetCurrent(Get(i)); Set(); } void cPlaylistMenu::SetHelpButtons(void) { SetHelp(!m_RandomPlay ? tr("Button$Random") : tr("Button$Normal"), tr("Button$Add files"), m_Playlist.Count()>1 ? tr("Button$Remove") : NULL, tr("Button$Mark")); Display(); } void cPlaylistMenu::Set(bool setCurrentPlaying) { m_NeedsUpdate = false; int currentItem = Current(); Clear(); SetHasHotkeys(); SetCols(2, 30); SetHelpButtons(); int currentPlaying = m_Playlist.Current()->Index(); int j = 0; for(cPlaylistItem *i = m_Playlist.First(); i; i = m_Playlist.Next(i), j++) { cString Title = cPlaylist::GetEntry(i, true, j==currentPlaying); Add(new cOsdItem(m_IC.Convert(*Title), (eOSState)(os_User + j))); } if(setCurrentPlaying) SetCurrent(Get(currentPlaying)); else SetCurrent(Get(currentItem)); Display(); } // // cXinelibPlayerControl // #include <vdr/skins.h> class cXinelibPlayerControl : public cControl { private: static cMutex m_Lock; static cXinelibPlayer *OpenPlayer(cXinelibDevice *Dev, const char *File, bool Queue = false, const char *SubFile = NULL); protected: static cXinelibPlayer *m_Player; cXinelibDevice *m_Dev; cSkinDisplayReplay *m_DisplayReplay; cPlaylistMenu *m_PlaylistMenu; eMainMenuMode m_Mode; bool m_ShowModeOnly; bool m_RandomPlay; time_t m_AutoShowStart; int m_CurrentPos; int m_CurrentLen; bool m_BlinkState; cTimeMs lastTime; int number; void CloseMenus(void); void MsgReplaying(const char *Title, const char *File); public: cXinelibPlayerControl(cXinelibDevice *Dev, eMainMenuMode Mode, const char *File, const char *SubFile = NULL); virtual ~cXinelibPlayerControl(); virtual void Show(void); virtual void Hide(void); virtual eOSState ProcessKey(eKeys Key); virtual cOsdObject *GetInfo(void); static void Close(void); static bool IsOpen(void) { return m_Player != NULL; }; static void Queue(cXinelibDevice *Dev, const char *File); }; cXinelibPlayer *cXinelibPlayerControl::m_Player = NULL; cMutex cXinelibPlayerControl::m_Lock; cXinelibPlayerControl::cXinelibPlayerControl(cXinelibDevice *Dev, eMainMenuMode Mode, const char *File, const char *SubFile) : cControl(OpenPlayer(Dev, File, false, SubFile)) { m_Dev = Dev; m_DisplayReplay = NULL; m_PlaylistMenu = NULL; m_ShowModeOnly = true; m_Mode = Mode; m_RandomPlay = false; m_AutoShowStart = time(NULL); m_BlinkState = true; number = 0; lastTime.Set(); m_Player->UseResumeFile( (Mode==ShowFiles) && xc.media_enable_resume); MsgReplaying(*m_Player->Playlist().Current()->Title, *m_Player->File()); } cXinelibPlayerControl::~cXinelibPlayerControl() { CloseMenus(); MsgReplaying(NULL, NULL); Close(); } void cXinelibPlayerControl::CloseMenus(void) { if (m_PlaylistMenu) { delete m_PlaylistMenu; m_PlaylistMenu = NULL; } if (m_DisplayReplay) { delete m_DisplayReplay; m_DisplayReplay = NULL; } } void cXinelibPlayerControl::MsgReplaying(const char *Title, const char *File) { cStatus::MsgReplaying(this, NULL, NULL, false); if(Title || File) cStatus::MsgReplaying(this, Title, File, true); } void cXinelibPlayerControl::Queue(cXinelibDevice *Dev, const char *File) { if(!File) return; m_Lock.Lock(); LOGMSG("cXinelibPlayerControl::Queue(%s)", File); if(!m_Player) { OpenPlayer(Dev, File, true); cControl::Launch(new cXinelibPlayerControl(Dev, ShowMusic, NULL)); } else { size_t len = strlen(File); if(len && File[len-1] == '/') m_Player->Playlist().Read(File, true); else m_Player->Playlist().Read(File); } cRemote::Put(Skins.Message(mtInfo, tr("Queued to playlist")), true); m_Lock.Unlock(); if(m_Player->Playlist().Count() > 0) m_Player->Playlist().StartScanner(); } cXinelibPlayer *cXinelibPlayerControl::OpenPlayer(cXinelibDevice *Dev, const char *File, bool Queue, const char *SubFile) { m_Lock.Lock(); if(!m_Player) m_Player = new cXinelibPlayer(Dev, File, Queue, SubFile); m_Lock.Unlock(); return m_Player; } void cXinelibPlayerControl::Close(void) { m_Lock.Lock(); if(m_Player) delete m_Player; m_Player = NULL; m_Lock.Unlock(); } void cXinelibPlayerControl::Show() { bool Play = (m_Player->Speed() > 0); bool Forward = true; int Speed = abs(m_Player->Speed()) - 2; if(Speed<-1) Speed=-1; if(!m_DisplayReplay) { if(cOsd::IsOpen()) return; m_DisplayReplay = Skins.Current()->DisplayReplay(m_ShowModeOnly); } if(!m_ShowModeOnly) { char t[128] = ""; int Current = m_Player->GetPos(); int Total = m_Player->GetLength(); if(Current>=0) m_CurrentPos = Current; if(Total>=0) m_CurrentLen = Total; if(m_CurrentLen >= 0 /*&& Total >= 0*/) { Total = (m_CurrentLen + 500) / 1000; // ms --> s Current = (m_CurrentPos + 500) / 1000; cString Title = cPlaylist::GetEntry(m_Player->Playlist().Current()); cCharSetConv ic("UTF-8", cCharSetConv::SystemCharacterTable()); m_DisplayReplay->SetTitle(ic.Convert(*Title)); m_DisplayReplay->SetProgress(Current, Total); sprintf(t, "%d:%02d:%02d", Total/3600, (Total%3600)/60, Total%60); m_DisplayReplay->SetTotal( t ); sprintf(t, "%d:%02d:%02d", Current/3600, (Current%3600)/60, Current%60); m_BlinkState = (m_Player->Speed() != 0) || (!m_BlinkState); m_DisplayReplay->SetCurrent( m_BlinkState ? t : ""); } } m_DisplayReplay->SetMode(Play, Forward, Speed); m_DisplayReplay->Flush(); } void cXinelibPlayerControl::Hide() { CloseMenus(); } cOsdObject *cXinelibPlayerControl::GetInfo(void) { return new cMetainfoMenu(m_Player->Playlist().Current()->Filename); } eOSState cXinelibPlayerControl::ProcessKey(eKeys Key) { if ( !m_Player->Playing() ) { LOGDBG("cXinelibPlayerControl: EndOfStreamReached"); if (m_Mode == ShowMusic && m_Player->Files() == 1 && !m_Player->Error()) { m_Player->NextFile(0); return osContinue; } int Jump = 1; if(m_RandomPlay) { srand((unsigned int)time(NULL)); Jump = (random() % m_Player->Files()) - m_Player->CurrentFile(); } if(m_Player->Files() < 2 || !m_Player->NextFile(Jump)) { Hide(); return osEnd; } if(m_PlaylistMenu) { m_PlaylistMenu->PlaylistChanged(m_Player->Playlist().Current()); m_PlaylistMenu->SetCurrentExt(m_Player->CurrentFile()); } if(!m_DisplayReplay) m_AutoShowStart = time(NULL); MsgReplaying(*m_Player->Playlist().Current()->Title, *m_Player->File()); } else { if (m_Player->UpdateMetaInfo()) MsgReplaying(*m_Player->Playlist().Current()->Title, *m_Player->File()); } // playlist menu if (m_PlaylistMenu) { m_AutoShowStart = 0; eOSState state = osUnknown; switch (state = m_PlaylistMenu->ProcessKey(Key)) { case osBack: case osEnd: Hide(); break; default: if (state >= os_User) { m_Player->NextFile( (int)state - (int)os_User - m_Player->CurrentFile()); m_PlaylistMenu->SetCurrentExt(m_Player->CurrentFile()); MsgReplaying(*m_Player->Playlist().Current()->Title, *m_Player->File()); } break; } if (state != osUnknown) return osContinue; } if (m_DisplayReplay) Show(); if ( m_Mode == ShowFiles ) { switch(Key) { // replay menu case kRed: if(m_Player->Playlist().Count() > 1) { Hide(); m_PlaylistMenu = new cPlaylistMenu(m_Dev, m_Player->Playlist(), m_RandomPlay); m_AutoShowStart = 0; } else { m_Player->Control("SEEK 0"); break; } break; case kUser8: case k1: m_Player->Control("SEEK -20"); break; case kUser9: case k3: m_Player->Control("SEEK +20"); break; case k2: xc.subtitle_vpos -= 10; /* fall thru */ case k5: xc.subtitle_vpos += 5; m_Player->Control("SUBTITLES %d", xc.subtitle_vpos); break; case kRight: { static const int speeds[] = { -3, -2, 1, 2, -4, 2, 3, 4, 4 }; m_Player->SetSpeed(speeds[m_Player->Speed() + 4]); if(m_Player->Speed() != 1) Show(); else Hide(); break; } case kLeft: { static const int speeds[] = { 0, -4, -3, -2, 0, -2, 1, 2, 3 }; m_Player->SetSpeed(speeds[m_Player->Speed() + 4]); if(m_Player->Speed() != 1 || !m_ShowModeOnly) Show(); else Hide(); break; } default: break; } } if ( m_Mode == ShowMusic ) { switch(Key) { case kRed: Hide(); m_PlaylistMenu = new cPlaylistMenu(m_Dev, m_Player->Playlist(), m_RandomPlay); m_AutoShowStart = 0; break; case kNext: case kRight: if(m_RandomPlay) { srand((unsigned int)time(NULL)); m_Player->NextFile((random() % m_Player->Files()) - m_Player->CurrentFile()); } else { m_Player->NextFile(1); } if(!m_DisplayReplay) m_AutoShowStart = time(NULL); MsgReplaying(*m_Player->Playlist().Current()->Title, *m_Player->File()); break; case kPrev: case kLeft: if(m_Player->GetPos() < 3000) { m_Player->NextFile(-1); if(!m_DisplayReplay) m_AutoShowStart = time(NULL); MsgReplaying(*m_Player->Playlist().Current()->Title, *m_Player->File()); } else { m_Player->NextFile(0); if(!m_DisplayReplay) m_AutoShowStart = time(NULL); } break; case k0 ... k9: if (number >= 0) { if (number * 10 + Key - k0 > m_Player->Files()) number = m_Player->Files(); else number = number * 10 + Key - k0; } break; case kNone: if (number > 0 && int(lastTime.Elapsed()) > 3000) { m_Player->NextFile( number - (m_Player->CurrentFile() + 1) ); if (!m_DisplayReplay) m_AutoShowStart = time(NULL); MsgReplaying(*m_Player->Playlist().Current()->Title, *m_Player->File()); number = 0; lastTime.Set(); } break; default: break; } } switch(Key) { // key bindings common for both players case kBack: Hide(); BackToMenu(m_Mode); break; case kStop: case kBlue: Hide(); Close(); return osEnd; case kUser7: if(m_Player->Playlist().Count()>1) { m_RandomPlay = !m_RandomPlay; if(m_RandomPlay) Skins.Message(mtInfo, tr("Random play")); else Skins.Message(mtInfo, tr("Normal play")); } break; case kGreen: m_Player->Control("SEEK -60"); break; case kYellow: m_Player->Control("SEEK +60"); break; case kUser8: m_Player->Control("SEEK -20"); break; case kUser9: m_Player->Control("SEEK +20"); break; case kDown: case kPause: if(m_Player->Speed()) { m_Player->SetSpeed(0); if(!m_DisplayReplay) m_ShowModeOnly = true; Show(); break; } // fall thru case kUp: case kPlay: m_Player->SetSpeed(1); if(m_ShowModeOnly && m_DisplayReplay) Hide(); else if(m_DisplayReplay) Show(); m_ShowModeOnly = false; break; case kFastFwd: { static const int speeds[] = { -3, -2, 1, 2, -4, 2, 3, 4, 4 }; m_Player->SetSpeed(speeds[m_Player->Speed() + 4]); if(m_Player->Speed() != 1) Show(); else Hide(); break; } case kFastRew: { static const int speeds[] = { 0, -4, -3, -2, 0, -2, 1, 2, 3 }; m_Player->SetSpeed(speeds[m_Player->Speed() + 4]); if(m_Player->Speed() != 1 || !m_ShowModeOnly) Show(); else Hide(); break; } case kOk: m_AutoShowStart = 0; if(m_Player->Speed() != 1) { Hide(); m_ShowModeOnly = !m_ShowModeOnly; Show(); } else { if(m_DisplayReplay) { m_ShowModeOnly = true; Hide(); } else { Hide(); m_ShowModeOnly = false; Show(); } } break; default: break; } if(m_DisplayReplay && m_AutoShowStart && time(NULL) - m_AutoShowStart > 5) { m_AutoShowStart = 0; Hide(); } if(!m_DisplayReplay && m_AutoShowStart) { m_ShowModeOnly = false; Show(); } return osContinue; } // --- DVD player ----------------------------------------------------------- // // cDvdMenu // class cDvdMenu : public cOsdMenu { private: cXinelibPlayer *m_Player; public: cDvdMenu(cXinelibPlayer *Player); virtual eOSState ProcessKey(eKeys Key); }; cDvdMenu::cDvdMenu(cXinelibPlayer *Player) : cOsdMenu(tr("DVD Menu")), m_Player(Player) { Add(new cOsdItem(tr("Exit DVD menu"), osUser1)); Add(new cOsdItem(tr("DVD Root menu"), osUser2)); Add(new cOsdItem(tr("DVD Title menu"), osUser3)); Add(new cOsdItem(tr("DVD SPU menu"), osUser4)); Add(new cOsdItem(tr("DVD Audio menu"), osUser5)); Add(new cOsdItem(tr("Close menu"), osEnd)); Display(); } eOSState cDvdMenu::ProcessKey(eKeys Key) { eOSState state = cOsdMenu::ProcessKey(Key); switch (state) { case osUser1: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU1"); return osEnd; case osUser2: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU2"); return osEnd; case osUser3: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU3"); return osEnd; case osUser4: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU4"); return osEnd; case osUser5: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU5"); return osEnd; case osBack: case osEnd: return osEnd; default: break; } return state; } // // cXinelibDvdPlayerControl // class cXinelibDvdPlayerControl : public cXinelibPlayerControl { private: cOsdMenu *m_DvdMenu; void CloseDvdMenu(void); public: cXinelibDvdPlayerControl(cXinelibDevice *Dev, const char *File) : cXinelibPlayerControl(Dev, ShowFiles, File), m_DvdMenu(NULL) {} virtual ~cXinelibDvdPlayerControl(); virtual void Show(void); virtual void Hide(void); virtual eOSState ProcessKey(eKeys Key); }; cXinelibDvdPlayerControl::~cXinelibDvdPlayerControl() { CloseDvdMenu(); } void cXinelibDvdPlayerControl::Hide(void) { CloseDvdMenu(); cXinelibPlayerControl::Hide(); } void cXinelibDvdPlayerControl::CloseDvdMenu(void) { if (m_DvdMenu) { delete m_DvdMenu; m_DvdMenu = NULL; } } void cXinelibDvdPlayerControl::Show(void) { if (!m_DvdMenu) cXinelibPlayerControl::Show(); else cXinelibPlayerControl::Hide(); } eOSState cXinelibDvdPlayerControl::ProcessKey(eKeys Key) { // Check for end of stream and failed open if ( !m_Player->Playing() ) { LOGDBG("cXinelibDvdPlayerControl: EndOfStreamReached"); Hide(); return osEnd; } // Check for changed title if (m_Player->UpdateMetaInfo()) MsgReplaying(*m_Player->Playlist().Current()->Title, NULL); // Handle menu selection if (m_DvdMenu) { if (Key == kRed || m_DvdMenu->ProcessKey(Key) == osEnd) Hide(); return osContinue; } // Update progress bar display if (m_DisplayReplay) Show(); // Detect DVD menus bool MenuDomain = !xc.dvd_arrow_keys_control_playback; if(Key != kNone || m_DisplayReplay) { const char *dt = m_Player->GetMetaInfo(miDvdTitleNo); if(dt && !strcmp("0", dt)) MenuDomain = true; else { dt = m_Player->GetMetaInfo(miDvdButtons); if(dt && *dt && *dt != '0') MenuDomain = true; } } // DVD menu navigation if(MenuDomain) { if(m_DisplayReplay) Hide(); switch(Key) { case kUp: m_Player->Control("EVENT XINE_EVENT_INPUT_UP"); return osContinue; case kDown: m_Player->Control("EVENT XINE_EVENT_INPUT_DOWN"); return osContinue; case kLeft: m_Player->Control("EVENT XINE_EVENT_INPUT_LEFT"); return osContinue; case kRight: m_Player->Control("EVENT XINE_EVENT_INPUT_RIGHT"); return osContinue; case kOk: m_Player->Control("EVENT XINE_EVENT_INPUT_SELECT"); return osContinue; case kBack: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU1"); return osContinue; default: break; } } // Handle normal keys if(!MenuDomain) { switch(Key) { // Replay control case kUp: Key = kPlay; break; case kDown: Key = kPause; break; case kLeft: Key = kFastRew; break; case kRight: Key = kFastFwd; break; case kOk: if(m_Player->Speed() != 1) { Hide(); m_ShowModeOnly = !m_ShowModeOnly; Show(); break; } if(m_DisplayReplay) { Hide(); m_ShowModeOnly = true; } else { Hide(); m_ShowModeOnly = false; Show(); } break; case kInfo: Hide(); if(m_DisplayReplay && !m_ShowModeOnly) { m_ShowModeOnly = true; } else { m_ShowModeOnly = false; Show(); } break; case kBack: if (config_t::IsDvdImage(m_Player->File())) { BackToMenu(m_Mode); } else { BackToMenu(ShowMenu); } Hide(); Close(); return osEnd; default: break; } } switch(Key) { // DVD menus case kRed: Hide(); m_DvdMenu = new cDvdMenu(m_Player); break; // Playback control case kGreen: m_Player->Control("SEEK -60"); break; case kYellow: m_Player->Control("SEEK +60"); break; case kUser8: case k1: m_Player->Control("SEEK -20"); break; case kUser9: case k3: m_Player->Control("SEEK +20"); break; case kStop: case kBlue: Hide(); Close(); return osEnd; case k9: m_Player->Control("EVENT XINE_EVENT_INPUT_NEXT TITLE"); break; case k7: m_Player->Control("EVENT XINE_EVENT_INPUT_PREVIOUS TITLE"); break; case k6: case kNext: m_Player->Control("EVENT XINE_EVENT_INPUT_NEXT CHAPTER"); break; case k4: case kPrev: m_Player->Control("EVENT XINE_EVENT_INPUT_PREVIOUS CHAPTER"); break; case kFastFwd: { static const int speeds[] = { -3, -2, 1, 2, -4, 2, 3, 4, 4 }; m_Player->SetSpeed(speeds[m_Player->Speed() + 4]); if(m_Player->Speed() != 1) Show(); else Hide(); break; } case kFastRew: { static const int speeds[] = { 0, -4, -3, -2, 0, -2, 1, 2, 3 }; m_Player->SetSpeed(speeds[m_Player->Speed() + 4]); if(m_Player->Speed() != 1 || !m_ShowModeOnly) Show(); else Hide(); break; } case kInfo: if(m_DisplayReplay) { Hide(); } else { m_ShowModeOnly = false; Show(); } break; case kPause: if(m_Player->Speed()) { m_Player->SetSpeed(0); m_ShowModeOnly = false; Show(); break; } // fall thru case kPlay: m_Player->SetSpeed(1); m_ShowModeOnly = true; Hide(); break; default: break; } return osContinue; } // --- BluRay player -------------------------------------------------------- // // cBdMenu // class cBdMenu : public cOsdMenu { private: cXinelibPlayer *m_Player; public: cBdMenu(cXinelibPlayer *Player, bool Menus); virtual eOSState ProcessKey(eKeys Key); }; cBdMenu::cBdMenu(cXinelibPlayer *Player, bool Menus) : cOsdMenu("BluRay"), m_Player(Player) { if (Menus) { Add(new cOsdItem(tr("BluRay Top menu"), osUser1)); Add(new cOsdItem(tr("Toggle Pop-Up menu"), osUser2)); } Add( new cOsdItem(tr("Next title"), osUser3)); Add( new cOsdItem(tr("Previous title"), osUser4)); Add( new cOsdItem( "----------", osUnknown)); Add( new cOsdItem(tr("Close menu"), osBack)); Display(); } eOSState cBdMenu::ProcessKey(eKeys Key) { eOSState state = cOsdMenu::ProcessKey(Key); switch (state) { case osUser1: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU1"); return osEnd; case osUser2: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU2"); return osEnd; case osUser3: m_Player->Control("EVENT XINE_EVENT_INPUT_NEXT TITLE"); return osEnd; case osUser4: m_Player->Control("EVENT XINE_EVENT_INPUT_PREVIOUS TITLE"); return osEnd; case osBack: case osEnd: return osEnd; default: break; } return state; } // // cXinelibBdPlayerControl // class cXinelibBdPlayerControl : public cXinelibPlayerControl { private: cBdMenu *m_BdMenu; void CloseBdMenu(void); public: cXinelibBdPlayerControl(cXinelibDevice *Dev, const char *File); virtual ~cXinelibBdPlayerControl(); virtual void Show(void); virtual void Hide(void); virtual eOSState ProcessKey(eKeys Key); }; cXinelibBdPlayerControl::cXinelibBdPlayerControl(cXinelibDevice *Dev, const char *File) : cXinelibPlayerControl(Dev, ShowFiles, File), m_BdMenu(NULL) { } cXinelibBdPlayerControl::~cXinelibBdPlayerControl() { CloseBdMenu(); } void cXinelibBdPlayerControl::CloseBdMenu(void) { if (m_BdMenu) { delete m_BdMenu; m_BdMenu = NULL; } } void cXinelibBdPlayerControl::Hide(void) { CloseBdMenu(); cXinelibPlayerControl::Hide(); } void cXinelibBdPlayerControl::Show(void) { if (!m_BdMenu) cXinelibPlayerControl::Show(); else cXinelibPlayerControl::Hide(); } eOSState cXinelibBdPlayerControl::ProcessKey(eKeys Key) { // Check for end of stream and failed open if ( !m_Player->Playing() ) { LOGDBG("cXinelibBdPlayerControl: EndOfStreamReached"); Hide(); return osEnd; } // Check for changed title if (m_Player->UpdateMetaInfo()) MsgReplaying(*m_Player->Playlist().Current()->Title, NULL); // Handle menu if (m_BdMenu) { if (Key == kRed || m_BdMenu->ProcessKey(Key) == osEnd) Hide(); return osContinue; } // Update progress bar display if (m_DisplayReplay) Show(); // Detect when in BluRay menus bool MenuDomain = false; if (Key != kNone || m_DisplayReplay) { const char *nb = m_Player->GetMetaInfo(miDvdButtons); if (nb && *nb && *nb != '0') MenuDomain = true; LOGMSG("dvd buttons %s", nb); } // Hide progress bar when in menus if (MenuDomain && m_DisplayReplay) Hide(); // menu navigation if (MenuDomain) { switch (Key) { case kUp: m_Player->Control("EVENT XINE_EVENT_INPUT_UP"); return osContinue; case kDown: m_Player->Control("EVENT XINE_EVENT_INPUT_DOWN"); return osContinue; case kLeft: m_Player->Control("EVENT XINE_EVENT_INPUT_LEFT"); return osContinue; case kRight: m_Player->Control("EVENT XINE_EVENT_INPUT_RIGHT"); return osContinue; case kOk: m_Player->Control("EVENT XINE_EVENT_INPUT_SELECT"); return osContinue; //case kBack: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU1"); return osContinue; default: break; } } // Handle normal keys if (!MenuDomain) { switch (Key) { // Replay control case kUp: Key = kPlay; break; case kDown: Key = kPause; break; case kLeft: Key = kFastRew; break; case kRight: Key = kFastFwd; break; case kOk: if(m_Player->Speed() != 1) { Hide(); m_ShowModeOnly = !m_ShowModeOnly; Show(); break; } if(m_DisplayReplay) { Hide(); m_ShowModeOnly = true; } else { Hide(); m_ShowModeOnly = false; Show(); } break; case kInfo: Hide(); if(m_DisplayReplay && !m_ShowModeOnly) { m_ShowModeOnly = true; } else { m_ShowModeOnly = false; Show(); } break; case kBack: if (config_t::IsDvdImage(m_Player->File())) { BackToMenu(m_Mode); } else { BackToMenu(ShowMenu); } Hide(); Close(); return osEnd; default: break; } } switch(Key) { // menu case kRed: Hide(); m_BdMenu = new cBdMenu(m_Player, !strncmp(m_Player->File(), "bd:/", 4)); break; // Playback control case kGreen: m_Player->Control("SEEK -60"); break; case kYellow: m_Player->Control("SEEK +60"); break; case kUser8: case k1: m_Player->Control("SEEK -20"); break; case kUser9: case k3: m_Player->Control("SEEK +20"); break; case k2: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU2"); break; case k5: m_Player->Control("EVENT XINE_EVENT_INPUT_MENU1"); break; case kStop: case kBlue: Hide(); Close(); return osEnd; case k9: m_Player->Control("EVENT XINE_EVENT_INPUT_NEXT TITLE"); break; case k7: m_Player->Control("EVENT XINE_EVENT_INPUT_PREVIOUS TITLE"); break; case k6: case kNext: m_Player->Control("EVENT XINE_EVENT_INPUT_NEXT CHAPTER"); break; case k4: case kPrev: m_Player->Control("EVENT XINE_EVENT_INPUT_PREVIOUS CHAPTER"); break; case kFastFwd: { static const int speeds[] = { -3, -2, 1, 2, -4, 2, 3, 4, 4 }; m_Player->SetSpeed(speeds[m_Player->Speed() + 4]); if(m_Player->Speed() != 1) Show(); else Hide(); break; } case kFastRew: { static const int speeds[] = { 0, -4, -3, -2, 0, -2, 1, 2, 3 }; m_Player->SetSpeed(speeds[m_Player->Speed() + 4]); if(m_Player->Speed() != 1 || !m_ShowModeOnly) Show(); else Hide(); break; } case kInfo: if(m_DisplayReplay) { Hide(); } else { m_ShowModeOnly = false; Show(); } break; case kPause: if(m_Player->Speed()) { m_Player->SetSpeed(0); m_ShowModeOnly = false; Show(); break; } // fall thru case kPlay: m_Player->SetSpeed(1); m_ShowModeOnly = true; Hide(); break; default: break; } return osContinue; } // --- Image player --------------------------------------------------------- // // cXinelibImagePlayer // class cXinelibImagePlayer : public cPlayer { private: cString m_Mrl; bool m_Active; bool m_Error; cXinelibDevice *m_Dev; bool Play(void); protected: virtual void Activate(bool On); public: cXinelibImagePlayer(cXinelibDevice *Dev, const char *File); virtual ~cXinelibImagePlayer(); bool ShowImage(const char *File); bool Error(void) { return m_Error; } }; cXinelibImagePlayer::cXinelibImagePlayer(cXinelibDevice *Dev, const char *File) { m_Mrl = File; m_Active = false; m_Error = false; m_Dev = Dev; } cXinelibImagePlayer::~cXinelibImagePlayer() { Activate(false); Detach(); } bool cXinelibImagePlayer::Play(void) { if (m_Mrl[0] == '/') m_Mrl = cPlaylist::BuildMrl("file", m_Mrl); return ! (m_Error = !m_Dev->PlayFile(m_Mrl, 0, true)); } void cXinelibImagePlayer::Activate(bool On) { m_Active = On; m_Error = false; if (On) Play(); else m_Dev->PlayFile(NULL); } bool cXinelibImagePlayer::ShowImage(const char *File) { m_Mrl = File; if (m_Active) return Play(); return true; } // // cXinelibImagesControl // class cXinelibImagesControl : public cControl { private: static cXinelibImagePlayer *m_Player; static cMutex m_Lock; cSkinDisplayReplay *m_DisplayReplay; cPlaylist *m_Playlist; int m_Speed; int m_LastShowTime; bool m_ShowModeOnly; static cXinelibImagePlayer *OpenPlayer(cXinelibDevice *Dev, const char *File); protected: void Seek(int Rel); void Delete(void); public: cXinelibImagesControl(cXinelibDevice *Dev, cPlaylist *Playlist); virtual ~cXinelibImagesControl(); virtual void Show(void); virtual void Hide(void); virtual eOSState ProcessKey(eKeys Key); virtual cOsdObject *GetInfo(void); static void Close(void); static bool IsOpen(void) { return m_Player != NULL; } }; cXinelibImagePlayer *cXinelibImagesControl::m_Player = NULL; cMutex cXinelibImagesControl::m_Lock; cXinelibImagesControl::cXinelibImagesControl(cXinelibDevice *Dev, cPlaylist *Playlist) : cControl(OpenPlayer(Dev, Playlist->Current()->Filename)) { m_DisplayReplay = NULL; m_Playlist = Playlist; m_Speed = 0; m_ShowModeOnly = false; Seek(0); } cXinelibImagesControl::~cXinelibImagesControl() { if(m_DisplayReplay) delete m_DisplayReplay; m_DisplayReplay = NULL; cStatus::MsgReplaying(this, NULL, NULL, false); Close(); delete m_Playlist; } cXinelibImagePlayer *cXinelibImagesControl::OpenPlayer(cXinelibDevice *Dev, const char *File) { m_Lock.Lock(); if(!m_Player) m_Player = new cXinelibImagePlayer(Dev, File); m_Lock.Unlock(); return m_Player; } void cXinelibImagesControl::Close(void) { m_Lock.Lock(); if(m_Player) delete m_Player; m_Player = NULL; m_Lock.Unlock(); } void cXinelibImagesControl::Delete(void) { if (!xc.media_enable_delete) { LOGMSG("Deleting files disabled in config"); return; } if(Interface->Confirm(tr("Delete image ?"))) { if(!unlink(m_Playlist->Current()->Filename)) { m_Playlist->Del(m_Playlist->Current()); Seek(0); } } } cOsdObject *cXinelibImagesControl::GetInfo(void) { return new cMetainfoMenu(m_Playlist->Current()->Filename); } void cXinelibImagesControl::Seek(int Rel) { m_Playlist->Seek(Rel); const char *Filename = m_Playlist->Current()->Filename; cStatus::MsgReplaying(this, m_Playlist->Current()->Title, Filename, true); m_Player->ShowImage(Filename); m_LastShowTime = time(NULL); strn0cpy(xc.browse_images_dir, Filename, sizeof(xc.browse_images_dir)); } void cXinelibImagesControl::Show(void) { bool Play = (m_Speed!=0), Forward = m_Speed>=0; int Speed = abs(m_Speed); if(!m_DisplayReplay) { m_DisplayReplay = Skins.Current()->DisplayReplay(m_ShowModeOnly); } if(!m_ShowModeOnly) { int count = m_Playlist->Count(); int index = m_Playlist->Current()->Index(); char t[128] = ""; m_DisplayReplay->SetTitle(m_Playlist->Current()->Title); m_DisplayReplay->SetProgress(index, count); sprintf(t, "%d", count); m_DisplayReplay->SetTotal( t ); sprintf(t, "%d", index+1); m_DisplayReplay->SetCurrent( t ); } m_DisplayReplay->SetMode(Play, Forward, Speed); m_DisplayReplay->Flush(); } void cXinelibImagesControl::Hide(void) { if(m_DisplayReplay) { delete m_DisplayReplay; m_DisplayReplay = NULL; } } eOSState cXinelibImagesControl::ProcessKey(eKeys Key) { switch(Key) { case kBack: Hide(); Close(); BackToMenu(ShowImages); //return osPlugin; return osEnd; case kYellow: Delete(); break; case kStop: case kBlue: Hide(); Close(); return osEnd; case kPrev: case kLeft: Seek(-1); break; case kNext: case kRight: Seek(1); break; case kUp: Seek(5); break; case kDown: Seek(-5); break; case kPause: m_Speed = 0; break; case kPlay: m_Speed = 2; break; case kFastFwd: m_Speed++; break; case kFastRew: m_Speed--; break; case kOk: if(m_DisplayReplay) { if(m_ShowModeOnly) { Hide(); m_ShowModeOnly = false; Show(); } else { Hide(); } } else { m_ShowModeOnly = true; Show(); } break; default: break; } static const int Speed2Time[] = { 0, 5, 3, 1 }; if(m_Speed > 3) m_Speed = 3; if(m_Speed < -3) m_Speed = -3; if(Key == kNone && m_Speed != 0) { if(m_LastShowTime + Speed2Time[m_Speed<0 ? -m_Speed : m_Speed] <= time(NULL)) Seek(sgn(m_Speed)); } if (m_DisplayReplay) Show(); return osContinue; } // --- Exteral interface --------------------------------------------------- // // cPlayerFactory // static cPlaylist *CreatePlaylist(const char *Mrl) { cPlaylist *pl = new cPlaylist(); pl->Read(Mrl); return pl; } cControl *CreateControl(cXinelibDevice *Dev, ePlayMode PlayMode, cPlaylist *Playlist, bool BackToMenu) { if (PlayMode == pmVideoOnly) { return new cXinelibImagesControl(Dev, Playlist); } LOGMSG("cPlayerFactory::Create(cPlaylist*) not implemented for PlayMode %d !", (int)PlayMode); return NULL; } cControl *CreateControl(cXinelibDevice *Dev, ePlayMode PlayMode, const char *Mrl, const char *SubFile, bool BackToMenu) { // Special mrls if (!strncmp(Mrl, "dvd:/", 5)) return new cXinelibDvdPlayerControl(Dev, Mrl); if (!strncmp(Mrl, "bluray:/", 8)) return new cXinelibBdPlayerControl(Dev, Mrl); if (!strncmp(Mrl, "bd:/", 4)) return new cXinelibBdPlayerControl(Dev, Mrl); if (!strncmp(Mrl, "cdda:/", 6)) return new cXinelibPlayerControl(Dev, ShowMusic, Mrl); if (xc.IsDvdImage(Mrl)) return new cXinelibDvdPlayerControl(Dev, Mrl); // Playmode if (PlayMode == pmAudioOnly) return new cXinelibPlayerControl(Dev, ShowMusic, Mrl); if (PlayMode == pmAudioVideo) return new cXinelibPlayerControl(Dev, ShowFiles, Mrl, SubFile); if (PlayMode == pmVideoOnly) { return new cXinelibImagesControl(Dev, CreatePlaylist(Mrl)); } // guess from playlist content if (xc.IsPlaylistFile(Mrl)) { cPlaylist Playlist; Playlist.Read(Mrl); if (Playlist.Count() < 1) return NULL; if (xc.IsAudioFile(Playlist.First()->Filename)) return new cXinelibPlayerControl(Dev, ShowMusic, Mrl); if (xc.IsImageFile(Playlist.First()->Filename)) { return new cXinelibImagesControl(Dev, CreatePlaylist(Mrl)); } return new cXinelibPlayerControl(Dev, ShowFiles, Mrl); } // guess from file type if (xc.IsAudioFile(Mrl)) return new cXinelibPlayerControl(Dev, ShowMusic, Mrl); if (xc.IsVideoFile(Mrl)) return new cXinelibPlayerControl(Dev, ShowFiles, Mrl, SubFile); if (xc.IsImageFile(Mrl)) { return new cXinelibImagesControl(Dev, CreatePlaylist(Mrl)); } // default return new cXinelibPlayerControl(Dev, ShowFiles, Mrl, SubFile); } void cPlayerFactory::Queue(cXinelibDevice *Dev, const char *Mrl) { cXinelibPlayerControl::Queue(Dev, Mrl); } bool cPlayerFactory::IsOpen(void) { return cXinelibPlayerControl::IsOpen(); } bool cPlayerFactory::Launch(cXinelibDevice *Dev, ePlayMode PlayMode, const char *Mrl, const char *SubFile, bool BackToMenu) { cControl *Control = CreateControl(Dev, PlayMode, Mrl, SubFile, BackToMenu); if (!Control) { LOGMSG("cPlayerFactory::Launch(%s) failed !", Mrl); return false; } cControl::Shutdown(); cControl::Launch(Control); return true; } bool cPlayerFactory::Launch(cXinelibDevice *Dev, ePlayMode PlayMode, cPlaylist *Playlist, bool BackToMenu) { cControl *Control = CreateControl(Dev, PlayMode, Playlist, BackToMenu); if (!Control) { LOGMSG("cPlayerFactory::Launch(cPlaylist*) failed !"); delete Playlist; return false; } cControl::Shutdown(); cControl::Launch(Control); return true; } ��������������������������������������xineliboutput-2.0.0/logdefs.h�����������������������������������������������������������������������0000644�0001750�0001750�00000012444�13061253352�014025� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * logdefs.h: Logging and debug output * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _LOGDEFS_H_ #define _LOGDEFS_H_ #if !defined(__GNUC__) || __GNUC__ < 3 # define LIKELY(x) (x) # define UNLIKELY(x) (x) #else # define LIKELY(x) __builtin_expect((x),1) # define UNLIKELY(x) __builtin_expect((x),0) #endif /* * Default module name (goes to every log line) */ #ifndef LOG_MODULENAME # define LOG_MODULENAME "[xine..put] " #endif /* * Logging functions, should not be used directly */ #include <syslog.h> /* logging levels: LOG_ERR, LOG_INFO, LOG_DEBUG */ #define SYSLOGLEVEL_NONE 0 #define SYSLOGLEVEL_ERRORS 1 #define SYSLOGLEVEL_INFO 2 #define SYSLOGLEVEL_DEBUG 3 #define SYSLOGLEVEL_VERBOSE 4 #if defined(esyslog) # define x_syslog(l,m,x...) syslog_with_tid(l, m x) #else # ifdef __cplusplus extern "C" { # endif /* from xine_frontend.c or vdr/tools.c: */ extern int SysLogLevel; /* errors, info, debug */ /* from logdefs.c: */ extern int LogToSysLog; void x_syslog(int level, const char *module, const char *fmt, ...) __attribute__((format (printf, 3, 4))) __attribute__((visibility("default"))); # ifdef __cplusplus } /* extern "C" { */ # endif #endif /* VDR */ #ifdef NEED_x_syslog # error NEED_x_syslog is deprecated #endif /* * Macros used for logging */ #include <errno.h> #define LOG_ERRNO \ x_syslog(LOG_ERR, LOG_MODULENAME, " (ERROR (%s,%d): %s)", \ __FILE__, __LINE__, strerror(errno)) #define LOGERR(x...) \ do { \ if (LIKELY(SysLogLevel >= SYSLOGLEVEL_ERRORS)) { \ x_syslog(LOG_ERR, LOG_MODULENAME, x); \ if (errno) \ LOG_ERRNO; \ } \ } while(0) #define LOGMSG(x...) \ do { \ if (LIKELY(SysLogLevel >= SYSLOGLEVEL_INFO)) \ x_syslog(LOG_INFO, LOG_MODULENAME, x); \ } while(0) #define LOGDBG(x...) \ do { \ if (UNLIKELY(SysLogLevel >= SYSLOGLEVEL_DEBUG)) \ x_syslog(LOG_DEBUG, LOG_MODULENAME, x); \ } while(0) #define LOGVERBOSE(x...) \ do { \ if (UNLIKELY(SysLogLevel >= SYSLOGLEVEL_VERBOSE)) \ x_syslog(LOG_DEBUG, LOG_MODULENAME, x); \ } while(0) #define TRACELINE LOGDBG("at %s:%d %s", __FILE__, __LINE__, __FUNCTION__) /* * ASSERT */ #ifdef NDEBUG # define ASSERT(expr) #else # define ASSERT(expr,fatal) \ do { \ if(UNLIKELY(!(expr))) { \ LOGERR("Asseretion failed: %s at %s:%d (%s)", \ #expr, __FILE__, __LINE__, __FUNCTION__); \ if(fatal) \ abort(); \ } \ } while(0) #endif #define ASSERT_RET(expr,ret) \ do { \ if(UNLIKELY(!(expr))) { \ LOGMSG("Asseretion failed: %s at %s:%d (%s)", \ #expr, __FILE__, __LINE__, __FUNCTION__); \ ret; \ } \ } while(0) /* * Plugin (call)trace */ #ifdef XINELIBOUTPUT_DEBUG # ifdef __cplusplus # # include <fstream> # include <iostream> # include <stdio.h> # # ifndef TRACE_IDENT # define TRACE_IDENT "" # endif # if defined(XINELIBOUTPUT_DEBUG_STDOUT) # define TRACE(x) do {std::cout << TRACE_IDENT << x << "\n"; fflush(stdout);}while(0) # elif defined(XINELIBOUTPUT_DEBUG_STDERR) # define TRACE(x) do {std::cerr << TRACE_IDENT << x << "\n"; fflush(stderr);}while(0) # else # error No trace target ! # endif # define TRACEF(x) cTraceFunctionCall _x_cTraceFunctionCall(x); class cTraceFunctionCall { public: const char *m_name; cTraceFunctionCall(const char *name) : m_name(name) { TRACE(m_name << " - Enter"); } ~cTraceFunctionCall() { TRACE(m_name << " - Leave "); } }; # endif #else # define TRACE(x) # define TRACEF(x) #endif /* * Execution time tracker: * log a message when function execution takes longer than expected */ #ifdef __cplusplus # ifdef TRACK_EXEC_TIME class cTimeTracker { private: const char *m_Message; const char *m_Where; uint64_t m_Start; uint64_t m_Trigger; public: cTimeTracker(const char *Message, int TriggerMs, const char *Where) { m_Message = Message; m_Where = Where; m_Trigger = TriggerMs; m_Start = cTimeMs::Now(); } ~cTimeTracker() { if(cTimeMs::Now() - m_Start > m_Trigger) LOGMSG("********** TimeTracker hit in %s: %d ms %s", m_Where, (int)(cTimeMs::Now() - m_Start), m_Message?m_Message:""); } }; # define TRACK_TIME(limit) cTimeTracker _timetracker(NULL,limit,__PRETTY_FUNCTION__) # define TRACK_TIME_EXT(limit,msg) cTrimeTracker __timetracker(msg,limit,__PRETTY_FUNCTION__) # else # define TRACK_TIME(limit) # define TRACK_TIME_EXT(limit,msg) # endif # endif #endif /* _LOGDEFS_H_ */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/logdefs.c�����������������������������������������������������������������������0000644�0001750�0001750�00000002152�13061253352�014013� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * logdefs.c: Logging and debug output * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "logdefs.h" #include <stdio.h> #include <unistd.h> #include <sys/syscall.h> #include <stdarg.h> #if !defined(__APPLE__) && !defined(__FreeBSD__) # include <linux/unistd.h> /* syscall(__NR_gettid) */ #endif /* next symbol is dynamically linked from input plugin */ int LogToSysLog __attribute__((visibility("default"))) = 1; /* log to syslog instead of console */ void x_syslog(int level, const char *module, const char *fmt, ...) { va_list argp; char buf[512]; va_start(argp, fmt); vsnprintf(buf, 512, fmt, argp); buf[sizeof(buf)-1] = 0; #if !defined(__APPLE__) && !defined(__FreeBSD__) if(!LogToSysLog) { fprintf(stderr,"[%ld] %s%s\n", (long int)syscall(__NR_gettid), module, buf); } else { syslog(level, "[%ld] %s%s", (long int)syscall(__NR_gettid), module, buf); } #else if(!LogToSysLog) { fprintf(stderr, "%s%s\n", module, buf); } else { syslog(level, "%s%s", module, buf); } #endif va_end(argp); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/include/������������������������������������������������������������������������0000755�0001750�0001750�00000000000�13061253352�013647� 5����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/include/vdr-xineliboutput/������������������������������������������������������0000755�0001750�0001750�00000000000�13061253352�017353� 5����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/include/vdr-xineliboutput/service_pip.h�����������������������������������������0000644�0001750�0001750�00000002127�13061253352�022036� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vdr-xineliboutput/service_pip.h: xineliboutput Picture-In-Picture service interface * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef VDR_XINELIBOUTPUT_SERVICE_PIP_H #define VDR_XINELIBOUTPUT_SERVICE_PIP_H /* * Example: * * cXineliboutputPipService *pip_if; * * if (Plugin->Service(XINELIBOUTPUT_SERVICE_PIP_ID, &pip_if)) { * while(...) * pip_if->PlayTs(...); * delete pip_if; * } * */ #define XINELIBOUTPUT_SERVICE_PIP_ID "Xineliboutput_Pip_Open" #define XINELIBOUTPUT_SERVICE_PIP_VERSION 0x0100 class cXineliboutputPipService { public: /* Set location and size (in % of full screen) * Example: * Blend PIP picture to left-top corner, resize to 20% of full screen: * SetPosition(5, 5, 20, 20); */ virtual void SetPosition(uint X, uint Y, uint W, uint H) = 0; /* PIP input: single MPEG-TS video packet */ virtual int PlayTs(const uint8_t *Data) = 0; virtual ~cXineliboutputPipService() {}; }; #endif /* VDR_XINELIBOUTPUT_SERVICE_PIP_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/frontend_svr.h������������������������������������������������������������������0000644�0001750�0001750�00000010346�13061253352�015112� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * frontend_svr.h: server for remote frontends * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_FRONTEND_SVR_H #define __XINELIB_FRONTEND_SVR_H #include "config.h" #include "frontend.h" //----------------------------- cXinelibServer -------------------------------- #define CTRL_BUF_SIZE 1024 class cBackgroundWriterI; class cUdpScheduler; class cStcFuture; class cCmdFutures; class cConnState; class cXinelibDevice; #include "tools/cxsocket.h" class cXinelibServer : public cXinelibThread { public: cXinelibServer(cXinelibDevice *Dev, int listen_port); virtual ~cXinelibServer(); protected: virtual void Action(void); public: // Playback control virtual void TrickSpeed(int Speed, bool Backwards); // Data transfer virtual int Poll(cPoller &Poller, int TimeoutMs); virtual bool Flush(int TimeoutMs); virtual void Clear(void); virtual int Play(const uchar *buf, int len, eStreamId StreamId = sidVdr); virtual void OsdCmd(void *cmd); virtual int64_t GetSTC(); virtual void SetHDMode(bool On); // Image grabbing virtual uchar *GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY); // Playback files virtual int PlayFileCtrl(const char *Cmd, int TimeoutMs=-1); virtual bool EndOfStreamReached(void); // Configuration virtual bool Listen(int port); virtual int SupportsTrueColorOSD(void); protected: // Playback control virtual int Xine_Control(const char *cmd); virtual int Xine_Control_Sync(const char *cmd); virtual int Xine_Control_Result(const char *cmd, uint TimeoutMs); virtual void Sync(void); protected: // Handling of messages from client(s) void Handle_Discovery_Broadcast(void); void Handle_ClientConnected(int fd); void Read_Control(int cli); void Handle_Control(int cli, const char *cmd); void Handle_Control_PIPE (int cli, const char *arg); void Handle_Control_RTP (int cli, const char *arg); void Handle_Control_UDP (int cli, const char *arg); void Handle_Control_DATA (int cli, const char *arg); void Handle_Control_KEY (int cli, const char *arg); void Handle_Control_UDP_RESEND(int cli, const char *arg); void Handle_Control_CONFIG (int cli); void Handle_Control_GRAB (int cli, const char *arg); void Handle_Control_CONTROL (int cli, const char *arg); void Handle_Control_HTTP (int cli, const char *arg); void Handle_Control_RTSP (int cli, const char *arg); void CloseDataConnection(int cli); void CloseConnection (int cli); protected: // Data int m_Port; int m_ServerId; int fd_listen; int fd_discovery; void *m_hAvahi; cxSocket fd_control[MAXCLIENTS]; int fd_data [MAXCLIENTS]; int m_OsdTimeouts[MAXCLIENTS]; char m_CtrlBuf [MAXCLIENTS][CTRL_BUF_SIZE + 1]; int m_CtrlBufPos [MAXCLIENTS]; int m_ConnType [MAXCLIENTS]; // Control connection type. See frontend_svr.c. bool m_bUdp [MAXCLIENTS]; // Client uses UDP transport bool m_bMulticast [MAXCLIENTS]; // Client uses multicast RTP bool m_bConfigOk [MAXCLIENTS]; // Client has been configured bool m_bArgbOSD [MAXCLIENTS]; // Client supports ARGB OSD int m_iMulticastMask; // bit [cli] is 1 or 0. 1 == multicast in use. int m_MasterCli; // Master client (controls playback speed) cString m_PipesDir; cString m_AllowedHostsFile; cBackgroundWriterI *m_Writer[MAXCLIENTS]; // buffered output (pipe/tcp/http) cConnState *m_State[MAXCLIENTS]; // connection state (http/rtsp) cUdpScheduler *m_Scheduler; // Storage for return values of pending RPCs cStcFuture *m_StcFuture; cCmdFutures *m_Futures; int m_Token; int AllocToken(void); bool HasClients(void); // Cache current PAT/PMT for new clients uchar *m_Header; size_t m_HeaderLength; // bytes used size_t m_HeaderSize; // bytes allocated public: void SetHeader(const uchar *Data, int Length, bool Reset = false); }; #endif // __XINELIB_FRONTEND_SVR_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/frontend_svr.c������������������������������������������������������������������0000644�0001750�0001750�00000146724�13061253352�015117� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * frontend_svr.c: server for remote frontends * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_FORMAT_MACROS #define __STDC_CONSTANT_MACROS #include <inttypes.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <time.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netdb.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <netinet/tcp.h> #include <vdr/config.h> #include <vdr/tools.h> #include <vdr/plugin.h> #include <vdr/device.h> #include "logdefs.h" #include "config.h" #include "xine_input_vdr_net.h" // stream header(s) #include "tools/osd_command.h" // osd commands #include "tools/cxsocket.h" #include "tools/future.h" #include "tools/backgroundwriter.h" #include "tools/udp_pes_scheduler.h" #include "tools/http.h" #include "tools/vdrdiscovery.h" #include "tools/sdp.h" #include "tools/rle.h" #include "tools/sys_cap.h" #include "tools/avahi.h" #include "frontend_svr.h" #include "device.h" #include "osd.h" #define ALLOWED_HOSTS_FILE "xineliboutput/allowed_hosts.conf" #define MAX_OSD_TIMEOUTS (25*5) /* max. rate 25 updates/s -> at least 5 seconds */ #define LOG_OSD_BANDWIDTH (128*1024) /* log messages if OSD bandwidth > 1 Mbit/s */ #define PLAYFILE_CTRL_TIMEOUT 300 /* ms */ #define PLAYFILE_TIMEOUT 20000 /* ms */ #define REMOTE_FLUSH_TIMEOUT 1000 /* ms */ #define REMOTE_SYNC_TIMEOUT 1000 /* ms */ #define REMOTE_STC_TIMEOUT 300 /* ms */ #undef MIN #define MIN(a,b) ( (a) < (b) ? (a) : (b)) typedef struct { int Size; uchar *Data; } grab_result_t; class cStcFuture : public cFuture<int64_t> {}; class cReplyFuture : public cFuture<int>, public cListObject {}; class cGrabReplyFuture : public cFuture<grab_result_t>, public cListObject {}; class cCmdFutures : public cHash<cReplyFuture> {}; class cAllowedHosts : public cSVDRPhosts { public: cAllowedHosts(const cString& AllowedHostsFile) { if (!Load(AllowedHostsFile, true, true)) { LOGMSG("Invalid or missing %s. Adding 127.0.0.1 to list of allowed hosts.", *AllowedHostsFile); cSVDRPhost *localhost = new cSVDRPhost; if (localhost->Parse("127.0.0.1")) Add(localhost); else delete localhost; } } }; //----------------------------- cXinelibServer -------------------------------- // (control stream) connection types enum { ctDetecting = 0x00, ctControl = 0x01, ctHttp = 0x02, ctRtsp = 0x04, // TCP/RTSP + UDP/RTP ctRtspMux = 0x08 // TCP: multiplexed RTSP control + RTP/RTCP data/control }; // (data) connection types enum { dtPipe = 0x01, dtTcp = 0x02, dtUdp = 0x04, dtRtp = 0x08, dtHttp = 0x10, dtRtspMux = 0x20, }; // (data) connection properties #define DATA_STREAM(dt) ((dt) & (dtPipe | dtTcp | dtHttp | dtRtspMux)) #define DATA_DATAGRAM(dt) ((dt) & (dtUdp | dtRtp)) #define DATA_NOPOLL(dt) ((dt) & (dtHttp | dtRtspMux)) #define DATA_NOCONTROL(dt) ((dt) & (dtHttp | dtRtspMux)) cXinelibServer::cXinelibServer(cXinelibDevice *Dev, int listen_port) : cXinelibThread(Dev, "Remote decoder/display server (cXinelibServer)") { int i; for(i=0; i<MAXCLIENTS; i++) { fd_data[i] = -1; m_OsdTimeouts[i] = 0; m_Writer[i] = NULL; m_State[i] = NULL; m_bMulticast[i] = 0; m_bConfigOk[i] = false; m_bArgbOSD[i] = false; m_bUdp[i] = 0; m_ConnType[i] = ctDetecting; } m_Port = listen_port; m_ServerId = time(NULL) ^ getpid(); m_hAvahi = NULL; fd_listen = -1; fd_discovery = -1; m_iMulticastMask = 0; m_MasterCli = -1; m_Scheduler = new cUdpScheduler; m_StcFuture = new cStcFuture; m_Futures = new cCmdFutures; cString Base(cPlugin::ConfigDirectory()); if(*Base) { m_PipesDir = cString::sprintf("%s/xineliboutput/pipes.%d", *Base, getpid()); m_AllowedHostsFile = cString::sprintf("%s/" ALLOWED_HOSTS_FILE, *Base); } else { LOGMSG("cXinelibServer: cPlugin::ConfigDirectory() failed !"); m_PipesDir = cString::sprintf("/tmp/xineliboutput/pipes.%d", getpid()); m_AllowedHostsFile = cString::sprintf("/video/" ALLOWED_HOSTS_FILE); } m_Token = 1; m_Header = NULL; m_HeaderLength = 0; m_HeaderSize = 0; } cXinelibServer::~cXinelibServer() { int i; Cancel(-1); CLOSESOCKET(fd_listen); CLOSESOCKET(fd_discovery); cHttpStreamer::CloseAll(); for(i=0; i<MAXCLIENTS; i++) CloseConnection(i); Cancel(3); delete m_StcFuture; delete m_Futures; delete m_Scheduler; free(m_Header); if (m_hAvahi) { x_avahi_stop(m_hAvahi); } } void cXinelibServer::Clear(void) { TRACEF("cXinelibServer::Clear"); LOCK_THREAD; SetHeader(NULL, 0, true); for(int i = 0; i < MAXCLIENTS; i++) if(fd_control[i].open() && m_Writer[i]) m_Writer[i]->Clear(); if(m_Scheduler) m_Scheduler->Clear(); cXinelibThread::Clear(); } void cXinelibServer::CloseDataConnection(int cli) { if(m_bUdp[cli] && fd_data[cli]>=0) m_Scheduler->RemoveHandle(fd_data[cli]); CLOSESOCKET(fd_data[cli]); if(m_Writer[cli]) { delete m_Writer[cli]; m_Writer[cli] = NULL; } m_bUdp[cli] = false; m_bMulticast[cli] = false; m_bConfigOk[cli] = false; m_bArgbOSD[cli] = false; m_iMulticastMask &= ~(1<<cli); if(!m_iMulticastMask && !xc.remote_rtp_always_on) m_Scheduler->RemoveRtp(); if (cli == m_MasterCli) m_MasterCli = -1; } void cXinelibServer::CloseConnection(int cli) { CloseDataConnection(cli); if(fd_control[cli].open()) { LOGMSG("Closing connection %d", cli); fd_control[cli].close(); if(m_State[cli]) { delete m_State[cli]; m_State[cli] = NULL; } m_Dev->ForcePrimaryDevice(false); } } static int write_osd_command(cxSocket& s, osd_command_t *cmd) { cxPoller p(s, true); if(!p.Poll(100)) { LOGMSG("write_osd_command: poll failed, OSD send skipped"); return 0; } ssize_t max = s.tx_buffer_free(); ssize_t size = (ssize_t)8 + (ssize_t)(sizeof(osd_command_t)) + (ssize_t)(sizeof(osd_clut_t) * ntohl(cmd->colors)) + (ssize_t)(ntohl(cmd->datalen)); if(max > 0 && max < MIN(size, 1024)) { /* #warning TODO: buffer latest failed OSD and retry -> skipped OSDs can be left out but latest will be always delivered */ LOGMSG("write_osd_command: socket buffer full, OSD send skipped (got %d ; need %d", (int)max, (int)size); return 0; } cmd->size = sizeof(osd_command_t); if(8 != s.write("OSDCMD\r\n", 8, 100)) { LOGDBG("write_osd_command: write (command) failed"); return -1; } if((ssize_t)sizeof(osd_command_t) != s.write(cmd, sizeof(osd_command_t), 100)) { LOGDBG("write_osd_command: write (data) failed"); return -1; } if(cmd->palette && cmd->colors && (ssize_t)(sizeof(osd_clut_t)*ntohl(cmd->colors)) != s.write(cmd->palette, sizeof(osd_clut_t)*ntohl(cmd->colors), 100)) { LOGDBG("write_osd_command: write (palette) failed"); return -1; } if(cmd->data && cmd->datalen && (ssize_t)ntohl(cmd->datalen) != s.write(cmd->data, ntohl(cmd->datalen), 300)) { LOGDBG("write_osd_command: write (bitmap) failed"); return -1; } return 1; } void cXinelibServer::OsdCmd(void *cmd_gen) { TRACEF("cXinelibServer::OsdCmd"); int i; LOCK_THREAD; // check if there are any clients if(!HasClients()) return; if(cmd_gen) { void *compressed_data = NULL; osd_command_t *cmd = (osd_command_t*)cmd_gen; osd_command_t cmdnet; memcpy(&cmdnet, cmd, sizeof(osd_command_t)); if (cmd->data) { // compress data if (cmd->cmd == OSD_Set_LUT8) { int num_rle = 0; cmdnet.datalen = rle_compress_hdmv(&cmdnet.raw_data, cmd->raw_data, cmd->w, cmd->h, &num_rle); cmdnet.num_rle = num_rle; cmdnet.cmd = OSD_Set_HDMV; compressed_data = cmdnet.raw_data; // free it later } else if (cmd->cmd == OSD_Set_RLE) { cmdnet.raw_data = (uint8_t *)malloc(cmd->datalen); cmdnet.datalen = rle_recompress_net(cmdnet.raw_data, cmd->data, cmd->num_rle); compressed_data = cmdnet.raw_data; // free it later } } // -> network byte order hton_osdcmd(cmdnet); for(i = 0; i < MAXCLIENTS; i++) { if(fd_control[i].open() && m_bConfigOk[i]) { int r = write_osd_command(fd_control[i], &cmdnet); if(r < 0) { LOGMSG("Send OSD command failed, closing connection"); CloseConnection(i); } else if(r == 0) { if(m_OsdTimeouts[i]++ > MAX_OSD_TIMEOUTS) { LOGMSG("Too many OSD timeouts, dropping client"); CloseConnection(i); } } else { m_OsdTimeouts[i] = 0; } } } free(compressed_data); #ifdef LOG_OSD_BANDWIDTH { static int64_t timer = 0LL; static int bytes = 0; int64_t now = cTimeMs::Now(); if(timer + 5000LL < now) { timer = now; bytes = 0; } else if(timer + 1000LL < now) { bytes = bytes / (((int)(now - timer)) / 1000); if(bytes > LOG_OSD_BANDWIDTH) LOGMSG("OSD bandwidth: %d bytes/s (%d kbit/s)", bytes, bytes*8/1024); timer = now; bytes = 0; } bytes += sizeof(osd_command_t) + ntohl(cmdnet.datalen); } #endif } } int64_t cXinelibServer::GetSTC(void) { Lock(); // check if there are any clients if(!HasClients()) { Unlock(); return INT64_C(-1); } // Query client(s) m_StcFuture->Reset(); Xine_Control("GETSTC"); Unlock(); if (!m_StcFuture->Wait(REMOTE_STC_TIMEOUT)) { LOGMSG("cXinelibServer::GetSTC timeout"); return INT64_C(-1); } //if(delay.Elapsed() > 0 && !is_Paused) // LOGMSG("GetSTC: compensating network delay by %s ticks (ms)\n", // delay.Elapsed()*90000/2, delay.Elapsed()/2); return m_StcFuture->Value() /*+ (delay.Elapsed()*90000/2*/; } void cXinelibServer::SetHeader(const uchar *Data, int Length, bool Reset) { LOCK_THREAD; // Lock control thread out if (Reset) m_HeaderLength = 0; if (m_HeaderSize < m_HeaderLength + Length) { if (!m_Header) { m_HeaderSize = Length; m_Header = (uchar*)malloc(m_HeaderSize); } else { m_HeaderSize = m_HeaderLength + Length; m_Header = (uchar*)realloc(m_Header, m_HeaderSize); } } if (m_Header && Data) { memcpy(m_Header + m_HeaderLength, Data, Length); m_HeaderLength += Length; } } int cXinelibServer::Play(const uchar *data, int len, eStreamId StreamId) { int TcpClients = 0, UdpClients = 0, RtpClients = 0; LOCK_THREAD; // Lock control thread out for(int i=0; i<MAXCLIENTS; i++) { if(fd_control[i].open()) { if((m_bConfigOk[i] && fd_data[i] >= 0) || m_ConnType[i] & (ctHttp|ctRtsp)) { if(m_bUdp[i]) { UdpClients++; } else if(m_Writer[i]) { int result = m_Writer[i]->Put(StreamId, m_StreamPos, data, len); if(!result) { LOGMSG("cXinelibServer::Play Write/Queue error (TCP/PIPE)"); CloseConnection(i); } else if(result<0) { LOGMSG("cXinelibServer::Play Buffer overflow (TCP/PIPE)"); if(m_ConnType[i] == ctHttp) m_Writer[i]->Clear(); } TcpClients++; } } } } RtpClients = (m_iMulticastMask || xc.remote_rtp_always_on); if(UdpClients || RtpClients) if(! m_Scheduler->Queue(StreamId, m_StreamPos, data, len)) LOGMSG("cXinelibServer::Play Buffer overflow (UDP/RTP)"); if(TcpClients || UdpClients || RtpClients) cXinelibThread::Play(data, len, StreamId); return len; } void cXinelibServer::SetHDMode(bool On) { cXinelibThread::SetHDMode(On); #if 0 /*#warning TODO*/ LOCK_THREAD; int i; for(i=0; i<MAXCLIENTS; i++) if(m_Writer[i]) m_Writer[i]->SetBuffer(On ? 2048 : 512); m_Scheduler->SetWindow(On ? 512 : 128); #endif } int cXinelibServer::Poll(cPoller &Poller, int TimeoutMs) { // in live mode transponder clock is the master ... // in replay mode local frontend (if present) is master if(m_bLiveMode || (*xc.local_frontend && strncmp(xc.local_frontend, "none", 4))) { if(m_Scheduler->Clients()) return m_Scheduler->Poll(TimeoutMs, false); return DEFAULT_POLL_SIZE; } // replay mode: do { Lock(); int Free = 0xfffff, FreeHttp = 0xfffff, FreeUdp = 0; int Clients = 0, Http = 0, Udp = 0; for(int i=0; i<MAXCLIENTS; i++) { if(fd_control[i].open()) { if(m_bConfigOk[i]) { if(m_Writer[i]) Free = min(Free, m_Writer[i]->Free()); else if(m_bUdp[i]) Udp++; Clients++; } else if(m_ConnType[i] & (ctHttp|ctRtspMux)) { if(m_Writer[i]) { FreeHttp = min(FreeHttp, m_Writer[i]->Free()); Http++; } } } } if(m_iMulticastMask) { Clients++; Udp++; } /* select master timing source for replay mode */ int master = -1; if(Clients && !Udp) { for(int i=0; i<MAXCLIENTS; i++) if(fd_control[i].open() && m_bConfigOk[i] && m_Writer[i]) { master = i; break; } } if(master != m_MasterCli) { if(m_MasterCli >= 0) Xine_Control("MASTER 0"); if(master >= 0) fd_control[master].write_cmd("MASTER 1\r\n"); m_MasterCli = master; } Unlock(); if(!Clients && !Http) { // live mode runs even if there are no clients if(m_bLiveMode) return DEFAULT_POLL_SIZE; // replay is paused when no clients if(TimeoutMs>0) cCondWait::SleepMs(TimeoutMs); return 0; } // in replay mode cUdpScheduler is master timing source if( Free < 8128 || ((FreeUdp = m_Scheduler->Poll(TimeoutMs, true)) < 1) || (!Clients && FreeHttp < 8128)) { if(TimeoutMs > 0) cCondWait::SleepMs(min(TimeoutMs, 5)); TimeoutMs -= 5; } else { Free = min(Free, FreeHttp) / 2070; Free = min(Free, FreeUdp); return max(0, Free); } } while(TimeoutMs > 0); return 0; } bool cXinelibServer::Flush(int TimeoutMs) { if (!HasClients()) return true; int result = true; if(m_Scheduler) result = m_Scheduler->Flush(TimeoutMs) && result; for(int i=0; i<MAXCLIENTS; i++) if(fd_control[i].open() && fd_data[i]>=0 && m_Writer[i]) result = m_Writer[i]->Flush(TimeoutMs) && result; if(TimeoutMs > 50) TimeoutMs = 50; if(result) { cString tmp = cString::sprintf("FLUSH %d %" PRIu64 " %d", TimeoutMs, m_StreamPos, m_Frames); result = (Xine_Control_Result(tmp, REMOTE_FLUSH_TIMEOUT)) <= 0; } return result; } /* * Xine_Control() * * Post control message to client (async RPC) */ int cXinelibServer::Xine_Control(const char *cmd) { TRACEF("cXinelibServer::Xine_Control"); if(cmd && *cmd) { char buf[4096]; int len = snprintf(buf, sizeof(buf), "%s\r\n", cmd); if(len >= (int)sizeof(buf)) { LOGMSG("Xine_Control: command truncated !"); return 0; } LOCK_THREAD; for(int i=0; i<MAXCLIENTS; i++) if(fd_control[i].open() && (fd_data[i]>=0 || m_bMulticast[i]) && m_bConfigOk[i]) if(len != fd_control[i].write(buf, len, 100)) { LOGMSG("Control send failed (%s), dropping client", cmd); CloseConnection(i); } } return 1; } /* * Xine_Control_Sync() * * Post control message to client (message is transported in data stream) */ int cXinelibServer::Xine_Control_Sync(const char *cmd) { TRACEF("cXinelibServer::Xine_Control_Sync"); if(cmd && *cmd) { int i, len, UdpClients = 0, RtpClients = 0; char buf[256]; len = snprintf(buf, sizeof(buf), "%s\r\n", cmd) + 1; if(len >= (int)sizeof(buf)) { LOGMSG("Xine_Control_Sync: command truncated ! (%s)", cmd); len = sizeof(buf); } LOCK_THREAD; for(i=0; i<MAXCLIENTS; i++) if(fd_control[i].open() && m_bConfigOk[i]) { if(fd_data[i] >= 0) { if(m_bUdp[i]) UdpClients++; else if(m_Writer[i]) m_Writer[i]->Put(sidControl, (-1ULL), (const uchar*)buf, len); } } RtpClients = (m_iMulticastMask || xc.remote_rtp_always_on); if(UdpClients || RtpClients) if(! m_Scheduler->Queue(sidControl, (uint64_t)(-1ULL), (const uchar*)buf, len)) LOGMSG("cXinelibServer::Xine_Control_Sync overflow (UDP/RTP)"); } return 1; } /* * Sync() * * Wait until all control messages have been processed by the client */ void cXinelibServer::Sync(void) { Xine_Control_Result("SYNC", REMOTE_SYNC_TIMEOUT); } void cXinelibServer::TrickSpeed(int Speed, bool Backwards) { if(Speed == 0) { m_Scheduler->Pause(true); } else { m_Scheduler->Pause(false); m_Scheduler->TrickSpeed(Speed == -1 ? 1 : Speed); } cXinelibThread::TrickSpeed(Speed, Backwards); } bool cXinelibServer::EndOfStreamReached(void) { LOCK_THREAD; /* Check if there are any clients */ if(!HasClients()) return true; return cXinelibThread::EndOfStreamReached(); } int cXinelibServer::AllocToken(void) { LOCK_THREAD; m_Token = (m_Token+1) & 0xffff; cXinelibThread::Xine_Control((const char *)"TOKEN", m_Token); return m_Token; } /* * Xine_Control_Result() * * Post control message to client and wait for result (synchronous RPC). */ int cXinelibServer::Xine_Control_Result(const char *Cmd, uint TimeoutMs) { if(!HasClients()) { return -1; } if (TimeoutMs > 20000) { LOGMSG("Xine_Control_Result(): very long tomeout (%d sec) !", TimeoutMs/1000); TimeoutMs = 20000; } Lock(); /* Get token, send it to client and set future for it */ int token = AllocToken(); cReplyFuture future; m_Futures->Add(&future, token); /* Send actual command */ Xine_Control(Cmd); Unlock(); /* When server thread get REPLY %d %d (first %d == token, second returned value) * it sets corresponding future (by token; if found) in list * and removes it from list. */ #ifdef XINELIBOUTPUT_DEBUG int64_t t = cTimeMs::Now(); #endif future.Wait(TimeoutMs); Lock(); m_Futures->Del(&future, token); Unlock(); if (!future.IsReady()) { LOGMSG("cXinelibServer::Xine_Control_Result: Timeout (%s, %d ms) %d", Cmd, TimeoutMs, token); return -1; } return future.Value(); } bool cXinelibServer::HasClients(void) { LOCK_THREAD; int i; for(i=0; i<MAXCLIENTS; i++) if(fd_control[i].open() && m_bConfigOk[i]) return true; return false; } int cXinelibServer::SupportsTrueColorOSD(void) { LOCK_THREAD; unsigned i, has_clients = 0; for (i = 0; i < MAXCLIENTS; i++) if (fd_control[i].open() && m_bConfigOk[i]) { if (!m_bArgbOSD[i]) return 0; else has_clients++; } return has_clients || xc.truecoloreverytime ? 1 : -1; } int cXinelibServer::PlayFileCtrl(const char *Cmd, int TimeoutMs) { /* Check if there are any clients */ if(!HasClients()) { cHttpStreamer::CloseAll(); return -1; } int result; bool bPlayfile = false; if((!strncmp(Cmd, "PLAYFILE", 8) && (bPlayfile=true)) || (!strncmp(Cmd, "GET", 3) )) { // GETPOS, GETLENGTH, ... if(TimeoutMs < 0) TimeoutMs = bPlayfile ? PLAYFILE_TIMEOUT : PLAYFILE_CTRL_TIMEOUT; result = Xine_Control_Result(Cmd, TimeoutMs); } else { result = cXinelibThread::PlayFileCtrl(Cmd); } if(!*m_FileName) cHttpStreamer::CloseAll(); return result; } bool cXinelibServer::Listen(int listen_port) { LOCK_THREAD; bool result = false; TRACEF("cXinelibServer::Listen"); if (m_hAvahi) { x_avahi_stop(m_hAvahi); m_hAvahi = NULL; } if(listen_port <= 0 || listen_port > 0xffff) { CLOSESOCKET(fd_listen); CLOSESOCKET(fd_discovery); if(m_Scheduler) m_Scheduler->RemoveRtp(); cHttpStreamer::CloseAll(); LOGMSG("Not listening for remote connections"); return false; } if(fd_listen<0 || listen_port != m_Port) { m_Port = listen_port; CLOSESOCKET(fd_listen); int iReuse = 1; struct sockaddr_in name; memset(&name, 0, sizeof(name)); name.sin_family = AF_INET; name.sin_addr.s_addr = htonl(INADDR_ANY); name.sin_port = htons(m_Port); if(xc.remote_local_ip[0]) { uint32_t ip = inet_addr(xc.remote_local_ip); if(ip != INADDR_NONE) { char txt[128]; name.sin_addr.s_addr = ip; LOGDBG("Binding server to %s", cxSocket::ip2txt(name.sin_addr.s_addr, htons(m_Port), txt)); } else { LOGERR("Local interface address %s is invalid !", xc.remote_local_ip); } } fd_listen = socket(PF_INET,SOCK_STREAM,0); if (fd_listen < 0) { LOGERR("cXinelibServer: error creating listen socket"); } else { if (setsockopt(fd_listen, SOL_SOCKET, SO_REUSEADDR, &iReuse, sizeof(int)) < 0) { LOGERR("cXinelibServer: error setting REUSE for listen socket"); } if (bind(fd_listen, (struct sockaddr *)&name, sizeof(name)) < 0) { LOGERR("cXinelibServer: bind error %s port %d: %s", xc.remote_local_ip[0] ? xc.remote_local_ip : "", m_Port, strerror(errno)); CLOSESOCKET(fd_listen); } else if(listen(fd_listen, MAXCLIENTS)) { LOGERR("cXinelibServer: listen error (port %d): %s", m_Port, strerror(errno)); CLOSESOCKET(fd_listen); } else { LOGMSG("Listening on port %d", m_Port); result = true; } } } else { result = true; } // set listen for discovery messages CLOSESOCKET(fd_discovery); if(xc.remote_usebcast) { fd_discovery = udp_discovery_init(); if (fd_discovery >= 0) { if(udp_discovery_broadcast(fd_discovery, m_Port, xc.remote_local_ip) < 0) CLOSESOCKET(fd_discovery); else LOGMSG("Listening for UDP broadcasts on port %d", m_Port); } } // set up multicast sockets if(m_Scheduler) { m_Scheduler->RemoveRtp(); if(xc.remote_usertp) { if(xc.remote_rtp_always_on) LOGMSG("WARNING: RTP Configuration: transmission is always on !"); if(xc.remote_rtp_always_on || m_iMulticastMask) m_Scheduler->AddRtp(); } } // AVAHI announces m_hAvahi = x_avahi_start(listen_port, xc.remote_use_rtsp, xc.remote_use_http); return result; } uchar *cXinelibServer::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { cGrabReplyFuture future; uchar *result = NULL; cString cmd; cmd = cString::sprintf("GRAB %s %d %d %d\r\n", Jpeg ? "JPEG" : "PNM", Quality, SizeX, SizeY); Lock(); /* Check if there are any clients */ if(!HasClients()) { Unlock(); return NULL; } int token = AllocToken(); m_Futures->Add(&future, token); // might be better to request image from one client only (?) Xine_Control(cmd); Unlock(); if(future.Wait(5000)) { grab_result_t r = future.Value(); if((Size = r.Size) > 0) { LOGDBG("cXinelibServer::GrabImage: image size is %d bytes", Size); result = r.Data; } else { LOGMSG("cXinelibServer::Grab: Grab failed (%d)", Size); } } else { LOGMSG("cXinelibServer::Grab: Timeout (5000 ms)"); } Lock(); m_Futures->Del(&future, token); Unlock(); return result; } // // (Client) Control message handling // void cXinelibServer::Handle_Control_PIPE(int cli, const char *arg) { LOGDBG("Trying PIPE connection ..."); CloseDataConnection(cli); // // TODO: client should create pipe; waiting here is not good thing ... // if(!xc.remote_usepipe) { LOGMSG("PIPE transport disabled in configuration"); fd_control[cli].write_cmd("PIPE: Pipe transport disabled in config.\r\n"); return; } MakeDirs(m_PipesDir, true); int i; cString pipeName; for(i=0; i<10; i++) { pipeName = cString::sprintf("%s/pipe.%d", *m_PipesDir, i); if(mknod(pipeName, 0644|S_IFIFO, 0) < 0) { unlink(pipeName); continue; } else break; } if(i>=10) { LOGERR("Pipe creation failed (%s)", *pipeName); RemoveFileOrDir(m_PipesDir, false); fd_control[cli].write_cmd("PIPE: Pipe creation failed.\r\n"); return; } fd_control[cli].printf("PIPE %s\r\n", *pipeName); cxPoller poller(fd_control[cli]); poller.Poll(500); /* quite short time ... */ int fd; if((fd = open(pipeName, O_WRONLY|O_NONBLOCK)) < 0) { LOGDBG("Pipe not opened by client"); /*write_cmd(fd_control[cli], "PIPE NONE\r\n");*/ unlink(pipeName); RemoveFileOrDir(m_PipesDir, false); return; } if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK) < 0) { LOGERR("error setting pipe to non-blocking mode"); } //LOGDBG("cXinelibServer::Handle_Control: pipe %s open", pipeName); unlink(pipeName); /* safe to remove now, both ends are open or closed. */ RemoveFileOrDir(m_PipesDir, false); fd_control[cli].write_cmd("PIPE OK\r\n"); if (m_Writer[cli]) delete m_Writer[cli]; m_Writer[cli] = new cTcpWriter(fd); if (m_Header) m_Writer[cli]->Put(sidVdr, 0, m_Header, m_HeaderLength); fd_data[cli] = fd; } void cXinelibServer::Handle_Control_DATA(int cli, const char *arg) { int clientId = -1; unsigned int ipc, portc; LOGDBG("Data connection (TCP) requested"); CloseDataConnection(cli); if(!xc.remote_usetcp) { LOGMSG("TCP transports disabled in configuration"); fd_control[cli].write_cmd("TCP: TCP transport disabled in config.\r\n"); CloseConnection(cli); /* actually closes the new data connection */ return; } /* validate client ID */ if(3 != sscanf(arg, "%d 0x%x:%d", &clientId, &ipc, &portc) || clientId < 0 || clientId >= MAXCLIENTS || !fd_control[clientId].open()) { fd_control[cli].write_cmd("TCP: Error in request (ClientId).\r\n"); LOGDBG("Invalid data connection (TCP) request"); /* close only new data connection, no control connection */ CloseConnection(cli); return; } #if 0 /* check client IP's */ struct sockaddr_in sinc, sind; socklen_t len = sizeof(sinc); sinc.sin_addr.s_addr = 0; sind.sin_addr.s_addr = ~0; fd_control[cli].getpeername((struct sockaddr *)&sind, &len); fd_control[clientId].getpeername((struct sockaddr *)&sinc, &len); if(sinc.sin_addr.s_addr != sind.sin_addr.s_addr) { fd_control[cli].write_cmd("TCP: Error in request (IP does not match).\r\n"); LOGMSG("Invalid data connection (TCP) request: IP does not match: ctrl %x, data %x", (unsigned int)sinc.sin_addr.s_addr, (unsigned int)sind.sin_addr.s_addr); CloseConnection(cli); return; } if(htonl(ipc) != sinc.sin_addr.s_addr || htons(portc) != sinc.sin_port) { fd_control[cli].write_cmd("TCP: Error in request (invalid IP:port).\r\n"); LOGMSG("Invalid data connection (TCP) request: control IP:port does not match" "control: %x:%d client: %x:%d", (unsigned int)sinc.sin_addr.s_addr, (unsigned int)sinc.sin_port, (unsigned int)htonl(ipc), (unsigned int)htons(portc)); CloseConnection(cli); return; } #endif /* close old data connection */ CloseDataConnection(clientId); /* change connection type */ fd_control[cli].write_cmd("DATA\r\n"); fd_data[clientId] = fd_control[cli].handle(true); cli = clientId; if (m_Writer[cli]) delete m_Writer[cli]; m_Writer[cli] = new cTcpWriter(fd_data[cli]); if (m_Header) m_Writer[cli]->Put(sidVdr, 0, m_Header, m_HeaderLength); /* not anymore control connection, so dec primary device reference counter */ m_Dev->ForcePrimaryDevice(false); } void cXinelibServer::Handle_Control_RTP(int cli, const char *arg) { LOGDBG("Trying RTP connection ..."); CloseDataConnection(cli); if(!xc.remote_usertp) { fd_control[cli].write_cmd("RTP: RTP transport disabled in configuration.\r\n"); LOGMSG("RTP transports disabled"); return; } fd_control[cli].printf("RTP %s:%d\r\n", xc.remote_rtp_addr, xc.remote_rtp_port); if(!m_iMulticastMask && !xc.remote_rtp_always_on) m_Scheduler->AddRtp(); m_bMulticast[cli] = true; m_iMulticastMask |= (1<<cli); // Send padding packet before header (PAT/PMT). // Client uses first received UDP/RTP packet to test connection. m_Scheduler->QueuePadding(); if (m_Header) m_Scheduler->Queue(sidVdr, 0, m_Header, m_HeaderLength); } void cXinelibServer::Handle_Control_UDP(int cli, const char *arg) { LOGDBG("Trying UDP connection ..."); CloseDataConnection(cli); if(!xc.remote_useudp) { fd_control[cli].write_cmd("UDP: UDP transport disabled in configuration.\r\n"); LOGMSG("UDP transport disabled in configuration"); return; } int fd = sock_connect(fd_control[cli].handle(), atoi(arg), SOCK_DGRAM); if(fd < 0) { LOGERR("socket() for UDP failed"); fd_control[cli].write_cmd("UDP: Socked failed.\r\n"); return; } fd_control[cli].write_cmd("UDP OK\r\n"); m_bUdp[cli] = true; fd_data[cli] = fd; m_Scheduler->AddHandle(fd); // Send padding packet before header (PAT/PMT). // Client uses first received UDP/RTP packet to test connection. m_Scheduler->QueuePadding(); if (m_Header) m_Scheduler->Queue(sidVdr, 0, m_Header, m_HeaderLength); } void cXinelibServer::Handle_Control_KEY(int cli, const char *arg) { TRACE("cXinelibServer received KEY " << buf); if(!xc.remote_keyboard) { LOGMSG("Handle_Control_KEY(%s): Remote keyboard disabled in config", arg); return; } char buf[256], *pt, *key; bool repeat = false, release = false; strn0cpy(buf, arg, sizeof(buf)); size_t n = *buf ? strlen(buf)-1 : 0; while(n && buf[n]==' ') buf[n--]=0; /* trailing spaces */ if(NULL != (key=strchr(buf, ' '))) { while(*key == ' ') *(key++) = 0; if(NULL != (pt = strchr(key, ' '))) { *(pt++) = 0; if(strstr(pt, "Repeat")) repeat = true; if(strstr(pt, "Release")) release = true; } cXinelibThread::KeypressHandler(buf, key, repeat, release); } else { cXinelibThread::KeypressHandler(NULL, buf, repeat, release); } } void cXinelibServer::Handle_Control_CONFIG(int cli) { m_bConfigOk[cli] = true; fd_control[cli].set_nodelay(true); fd_control[cli].printf("NOVIDEO %d\r\nLIVE %d\r\n", m_bNoVideo?1:0, m_bLiveMode?1:0); fd_control[cli].printf("TRICKSPEED %d%s\r\n", m_TrickSpeed, m_bTrickSpeedBack ? " Backwards" : ""); SetVolume(m_Volume); Configure(); fd_control[cli].write_cmd("CLEAR\r\n"); if(m_bPlayingFile && *m_FileName) { Unlock(); int pos = m_Dev->PlayFileCtrl("GETPOS"); Lock(); if(m_bPlayingFile && *m_FileName) { fd_control[cli].printf("PLAYFILE %d %s %s\r\n", (pos>0?pos/1000:0), xc.audio_visualization, *m_FileName); } } cXinelibOsdProvider::RefreshOsd(); } void cXinelibServer::Handle_Control_UDP_RESEND(int cli, const char *arg) { unsigned int seq1, seq2; uint64_t pos; if( (!fd_data[cli] || !m_bUdp[cli]) && (!m_bMulticast[cli])) { LOGMSG("Got invalid re-send request: no udp/rtp in use"); return; } if(3 == sscanf(arg, "%d-%d %" PRIu64, &seq1, &seq2, &pos)) { if(seq1 <= UDP_SEQ_MASK && seq2 <= UDP_SEQ_MASK && pos <= m_StreamPos) { if(fd_data[cli] >= 0) m_Scheduler->ReSend(fd_data[cli], pos, seq1, seq2); else m_Scheduler->ReSend(-1, pos, seq1, seq2); } else { LOGMSG("Invalid re-send request: %s (send pos=%" PRIu64 ")", arg, m_StreamPos); } } else { LOGMSG("Invalid re-send request: %s (send pos=%" PRIu64 ")", arg, m_StreamPos); } } void cXinelibServer::Handle_Control_GRAB(int cli, const char *arg) { cGrabReplyFuture *f; int token = -1, size = 0; if(2 == sscanf(arg, "%d %d", &token, &size)) { if(size > 0 && size < 20480000) { uchar *result = (uchar*)malloc(size); Unlock(); /* may take a while ... */ ssize_t n = fd_control[cli].read(result, size, 2000); Lock(); if(n == size) { if(NULL != (f = (cGrabReplyFuture*)m_Futures->Get(token))) { grab_result_t r; r.Size = size; r.Data = result; m_Futures->Del(f, token); f->Set(r); result = NULL; } else { LOGMSG("cXinelibServer: Grab image discarded"); } } else { LOGMSG("cXinelibServer: Grab result read() failed"); CloseConnection(cli); } free(result); } else if(NULL != (f = (cGrabReplyFuture*)m_Futures->Get(token))) { grab_result_t r; r.Size = 0; r.Data = NULL; m_Futures->Del(f, token); f->Set(r); } } } void cXinelibServer::Handle_Control_CONTROL(int cli, const char *arg) { fd_control[cli].printf("VDR-" VDRVERSION " " "xineliboutput-" XINELIBOUTPUT_VERSION " " "READY\r\nCLIENT-ID %d\r\n", cli); m_ConnType[cli] = ctControl; } static int strcmp_escaped(const char *s1, const char *s2) { if(!strncmp(s1, "file:", 5)) s1 += 5; while(*s1 && *s2) { int c1 = *s1; int c2 = *s2; if(c1 == '%' && s1[1] && s1[2] && 1 == sscanf(s1+1, "%02x", &c1)) s1 += 2; if(c2 == '%' && s2[1] && s2[2] && 1 == sscanf(s2+1, "%02x", &c2)) s2 += 2; if(c1 < c2) return -1; if(c1 > c2) return 1; s1++; s2++; } return *s1 ? -1 : *s2 ? 1 : 0; } void cXinelibServer::Handle_Control_HTTP(int cli, const char *arg) { // Parse request if(m_ConnType[cli] == ctDetecting || !m_State[cli]) { LOGDBG("HTTP request: %s", arg); DELETENULL(m_Writer[cli]); DELETENULL(m_State[cli]); m_State[cli] = new cConnState; if( !m_State[cli]->SetCommand(arg) || strncmp(m_State[cli]->Version(), "HTTP/1.", 7) || strcmp(m_State[cli]->Name(), "GET")) { LOGMSG("invalid HTTP request: %s", arg); CloseConnection(cli); return; } m_ConnType[cli] = ctHttp; return; } // Handle request else if(m_ConnType[cli] == ctHttp) { LOGDBG("HTTP(%d): %s", cli, arg); // Collect headers if(*arg) { m_State[cli]->AddHeader(arg); return; } LOGMSG("HTTP Request complete"); // // primary device output (PES) // if(!strcmp(m_State[cli]->Uri(), "/")) { if(!xc.remote_use_http) { LOGMSG("HTTP transport disabled in configuration"); fd_control[cli].write_cmd(HTTP_REPLY_404); LOGDBG("HTTP Reply: HTTP/1.1 404 Not Found"); CloseConnection(cli); return; } LOGMSG("HTTP streaming primary device feed"); fd_control[cli].write_cmd(HTTP_REPLY_200_PRIMARY); #if 0 // pack header (scr 0, mux rate 0x6270) fd_control[cli].write( "\x00\x00\x01\xba" "\x44\x00\x04\x00" "\x04\x01\x01\x89" "\xc3\xf8", 14); // system header (streams C0, E0, BD, BF) fd_control[cli].write( "\x00\x00\x01\xbb" "\x00\x12" "\x80\xc4\xe1" "\x00\xe1" "\x7f" "\xb9\xe0\xe8" "\xb8\xc0\x20" "\xbd\xe0\x3a" "\xbf\xe0\x02", 24); #endif m_Writer[cli] = new cRawWriter(fd_control[cli].handle(), KILOBYTE(1024)); if (m_Header) m_Writer[cli]->Put(sidVdr, 0, m_Header, m_HeaderLength); DELETENULL(m_State[cli]); return; } // // currently playing media file // else if(!strncmp(m_State[cli]->Uri(), "/PLAYFILE", 9)) { if(!xc.remote_http_files) { LOGMSG("HTTP transport for media files disabled in configuration"); fd_control[cli].write_cmd(HTTP_REPLY_404); LOGDBG("HTTP Reply: HTTP/1.1 404 Not Found"); CloseConnection(cli); return; } if( *m_FileName && m_bPlayingFile) { cString file = m_FileName; const char *pos = strstr(m_FileName, "#subtitle:"); if(pos) file.Truncate(pos - m_FileName); bool Allow = ( !strcmp_escaped(file, m_State[cli]->Uri() + 9) || !strcmp_escaped(xc.audio_vis_image_mrl, m_State[cli]->Uri() + 9) || (pos && !strcmp_escaped(pos + 10, m_State[cli]->Uri() + 9))); if(Allow) { LOGMSG("HTTP streaming media file"); // detach socket new cHttpStreamer(fd_control[cli].handle(true), m_State[cli]->Uri() + 9, m_State[cli]); m_State[cli] = NULL; CloseConnection(cli); return; } LOGDBG("HTTP Unauthorized request: %s", *m_State[cli]->Uri()); } else LOGDBG("No currently playing file"); } // // nothing else will be served ... // LOGMSG("Rejected HTTP request for \'%s\'", *m_State[cli]->Uri()); fd_control[cli].write_cmd(HTTP_REPLY_404); LOGDBG("HTTP Reply: HTTP/1.1 404 Not Found"); CloseConnection(cli); } } #define RTSP_200_OK "RTSP/1.0 200 OK\r\n" \ "CSeq: %d\r\n" #define RTSP_401 "RTSP/1.0 401 Unauthorized\r\n" \ "CSeq: %d\r\n" RTSP_FIN #define RTSP_415 "RTSP/1.0 415 Unsupported media type\r\n" \ "CSeq: %d\r\n" RTSP_FIN #define RTSP_461 "RTSP/1.0 461 Unsupported transport\r\n" \ "CSeq: %d\r\n" RTSP_FIN #define RTSP_501 "RTSP/1.0 501 Not implemented\r\n" \ "CSeq: %d\r\n" RTSP_FIN #define RTSP_FIN "\r\n", CSeq #define RTSPOUT(x...) do { fd_control[cli].printf(x); LOGMSG("RTSP TX:" x); } while(0) //#define RTSPOUT(x...) fd_control[cli].printf_cmd(x) void cXinelibServer::Handle_Control_RTSP(int cli, const char *arg) { // // Minimal RTSP (RFC 2326) server implementation // // // collect request and headers // if(m_ConnType[cli] == ctDetecting || !m_State[cli]) { LOGDBG("RTSP request: %s", arg); DELETENULL(m_State[cli]); m_State[cli] = new cConnState; if( !m_State[cli]->SetCommand(arg) || strcmp(m_State[cli]->Version(), "RTSP/1.0")) { LOGMSG("invalid RTSP request: %s", arg); CloseConnection(cli); return; } m_ConnType[cli] = ctRtsp; return; } // // Process complete request // else if(m_ConnType[cli] == ctRtsp) { LOGDBG("RTSP(%d): %s", cli, arg); if(*arg) { m_State[cli]->AddHeader(arg); return; } cHeader *cseq = m_State[cli]->Header("CSeq"); int CSeq = cseq ? cseq->IntValue() : -1; LOGMSG("RTSP Request complete (cseq %d)", CSeq); if(!xc.remote_use_rtsp) { LOGMSG("RTSP transport disabled in configuration"); RTSPOUT(RTSP_401); LOGDBG("RTSP Reply: HTTP/1.1 404 Not Found"); CloseConnection(cli); return; } // // OPTIONS rtsp://127.0.0.1:37890 RTSP/1.0 // CSeq: 1 // if(!strcmp(m_State[cli]->Name(), "OPTIONS")) { RTSPOUT(RTSP_200_OK "Public: DESCRIBE, SETUP, TEARDOWN, PLAY\r\n" RTSP_FIN); } // OPTIONS // // DESCRIBE rtsp://127.0.0.1:37890 RTSP/1.0 // CSeq: 2 // Accept: application/sdp // else if(!strcmp(m_State[cli]->Name(), "DESCRIBE")) { cHeader *accept = m_State[cli]->Header("Accept"); if(accept && strstr(accept->Value(), "application/sdp")) { struct sockaddr_in sin; socklen_t len = sizeof(sin); char buf[64]; uint32_t payload_type = VDRVERSNUM > 10702 ? SDP_PAYLOAD_MPEG_TS : SDP_PAYLOAD_MPEG_PES; if (fd_control[cli].getsockname((struct sockaddr *)&sin, &len) < 0) { LOGERR("Error getting control socket address"); } const char *sdp_descr = vdr_sdp_description(cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf), 2001, xc.listen_port, xc.remote_rtp_addr, payload_type, /*m_ssrc*/0x4df73452, xc.remote_rtp_port, xc.remote_rtp_ttl); size_t sdplen = sdp_descr ? strlen(sdp_descr) : 0; RTSPOUT(RTSP_200_OK "Content-Type: application/sdp\r\n" "Content-Length: %lu\r\n" "\r\n", CSeq, (unsigned long)sdplen); if (sdplen) { fd_control[cli].write_cmd(sdp_descr, sdplen); } } else { RTSPOUT(RTSP_415 /*UNSUPPORTED_MEDIATYPE*/); } } // DESCRIBE // // SETUP rtsp://127.0.0.1:37890/ RTSP/1.0 // CSeq: 15 // Transport: RTP/AVP;unicast;client_port=37890-37891 // User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10) // else if(!strcmp(m_State[cli]->Name(), "SETUP")) { cHeader *transport = m_State[cli]->Header("Transport"); int urtp=0, mrtp=0, tcp=0; if(transport && ( (strstr(transport->Value(), "RTP/AVP;multicast") && (mrtp=1)) || (strstr(transport->Value(), "RTP/AVP;unicast") && (urtp=1)) || (strstr(transport->Value(), "RTP/AVP;interleaved") && (tcp=1)))) { //if(!mrtp) // sprintf(buf, "RTSP/1.0 461 Unsupported transport\r\n" RTSP_H_CSEQ RTSP_OK_FIN); //else RTSPOUT(RTSP_200_OK "Session: %u\r\n" "Transport: RTP/AVP;multicast;destination=224.8.4.9;server_port=37890-37891\r\n" RTSP_FIN, cli); } else { RTSPOUT(RTSP_461 /*UNSUPPORTED_TRANSPORT*/ ); } } // SETUP // // PLAY rtsp://127.0.0.1:37890 RTSP/1.0 // CSeq: 13 // Session: 0 // Range: npt=0.000- // User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10) // else if(!strcmp(m_State[cli]->Name(), "PLAY")) { RTSPOUT(RTSP_200_OK RTSP_FIN); if(!m_iMulticastMask && !xc.remote_rtp_always_on) m_Scheduler->AddRtp(); m_bMulticast[cli] = true; m_iMulticastMask |= (1<<cli); #if 0 //udp int fd = sock_connect(fd_control[cli], atoi(arg), SOCK_DGRAM); if(fd < 0) { LOGERR("socket() for UDP failed"); write_cmd(fd_control[cli], "UDP: Socked failed.\r\n"); return; } m_bUdp[cli] = true; fd_data[cli] = fd; m_Scheduler->AddHandle(fd); #endif } // PLAY // // TEARDOWN rtsp://127.0.0.1:37890 RTSP/1.0 // CSeq: 39 // Session: 1 // User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10) // else if(!strcmp(m_State[cli]->Name(), "TEARDOWN")) { RTSPOUT(RTSP_200_OK RTSP_FIN); CloseConnection(cli); } // TEARDOWN // // unknown/unsupported request // else { LOGMSG("Unsupported RTSP request: %s", *m_State[cli]->Name()); RTSPOUT(RTSP_501 /*NOT_IMPLEMENTED*/); } // unsupported request // dispose buffer DELETENULL(m_State[cli]); } // ConnectionType == ctRtsp } void cXinelibServer::Handle_Control(int cli, const char *cmd) { TRACEF("cXinelibServer::Handle_Control"); LOGVERBOSE("Server received %s", cmd); /* Order of tests is significant !!! (example: UDP 2\r\n or UDP FULL 1\r\n) */ if(!strncasecmp(cmd, "OPTIONS ", 8) || !strncasecmp(cmd, "SETUP ", 6) || !strncasecmp(cmd, "DESCRIBE ", 9) || m_ConnType[cli] == ctRtsp) { Handle_Control_RTSP(cli, cmd); } else if(!strncasecmp(cmd, "GET ", 4) || m_ConnType[cli] == ctHttp) { Handle_Control_HTTP(cli, cmd); } else if(!strncasecmp(cmd, "PIPE OPEN", 9)) { LOGDBG("Pipe open"); } else if(!strncasecmp(cmd, "PIPE", 4)) { Handle_Control_PIPE(cli, cmd+4); } else if(!strncasecmp(cmd, "RTP", 3)) { Handle_Control_RTP(cli, cmd+4); } else if(!strncasecmp(cmd, "UDP FULL", 8)) { } else if(!strncasecmp(cmd, "UDP RESEND ", 11)) { Handle_Control_UDP_RESEND(cli, cmd+11); } else if(!strncasecmp(cmd, "UDP ", 4)) { Handle_Control_UDP(cli, cmd+4); } else if(!strncasecmp(cmd, "DATA ", 5)) { Handle_Control_DATA(cli, cmd+5); } else if(!strncasecmp(cmd, "KEY ", 4)) { Handle_Control_KEY(cli, cmd+4); } else if(!strncasecmp(cmd, "CONFIG", 6)) { Handle_Control_CONFIG(cli); } else if(!strncasecmp(cmd, "STC ", 4)) { int64_t pts = -1; if(1 == sscanf(cmd+4, "%" PRId64, &pts)) m_StcFuture->Set(pts); } else if(!strncasecmp(cmd, "ENDOFSTREAM", 11)) { m_bEndOfStreamReached = true; } else if(!strncasecmp(cmd, "RESULT ", 7)) { int token = -1, result = -1; if(2 == sscanf(cmd+7, "%d %d", &token, &result)) { cReplyFuture *f = m_Futures->Get(token); if(f) { m_Futures->Del(f, token); f->Set(result); } } } else if(!strncmp(cmd, "INFO ", 5)) { if(!strncmp(cmd, "INFO ARGBOSD", 12)) m_bArgbOSD[cli] = true; if(!*xc.local_frontend || !strncmp(xc.local_frontend, "none", 4)) cXinelibThread::InfoHandler(cmd+5); } else if(!strncasecmp(cmd, "GRAB ", 5)) { Handle_Control_GRAB(cli, cmd+5); } else if(!strncasecmp(cmd, "CLOSE", 5)) { CloseConnection(cli); } else if(!strncasecmp(cmd, "CONTROL", 7)) { Handle_Control_CONTROL(cli, cmd); } } void cXinelibServer::Read_Control(int cli) { int n; while((n = fd_control[cli].recv(&m_CtrlBuf[ cli ][ m_CtrlBufPos[cli] ], 1)) == 1) { ++m_CtrlBufPos[cli]; if( m_CtrlBufPos[cli] > CTRL_BUF_SIZE-2) { LOGMSG("Received too long control message from client %d (%d bytes)", cli, m_CtrlBufPos[cli]); LOGMSG("%81s",m_CtrlBuf[cli]); CloseConnection(cli); return; } if( m_CtrlBufPos[cli] > 1 && m_CtrlBuf[ cli ][ m_CtrlBufPos[cli] - 2 ] == '\r' && m_CtrlBuf[ cli ][ m_CtrlBufPos[cli] - 1 ] == '\n') { m_CtrlBufPos[cli] -= 2; m_CtrlBuf[ cli ][ m_CtrlBufPos[cli] ] = 0; Handle_Control(cli, m_CtrlBuf[cli]); m_CtrlBufPos[cli] = 0; } } if (n == 0) { LOGMSG("Client connection %d closed", cli); CloseConnection(cli); } } void cXinelibServer::Handle_ClientConnected(int fd) { char buf[64]; struct sockaddr_in sin; socklen_t len = sizeof(sin); int cli; for(cli=0; cli<MAXCLIENTS; cli++) if(!fd_control[cli].open()) break; if(getpeername(fd, (struct sockaddr *)&sin, &len)) { LOGERR("getpeername() failed, dropping new incoming connection %d", cli); CLOSESOCKET(fd); return; } LOGMSG("Client %d connected: %s", cli, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf)); cAllowedHosts AllowedHosts(m_AllowedHostsFile); if (!AllowedHosts.Acceptable(sin.sin_addr.s_addr)) { const char *msg = "Access denied.\r\n"; ssize_t len = strlen(msg); LOGMSG("Address not allowed to connect (%s)", *m_AllowedHostsFile); if(write(fd, msg, len) != len) LOGERR("Write failed."); CLOSESOCKET(fd); return; } if(cli>=xc.remote_max_clients || cli>=MAXCLIENTS) { const char *msg = "Server busy.\r\n"; ssize_t len = strlen(msg); // too many clients LOGMSG("Too many clients (%d), connection refused", cli); if(write(fd, msg, len) != len) LOGERR("Write failed."); CLOSESOCKET(fd); return; } if (fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK) == -1) { LOGERR("Error setting control socket to nonblocking mode"); CLOSESOCKET(fd); return; } int alive = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &alive, sizeof(alive)) < 0) { LOGERR("error setting SO_KEEPALIVE"); } CloseDataConnection(cli); m_OsdTimeouts[cli] = 0; m_CtrlBufPos[cli] = 0; m_CtrlBuf[cli][0] = 0; m_ConnType[cli] = ctDetecting; fd_control[cli].set_handle(fd); fd_control[cli].set_buffers(KILOBYTE(128), KILOBYTE(128)); if (!m_Dev->ForcePrimaryDevice(true)) { const char *msg = "Not primary device.\r\n"; ssize_t len = strlen(msg); LOGMSG("Dropping client: xineliboutput is not the primary device !"); if(write(fd, msg, len) != len) LOGERR("Write failed."); CLOSESOCKET(fd); return; } } void cXinelibServer::Handle_Discovery_Broadcast(void) { if(!xc.remote_usebcast) { LOGDBG("BROADCASTS disabled in configuration"); CLOSESOCKET(fd_discovery); return; } char buf[DISCOVERY_MSG_MAXSIZE] = {0}; struct sockaddr_in from; if(udp_discovery_recv(fd_discovery, buf, 0, &from) > 0) if(udp_discovery_is_valid_search(buf)) { // Reply only if we can accept one more client int clients = 0; for(int c=0; c<MAXCLIENTS; c++) if(fd_control[c].open()) clients++; if(clients >= xc.remote_max_clients) { LOGMSG("Not replying to discovery broadcast (too many clients)"); return; } udp_discovery_broadcast(fd_discovery, m_Port, xc.remote_local_ip); } } void cXinelibServer::Action(void) { TRACEF("cXinelibServer::Action"); pollfd pfd[2*MAXCLIENTS + 2]; /* higher priority */ if (have_cap_sys_nice()) SetPriority(-1); sched_param temp; temp.sched_priority = 2; /* request real-time scheduling */ if (!pthread_setschedparam(pthread_self(), SCHED_RR, &temp)) { LOGDBG("cXinelibServer priority set successful SCHED_RR %d [%d,%d]", temp.sched_priority, sched_get_priority_min(SCHED_RR), sched_get_priority_max(SCHED_RR)); } else { LOGDBG("cXinelibServer: Can't set priority to SCHED_RR %d [%d,%d]", temp.sched_priority, sched_get_priority_min(SCHED_RR), sched_get_priority_max(SCHED_RR)); } errno = 0; Lock(); Listen(m_Port); m_bReady=true; if(fd_listen>=0) while (Running()) { int i, fds = 0; if(fd_listen>=0) { pfd[fds].fd = fd_listen; pfd[fds++].events = POLLIN; } if(fd_discovery >= 0) { pfd[fds].fd = fd_discovery; pfd[fds++].events = POLLIN; } for(i=0; i<MAXCLIENTS; i++) { if(fd_control[i].open()) { pfd[fds].fd = fd_control[i].handle(); pfd[fds++].events = POLLIN; } if(fd_data[i]>=0) { pfd[fds].fd = fd_data[i]; pfd[fds++].events = 0; /* check for errors only */ } } Unlock(); int err = poll(pfd,fds,1000); if(err < 0) { LOGERR("cXinelibServer: poll failed"); if(Running()) cCondWait::SleepMs(100); } else if(err == 0) { // poll timeout } else { Lock(); for(int f=0; f<fds; f++) { // Check errors (closed connections etc.) if(pfd[f].revents & (POLLERR|POLLHUP|POLLNVAL)) { if(pfd[f].fd == fd_listen) { LOGERR("cXinelibServer: listen socket error"); CLOSESOCKET(fd_listen); cCondWait::SleepMs(100); Listen(m_Port); } /* fd_listen */ else if(pfd[f].fd == fd_discovery) { LOGERR("cXinelibServer: discovery socket error"); CLOSESOCKET(fd_discovery); } /* fd_discovery */ else /* fd_data[] / fd_control[] */ { for(i=0; i<MAXCLIENTS; i++) { if(pfd[f].fd == fd_data[i] || pfd[f].fd == fd_control[i].handle()) { LOGMSG("Client %d disconnected", i); CloseConnection(i); } } } /* fd_data / fd_control */ } /* Check ERRORS */ // Check ready for reading else if(pfd[f].revents & POLLIN) { // New connection if(pfd[f].fd == fd_listen) { int fd = accept(fd_listen, 0, 0); if(fd>=0) Handle_ClientConnected(fd); } /* fd_listen */ // VDR Discovery else if(pfd[f].fd == fd_discovery) { Handle_Discovery_Broadcast(); } /* fd_discovery */ // Control data else { for(i=0; i<MAXCLIENTS; i++) { if(pfd[f].fd == fd_control[i].handle()) { Read_Control(i); break; } } } /* fd_control */ } /* Check ready for reading */ } /* for(fds) */ Unlock(); } /* Check poll result */ Lock(); } /* while running */ m_bReady = false; Unlock(); } ��������������������������������������������xineliboutput-2.0.0/frontend_local.h����������������������������������������������������������������0000644�0001750�0001750�00000003054�13061253352�015370� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * frontend_local.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_FRONTEND_LOCAL_H #define __XINELIB_FRONTEND_LOCAL_H #include "frontend.h" //----------------------------- cXinelibLocal -------------------------------- extern "C" { typedef struct frontend_s frontend_t; } class cXinelibDevice; class cXinelibLocal : public cXinelibThread { public: cXinelibLocal(cXinelibDevice *Dev, const char *frontend_name); virtual ~cXinelibLocal(); protected: virtual void Action(void); public: // Data transfer virtual int Play(const uchar *buf, int len, eStreamId StreamId = sidVdr); virtual void OsdCmd(void *cmd); virtual int64_t GetSTC(); // Playback files virtual bool EndOfStreamReached(void); // Image grabbing virtual uchar *GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY); // Configuration virtual void ConfigureWindow(int fullscreen, int width, int height, int modeswitch, const char *modeline, int aspect, int scale_video); virtual void ConfigureDecoder(int pes_buffers); virtual int SupportsTrueColorOSD(void); protected: // Playback control virtual int Xine_Control(const char *cmd); protected: // Frontend access frontend_t *load_frontend(const char *fe_name); // Data void *h_fe_lib; frontend_t *fe; cRwLock m_feLock; bool m_bReconfigRequest; }; #endif // __XINELIB_FRONTEND_LOCAL_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/frontend_local.c����������������������������������������������������������������0000644�0001750�0001750�00000025514�13061253352�015370� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * frontend_local.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_CONSTANT_MACROS #include <stdlib.h> #include <string.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <dlfcn.h> #include <vdr/config.h> #include <vdr/tools.h> #include <vdr/shutdown.h> #include <vdr/plugin.h> #include <vdr/device.h> #include "logdefs.h" #include "config.h" #include "xine_frontend.h" #include "frontend_local.h" //------------------------------ cRwLockBlock --------------------------------- class cRwLockBlock { private: cRwLock& m_Lock; public: cRwLockBlock(cRwLock& lock, bool write) : m_Lock(lock) { m_Lock.Lock(write); } ~cRwLockBlock() { m_Lock.Unlock(); } }; #define LOCK_FE cRwLockBlock(m_feLock, false) #define LOCK_FE_WR cRwLockBlock(m_feLock, true) //----------------- keyboard control handler (C callback) -------------------- extern "C" { static void keypress_handler(void *handle, const char *keymap, const char *key) { if (!handle) { LOGMSG("keypress_handler(): missing handle"); return; } cXinelibThread *t = (cXinelibThread *)handle; if (!strncmp("INFO ", keymap, 5)) { t->InfoHandler(keymap+5); } else if (!xc.use_x_keyboard || !key) { /* Only X11 key events came this way in local mode. Keyboard is handled by vdr. */ LOGMSG("keypress_handler(%s): X11 Keyboard disabled in config", key); } else { t->KeypressHandler(keymap, key, false, false); } } }; //----------------------------- cXinelibLocal -------------------------------- cXinelibLocal::cXinelibLocal(cXinelibDevice *Dev, const char *frontend_name) : cXinelibThread(Dev, "Local decoder/display (cXinelibThread)"), m_feLock(true) { fe = NULL; h_fe_lib = NULL; m_bReconfigRequest = true; if (!xc.config_file && 0 < asprintf(&xc.config_file, "%s/xineliboutput/config", cPlugin::ConfigDirectory())) LOGMSG("cXinelibLocal: Using xine-lib configuration file %s", xc.config_file); } cXinelibLocal::~cXinelibLocal() { TRACEF("cXinelibLocal::~cXinelibLocal"); m_bReady = false; Cancel(-1); { LOCK_FE; m_bReady = false; if(fe) fe->fe_interrupt(fe); } Cancel(3); if (fe) { fe->fe_free(fe); fe = NULL; } if (h_fe_lib) { dlclose(h_fe_lib); } } // // Data transfer // int cXinelibLocal::Play(const uchar *data, int len, eStreamId StreamId) { TRACEF("cXinelibLocal::Play"); { LOCK_FE; if (fe && Running()) { int done = fe->xine_queue_pes_packet(fe, StreamId, m_StreamPos, (const char *)data, len); if (done >= 0) return cXinelibThread::Play(data, done, StreamId); } } //cCondWait::SleepMs(5); return len; } void cXinelibLocal::OsdCmd(void *cmd) { TRACEF("cXinelibLocal::OsdCmd"); LOCK_FE; if(cmd && fe && m_bReady) fe->xine_osd_command(fe, (struct osd_command_s*)cmd); } uchar *cXinelibLocal::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { uchar *data; LOCK_FE; if(fe && fe->grab && m_bReady) if((data = (uchar*)fe->grab(fe, &Size, Jpeg, Quality, SizeX, SizeY))) return data; return NULL; } int64_t cXinelibLocal::GetSTC() { TRACEF("cXinelibLocal::GetSTC"); union { char buf[32]; int64_t pts; } u = {"GETSTC\r\n"}; LOCK_FE; if (fe && m_bReady) if (0 == fe->xine_control(fe, u.buf)) return u.pts; return INT64_C(-1); } // // Playback files // bool cXinelibLocal::EndOfStreamReached(void) { LOCK_THREAD; if(fe && fe->xine_is_finished(fe, 1)) return true; return cXinelibThread::EndOfStreamReached(); } // // Configuration // void cXinelibLocal::ConfigureWindow(int fullscreen, int width, int height, int modeswitch, const char *modeline, int aspect, int scale_video) { LOCK_FE; if(fe) fe->fe_display_config(fe, -1, -1, width, height, fullscreen, modeswitch, modeline, aspect, scale_video); } void cXinelibLocal::ConfigureDecoder(int pes_buffers) { // needs xine restart { LOCK_FE; xc.pes_buffers = pes_buffers; if(!fe) return; m_bReady = false; m_bReconfigRequest = true; fe->fe_interrupt(fe); } while (!m_bReady && Running()) cCondWait::SleepMs(100); cCondWait::SleepMs(100); } int cXinelibLocal::SupportsTrueColorOSD(void) { return !!xc.hud_osd; } // // Xine control // int cXinelibLocal::Xine_Control(const char *cmd) { TRACEF("cXinelibLocal::Xine_Control"); if (cmd && *cmd && Running()) { char buf[4096]; if(snprintf(buf, sizeof(buf), "%s\r\n", cmd) >= (int)sizeof(buf)) { buf[sizeof(buf)-1] = 0; LOGMSG("Xine_Control: message too long ! (%s)", buf); return 0; } LOCK_FE; if(fe) return fe->xine_control(fe, (char*)buf); } return 0; } // // Frontend loader // frontend_t *cXinelibLocal::load_frontend(const char *fe_name) { Dl_info info; struct stat statbuffer; char libname[4096]=""; void *lib = NULL; fe_creator_f *fe_creator = NULL; static int my_marker = 0; if (!dladdr((void *)&my_marker, &info)) { LOGERR("Error searching plugin: dladdr() returned false (%s)", dlerror()); return NULL; } LOGDBG("xineliboutput: plugin file is %s", info.dli_fname); int fe_ind = strstra(fe_name, xc.s_frontends, FRONTEND_NONE); bool fe_try = false; if (fe_ind == FRONTEND_NONE) { LOGMSG("Front-end %s unknown!", fe_name); fe_ind = 0; fe_try = true; } strn0cpy(libname, info.dli_fname, sizeof(libname) - 128); if (strrchr(libname, '/')) *(strrchr(libname, '/')+1) = 0; LOGDBG("Searching frontend %s from %s", xc.s_frontends[fe_ind], libname); do { strncat(libname, xc.s_frontend_files[fe_ind], 64); LOGDBG("Probing %s", libname); if (stat(libname, &statbuffer)) { LOGERR("load_frontend: can't stat %s", libname); } else if((statbuffer.st_mode & S_IFMT) != S_IFREG) { LOGMSG("load_frontend: %s not regular file ! trying to load anyway ...", libname); } if ( !(lib = dlopen (libname, RTLD_LAZY | RTLD_GLOBAL))) { LOGERR("load_frontend: cannot dlopen file %s: %s", libname, dlerror()); } else if ( (fe_creator = (fe_creator_f*)dlsym(lib, "fe_creator"))) { LOGDBG("load_frontend: entry at %p", fe_creator); frontend_t *fe = (**fe_creator)(); if (fe) { if (h_fe_lib) dlclose(h_fe_lib); h_fe_lib = lib; LOGDBG("Using frontend %s (%s) from %s", xc.s_frontends[fe_ind], xc.s_frontendNames[fe_ind], xc.s_frontend_files[fe_ind]); return fe; } else { LOGMSG("Frontend %s (%s) creation failed", xc.s_frontends[fe_ind], xc.s_frontendNames[fe_ind]); } dlclose(lib); } else { LOGERR("Frontend entry point not found"); dlclose(lib); } fe_ind++; // try next frontend ... } while (fe_try && fe_ind < FRONTEND_count); LOGMSG("No usable frontends found, giving up !"); return NULL; } // // Thread main loop // void cXinelibLocal::Action(void) { frontend_t *curr_fe = NULL; TRACEF("cXinelibLocal::Action"); SetPriority(2); /* lower priority */ // init frontend curr_fe = load_frontend(xc.local_frontend); if(!curr_fe) { LOGMSG("cXinelibLocal: Error initializing frontend"); Cancel(-1); return; } LOGDBG("cXinelibLocal::Action - fe created"); curr_fe->fe_message_cb = keypress_handler; curr_fe->fe_message_h = this; if(!curr_fe->fe_display_open(curr_fe, xc.xpos, xc.ypos, xc.width, xc.height, xc.fullscreen, xc.hud_osd, xc.opengl, xc.modeswitch, xc.modeline, xc.display_aspect, 0/*no_x_kbd*/, 0/*gui_hotkeys*/, 0/*touchscreen*/, xc.video_port, xc.scale_video, NULL, xc.window_id)) { LOGMSG("cXinelibLocal: Error initializing display"); Cancel(-1); } else { LOGDBG("cXinelibLocal::Action - fe->fe_display_open ok"); } // main loop while (Running()) { { // init and start xine engine LOCK_FE_WR; LOGDBG("cXinelibLocal::Action - xine_init"); fe = curr_fe; if(m_bReconfigRequest) { if(!fe->xine_init(fe, xc.audio_driver, xc.audio_port, xc.video_driver, xc.pes_buffers, xc.post_plugins, xc.config_file)) { LOGMSG("cXinelibLocal: Error initializing frontend"); break; } LOGDBG("cXinelibLocal::Action - fe->xine_init ok"); m_bReconfigRequest = false; } // open (xine) stream LOGDBG("cXinelibLocal::Action - xine_open"); if(!fe->xine_open(fe, NULL)) { LOGMSG("cXinelibLocal: Error opening xvdr://"); break; } LOGDBG("cXinelibLocal::Action - fe->xine_open ok"); // start playing (xine) stream if(!fe->xine_play(fe)) { LOGMSG("cXinelibLocal: Error playing xvdr://"); break; } LOGDBG("cXinelibLocal::Action - fe->xine_play ok"); m_StreamPos = 0; Xine_Control("STREAMPOS 0"); Xine_Control("VERSION " XINELIBOUTPUT_VERSION " " "\r\n"); } // configure frontend and xine m_bNoVideo = false; Configure(); LOGDBG("cXinelibLocal::Action - fe config OK"); LogoDisplay(); LOGDBG("cXinelibLocal::Action - logo sent"); { LOCK_THREAD; Xine_Control("NOVIDEO 0"); Xine_Control("LIVE 1"); Xine_Control("CLEAR"); m_bNoVideo = false; m_bLiveMode = true; m_bReady = true; } // main event loop LOGDBG("cXinelibLocal:Action - Starting event loop"); { LOCK_FE; while (Running() && m_bReady && (/*m_bLoopPlay ||*/ !fe->xine_is_finished(fe, 0)) && fe->fe_run(fe)) /*cCondWait::SleepMs(50)*/ ; } LOGDBG("cXinelibLocal::Action - event loop terminated, " "xine_is_finished=%d", fe->xine_is_finished(fe, 0)); { LOCK_THREAD; m_bReady = false; m_bEndOfStreamReached = true; } { LOCK_FE_WR; if(fe) fe->xine_close(fe); fe = NULL; } LOGMSG("cXinelibLocal::Action: Xine closed"); if(!m_bReconfigRequest && xc.exit_on_close) { LOGMSG("Shutting down VDR"); ShutdownHandler.RequestEmergencyExit(); break; } } if(curr_fe) { LOCK_FE_WR; fe = NULL; curr_fe->xine_exit(curr_fe); curr_fe->fe_display_close(curr_fe); curr_fe->fe_free(curr_fe); } LOGMSG("cXinelibLocal::Action: thread finished"); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/frontend.h����������������������������������������������������������������������0000644�0001750�0001750�00000011121�13061253352�014210� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * frontend.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_FRONTEND_H #define __XINELIB_FRONTEND_H #include <vdr/tools.h> #include <vdr/thread.h> #include "xine_input_vdr_net.h" // eStreamId class cStatus; class cXinelibDevice; enum ePlayMode; //----------------------------- cXinelibThread -------------------------------- class cXinelibThread : public cThread, public cListObject { private: cXinelibThread(cXinelibThread&); // no copy contructor public: cXinelibThread(cXinelibDevice *Dev, const char *Description = NULL); virtual ~cXinelibThread(); // // Thread control // public: bool IsReady(void); // // Playback control // public: void PauseOutput(void) { TrickSpeed(0); } void ResumeOutput(void) { TrickSpeed(1); } void TrickSpeed(int Speed) { TrickSpeed(Speed, false); } void SetVolume(int NewVolume); void SetLiveMode(bool); void SetStillMode(bool); void SetNoVideo(bool bVal); void AudioStreamChanged(bool ac3, int StreamId); void SetSubtitleTrack(eTrackType Track); virtual void TrickSpeed(int Speed, bool Backwards); // Sync(): wait until all pending control messages have been processed by the client virtual void Sync(void) { Xine_Control("SYNC"); }; void Pip_Config(int Index, int X = -1, int Y = -1, int W = -1, int H = -1); void Pip_Close(int Index); protected: int Xine_Control(const char *cmd, const char *p1); int Xine_Control(const char *cmd, int p1); int Xine_Control(const char *cmd, int64_t p1); virtual int Xine_Control(const char *cmd) = 0; virtual int Xine_Control_Sync(const char *cmd) { return Xine_Control(cmd); } void Configure(void); // // Data transfer // public: virtual int Poll(cPoller &Poller, int TimeoutMs); virtual bool Flush(int TimeoutMs); virtual void Clear(void); virtual int Play(const uchar *buf, int len, eStreamId StreamId = sidVdr); virtual void OsdCmd(void *cmd) = 0; virtual int64_t GetSTC(void) { return -1; } virtual void SetHDMode(bool On) { (void)Xine_Control("HDMODE", m_bHDMode = On); }; virtual void SetHeader(const uchar *data, int length, bool reset = false) {}; // Stream type conversions int Play_Mpeg1_PES(const uchar *data, int len); bool Play_Mpeg2_ES(const uchar *data, int len, int streamID, bool h264 = false); // Built-in still images bool BlankDisplay(void); bool QueueBlankDisplay(void); bool LogoDisplay(void); bool NoSignalDisplay(void); // Playback files virtual bool PlayFile(const char *FileName, int Position = 0, bool LoopPlay = false, ePlayMode PlayMode = pmAudioVideo, int TimeoutMs = -1); virtual int PlayFileCtrl(const char *Cmd, int TimeoutMs=-1) { return Xine_Control(Cmd); } virtual bool EndOfStreamReached(void); // Image grabbing virtual uchar *GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { return NULL; } // Control from frontend void KeypressHandler(const char *keymap, const char *key, bool repeat, bool release); void InfoHandler(const char *info); // // Configuration // public: virtual int ConfigurePostprocessing(const char *deinterlace_method, int audio_delay, int audio_compression, const int *audio_equalizer, int audio_surround, int speaker_type); virtual int ConfigurePostprocessing(const char *name, bool on, const char *args); virtual int ConfigureVideo(int hue, int saturation, int brightness, int sharpness, int noise_reduction, int contrast, int overscan, int vo_aspect_ratio); // Local frontend: virtual void ConfigureWindow(int fullscreen, int width, int height, int modeswitch, const char *modeline, int aspect, int scale_video) {}; virtual void ConfigureDecoder(int pes_buffers) {}; // Remote frontend server: virtual bool Listen(int port) { return false; } virtual int SupportsTrueColorOSD(void) { return 0; } /* 0: no, 1: yes, -1: unknown */ // // Data // protected: cXinelibDevice *m_Dev; bool m_bReady; bool m_bNoVideo; bool m_bLiveMode; bool m_bHDMode; int m_TrickSpeed; bool m_bTrickSpeedBack; bool m_bEndOfStreamReached; bool m_bPlayingFile; int m_Volume; cString m_FileName; uint64_t m_StreamPos; uint64_t m_LastClearPos; uint32_t m_Frames; cStatus *m_StatusMonitor; bool m_SpuLangAuto; }; #endif // __XINELIB_FRONTEND_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/frontend.c����������������������������������������������������������������������0000644�0001750�0001750�00000056204�13061253352�014216� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * frontend.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_FORMAT_MACROS #define __STDC_CONSTANT_MACROS #include <inttypes.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <time.h> #include <pthread.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netdb.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <vdr/config.h> #include <vdr/tools.h> #include <vdr/plugin.h> #include <vdr/device.h> #include "logdefs.h" #include "config.h" #include "frontend.h" #include "device.h" #include "tools/pes.h" #include "tools/mpeg.h" #include "tools/h264.h" #include "tools/general_remote.h" #include "tools/iso639.h" #ifndef STARTUP_IMAGE_FILE # define STARTUP_IMAGE_FILE "/usr/share/vdr/xineliboutput/logo.mpv" #endif #ifndef STARTUP_MAX_SIZE # define STARTUP_MAX_SIZE (256*1024) #endif //----------------------------- cXinelibThread -------------------------------- // // keyboard control handler // /*static*/ void cXinelibThread::KeypressHandler(const char *keymap, const char *key, bool repeat, bool release) { TRACE("keypress_handler: " << (keymap?keymap:"") << " " << key); // check if key exists. // Note: empty key ("") is used to trigger learning; it only creates the cRemote object. if (!key) return; if (!keymap) { // raw VDR key cRemote::Put(cKey::FromString(key)); return; } // find correct remote cGeneralRemote *remote = NULL; for (cRemote *item = Remotes.First(); item; item = Remotes.Next(item)) { if (!strcmp(item->Name(), keymap)) { // dirty... but using protected cRemote::Put() is the only way to support learning ... remote = (cGeneralRemote*)item; break; } } // not found ? create new one if (!remote) remote = new cGeneralRemote(keymap); // put key to remote queue if (key[0]) { if (!remote->Put(key, repeat, release)) { if (!strcmp(keymap, "KBD")) { uint64_t value = 0; sscanf(key, "%" PRIX64, &value); if (value) { remote->cRemote::Put(KBDKEY(value)); return; } } if (!key[1]) { remote->cRemote::Put(KBDKEY(key[0])); } } } } #include <vdr/status.h> class cFrontendStatusMonitor : public cStatus { private: bool& m_SpuLangAuto; public: cFrontendStatusMonitor(bool& SpuLangAuto) : m_SpuLangAuto(SpuLangAuto) {}; virtual void SetSubtitleTrack(int /*Index*/, const char * const */*Tracks*/) { m_SpuLangAuto = false; } }; void cXinelibThread::InfoHandler(const char *info) { char *pmap = strdup(info), *map = pmap, *pt; if(NULL != (pt=strchr(map, '\r'))) *pt = 0; if(!strncmp(info, "TRACKMAP SPU", 12)) { int CurrentTrack = ttXSubtitleAuto; map += 12; while(*map) { bool Current = false; while(*map == ' ') map++; if(*map == '*') { Current = true; map++; if (*map == '-') { CurrentTrack = atoi(map); while (*map && *map != ' ') map++; continue; } } if(*map >= '0' && *map <= '9') { int id = atoi(map); while(*map && *map != ':') map++; if(*map == ':') map++; char *lang = map; while(*map && *map != ' ') map++; if(*map == ' ') { *map = 0; map++; }; m_Dev->SetAvailableTrack(ttSubtitle, id, id+1, iso639_1_to_iso639_2(lang) ?: *cString::sprintf("%03d", id+1)); if (Current) CurrentTrack = id; } } if (CurrentTrack == ttXSubtitleAuto) m_Dev->EnsureSubtitleTrack(); else if (CurrentTrack == ttXSubtitleNone) m_Dev->SetCurrentSubtitleTrack(ttNone, true); else m_Dev->SetCurrentSubtitleTrack(eTrackType(CurrentTrack+ttSubtitleFirst), true); } else if(!strncmp(info, "TRACKMAP AUDIO", 14)) { map += 14; m_Dev->ClrAvailableTracks(); while(*map) { bool Current = false; while(*map == ' ') map++; if(*map == '*') { Current = true; map++; } int id = atoi(map); while(*map && *map != ':') map++; if(*map == ':') map++; char *lang = map; while(*map && *map != ' ') map++; if(*map == ' ') { *map = 0; map++; }; m_Dev->SetAvailableTrack(ttDolby, id, ttDolby+id, iso639_1_to_iso639_2(lang) ?: *cString::sprintf("%03d", id+1)); if(Current) m_Dev->SetCurrentAudioTrack((eTrackType)(ttDolby+id)); } } else if(!strncmp(info, "METAINFO", 8)) { map += 8; while(*map) { while(*map == ' ') map++; char *next = strstr(map, "=@"); if(!next) break; *next = 0; next += 2; char *end = strstr(next, "@"); if(!end) break; *end = 0; if(!strcmp(map, "title")) m_Dev->SetMetaInfo(miTitle, next); if(!strcmp(map, "tracknumber")) m_Dev->SetMetaInfo(miTracknumber, next); if(!strcmp(map, "album")) m_Dev->SetMetaInfo(miAlbum, next); if(!strcmp(map, "artist")) m_Dev->SetMetaInfo(miArtist, next); map = end+1; } } else if(!strncmp(info, "DVDBUTTONS ", 11)) { map += 11; while(*map == ' ') map++; m_Dev->SetMetaInfo(miDvdButtons, map); } else if(!strncmp(info, "TITLE ", 6)) { map += 6; while(*map == ' ') map++; m_Dev->SetMetaInfo(miTitle, map); } else if(!strncmp(info, "DVDTITLE ", 9)) { map += 9; while(*map == ' ') map++; m_Dev->SetMetaInfo(miDvdTitleNo, map); if (*map == '0') // DVD Menu, set spu track to 0 m_Dev->SetCurrentSubtitleTrack(ttSubtitleFirst); } else if (!strncmp(info, "WINDOW ", 7)) { int w, h; map += 7; while(*map == ' ') map++; if (2 == sscanf(map, "%dx%d", &w, &h)) { xc.osd_width_auto = w; xc.osd_height_auto = h; } } free(pmap); } cXinelibThread::cXinelibThread(cXinelibDevice *Dev, const char *Description) : cThread(Description) { TRACEF("cXinelibThread::cXinelibThread"); m_Dev = Dev; m_Volume = 255; m_bReady = false; m_bNoVideo = true; m_bHDMode = false; m_bLiveMode = true; /* can't be replaying when there is no output device */ m_StreamPos = 0; m_LastClearPos = 0; m_Frames = 0; m_TrickSpeed = -1; m_bTrickSpeedBack = false; m_bEndOfStreamReached = false; m_bPlayingFile = false; m_StatusMonitor = NULL; m_SpuLangAuto = true; } cXinelibThread::~cXinelibThread() { TRACEF("cXinelibThread::~cXinelibThread"); Cancel(3); if (m_StatusMonitor) DELETENULL(m_StatusMonitor); } // // Thread control // bool cXinelibThread::IsReady(void) { LOCK_THREAD; return m_bReady; } // // Playback control // void cXinelibThread::SetVolume(int NewVolume) { m_Volume = NewVolume; cString str = cString::sprintf("VOLUME %d%s", NewVolume * 100 / 255, xc.sw_volume_control ? " SW" : ""); Xine_Control(str); } void cXinelibThread::TrickSpeed(int Speed, bool Backwards) { TRACEF("cXinelibThread::TrickSpeed"); Lock(); m_TrickSpeed = Speed; m_bTrickSpeedBack = Backwards; Unlock(); cString str = cString::sprintf("TRICKSPEED %d%s", Speed, Backwards ? " Backwards" : ""); Xine_Control(str); } void cXinelibThread::SetLiveMode(bool LiveModeOn) { TRACEF("cXinelibThread::SetLiveMode"); Lock(); if(m_bLiveMode == LiveModeOn) { Unlock(); return; } m_bLiveMode = LiveModeOn; Unlock(); Xine_Control("LIVE", m_bLiveMode ? 1 : 0); } void cXinelibThread::SetStillMode(bool StillModeOn) { TRACEF("cXinelibThread::SetStillMode"); Xine_Control("STILL", StillModeOn ? 1 : 0); } void cXinelibThread::SetNoVideo(bool bVal) { TRACEF("cXinelibThread::SetNoVideo"); Lock(); if(m_bNoVideo == bVal) { Unlock(); return; } m_bNoVideo = bVal; Unlock(); Xine_Control("NOVIDEO", m_bNoVideo ? 1 : 0); if(m_bNoVideo && strcmp(xc.audio_visualization, "none") && strcmp(xc.audio_visualization, "image")) { char *opts = NULL; if(xc.audio_vis_goom_opts[0] && !strcmp(xc.audio_visualization, "goom")) opts = xc.audio_vis_goom_opts; ConfigurePostprocessing(xc.audio_visualization, true, opts); } else { ConfigurePostprocessing("AudioVisualization", false, NULL); } } void cXinelibThread::AudioStreamChanged(bool ac3, int StreamId) { TRACEF("cXinelibThread::AudioStreamChanged"); if(ac3) Xine_Control("AUDIOSTREAM AC3", StreamId); else Xine_Control("AUDIOSTREAM", StreamId); } void cXinelibThread::SetSubtitleTrack(eTrackType Track) { TRACEF("cXinelibThread::SetSubtitleTrack"); cString buf = cString::sprintf("SPUSTREAM %d%s", Track==ttNone ? ttXSubtitleNone : (Track - ttSubtitleFirst), m_SpuLangAuto ? " auto" : ""); Xine_Control(buf); } void cXinelibThread::Pip_Config(int Index, int X, int Y, int W, int H) { Xine_Control(cString::sprintf("PIP %d %d %d %d %d", Index, X, Y, W, H)); } void cXinelibThread::Pip_Close(int Index) { Xine_Control(cString::sprintf("PIP %d Close", Index)); } void cXinelibThread::Clear(void) { TRACEF("cXinelibThread::Clear"); char buf[128]; { LOCK_THREAD; if (m_StreamPos == m_LastClearPos) { //LOGDBG("cXinelibThread::Clear(): double Clear() ignored"); return; } m_LastClearPos = m_StreamPos; snprintf(buf, sizeof(buf), "DISCARD %" PRId64 " %d", m_StreamPos, m_Frames); } /* Send to control stream and data stream. If message is sent only to * control stream, and it is delayed, engine flush will be skipped. */ Xine_Control(buf); Xine_Control_Sync(buf); } bool cXinelibThread::Flush(int TimeoutMs) { TRACEF("cXinelibThread::Flush"); return Xine_Control("FLUSH", TimeoutMs) <= 0; } int cXinelibThread::Poll(cPoller& Poller, int TimeoutMs) { TRACEF("cXinelibThread::Poll"); if(!m_bReady) { if(TimeoutMs>0) cCondWait::SleepMs(TimeoutMs); if(!m_bReady) return 0; } int n = Xine_Control("POLL", TimeoutMs); return max(n, 0); } // // Data transfer // int cXinelibThread::Play(const uchar *data, int len, eStreamId StreamId) { if (StreamId == sidVdr && len >= 0) { Lock(); m_StreamPos += len; m_Frames++; Unlock(); } return len; } // // Stream conversions // // Convert MPEG1 PES headers to MPEG2 PES headers int cXinelibThread::Play_Mpeg1_PES(const uchar *data1, int len) { if(!data1[0] && !data1[1] && data1[2] == 0x01 && len>7 && /* header sync bytes */ ( IS_VIDEO_PACKET(data1) || IS_AUDIO_PACKET(data1)) && /* video / audio / ps1 stream */ ((data1[6] & 0xC0) != 0x80) && /* really mpeg1 pes */ (len == ((data1[4]<<8) | data1[5]) + 6)) { /* whole PES packet and nothing else */ uchar *data2 = new uchar[len+64]; int i1=0, i2=0, r=0; data2[i2++]=data1[i1++]; // 00 (sync) data2[i2++]=data1[i1++]; // 00 (sync) data2[i2++]=data1[i1++]; // 01 (sync) data2[i2++]=data1[i1++]; // stream ID data2[i2++]=data1[i1++]; // len hi data2[i2++]=data1[i1++]; // len lo // skip stuffing while ((data1[i1] & 0x80) == 0x80) i1++; if ((data1[i1] & 0xc0) == 0x40) { // skip STD_buffer_scale, STD_buffer_size i1 += 2; } if (len < i1 + 5) { delete[] data2; return len; } data2[i2++] = 0x80; if ((data1[i1] & 0xf0) == 0x20) { /* PTS */ data2[i2++] = 0x80; data2[i2++] = 5; data2[i2++] = data1[i1++] & 0x0E; data2[i2++] = data1[i1++] & 0xFF; data2[i2++] = data1[i1++] & 0xFE; data2[i2++] = data1[i1++] & 0xFF; data2[i2++] = data1[i1++] & 0xFE; } else if ((data1[i1] & 0xf0) == 0x30) { /* PTS & DTS */ data2[i2++] = 0x80|0x40; data2[i2++] = 10; data2[i2++] = data1[i1++] & 0x0E; data2[i2++] = data1[i1++] & 0xFF; data2[i2++] = data1[i1++] & 0xFE; data2[i2++] = data1[i1++] & 0xFF; data2[i2++] = data1[i1++] & 0xFE; data2[i2++] = data1[i1++] & 0x0E; data2[i2++] = data1[i1++] & 0xFF; data2[i2++] = data1[i1++] & 0xFE; data2[i2++] = data1[i1++] & 0xFF; data2[i2++] = data1[i1++] & 0xFE; } else { i1++; data2[i2++] = 0; /* no pts, no dts */ data2[i2++] = 0; /* header len */ } int newlen = ((data1[4]<<8) | data1[5]) + (i2-i1), loops=0; data2[4] = ((newlen)&0xff00)>>8; data2[5] = ((newlen)&0xff); if(len-i1 > 0) { memcpy(data2+i2, data1+i1, len-i1); cPoller p; while(!Poll(p,100) && loops++ < 10) { LOGDBG("Play_Mpeg1_PES: poll failed"); } r = Play(data2, newlen + 6); } delete [] data2; return r==newlen+6 ? ((data1[4]<<8)|data1[5])+6 : 0; } return len; // nothing useful found ... } // Pack elementary MPEG stream to PES bool cXinelibThread::Play_Mpeg2_ES(const uchar *data, int len, int streamID, bool h264) { uchar hdr_vid[] = {0x00,0x00,0x01,0xe0, 0x00,0x00,0x80,0x00,0x00}; /* mpeg2 */ uchar hdr_pts[] = {0x00,0x00,0x01,0xe0, 0x00,0x08,0x80,0x80, 0x05,0x00,0x00,0x00, 0x00,0x00}; /* mpeg2 */ uchar seq_end[] = {0x00,0x00,0x01,0xe0, 0x00,0x07,0x80,0x00, 0x00, 0x00,0x00,0x01,SC_SEQUENCE_END}; /* mpeg2 */ int todo = len, done = 0, hdrlen = 9/*sizeof(hdr)*/; uchar *frame = new uchar[PES_CHUNK_SIZE+32]; cPoller p; hdr_pts[3] = (uchar)streamID; Poll(p, 100); Play(hdr_pts, sizeof(hdr_pts)); hdr_vid[3] = (uchar)streamID; while(todo) { int blocklen = todo; if(blocklen > ((PES_CHUNK_SIZE - hdrlen) & 0xfffc)) blocklen = (PES_CHUNK_SIZE - hdrlen) & 0xfffc; hdr_vid[4] = ((blocklen+3)&0xff00)>>8; hdr_vid[5] = ((blocklen+3)&0xff); memcpy(frame, hdr_vid, hdrlen); memcpy(frame+hdrlen, data+done, blocklen); done += blocklen; todo -= blocklen; Poll(p, 100); if (blocklen + hdrlen != Play(frame, blocklen + hdrlen)) { delete [] frame; return false; } } // append sequence end code to video if((streamID & 0xF0) == 0xE0) { seq_end[3] = (uchar)streamID; seq_end[12] = h264 ? NAL_END_SEQ : SC_SEQUENCE_END; Poll(p, 100); Play(seq_end, sizeof(seq_end)); } delete[] frame; return true; } // // Built-in still images // bool cXinelibThread::QueueBlankDisplay(void) { TRACEF("cXinelibThread::BlankDisplay"); Xine_Control_Sync("BLANK"); return true; } bool cXinelibThread::BlankDisplay(void) { TRACEF("cXinelibThread::BlankDisplay"); bool r = QueueBlankDisplay(); for(int i=0; i<5 && !Flush(100); i++) ; return r; } bool cXinelibThread::LogoDisplay(void) { TRACEF("cXinelibThread::LogoDisplay"); cString Path; int fd = -1; if(Setup.FileName()) { cString SetupPath = Setup.FileName(); const char *end = strrchr(SetupPath, '/'); if(end) { SetupPath.Truncate(end - (const char *)SetupPath); fd = open(Path=cString::sprintf("%s/plugins/xineliboutput/logo.mpv", *SetupPath), O_RDONLY); } } if(fd<0) fd = open(Path=STARTUP_IMAGE_FILE, O_RDONLY); if(fd >= 0) { uint8_t *data = (uint8_t*)malloc(STARTUP_MAX_SIZE); int datalen = read(fd, data, STARTUP_MAX_SIZE); close(fd); if(datalen == STARTUP_MAX_SIZE) { LOGMSG("WARNING: custom startup image %s too large", *Path); } else if(datalen<=0) { LOGERR("error reading custom startup image %s", *Path); } else { LOGMSG("using custom startup image %s", *Path); bool r = Play_Mpeg2_ES(data, datalen, VIDEO_STREAM); free(data); for(int i=0; i<5 && !Flush(100); i++) ; return r; } free(data); } /* use default image */ extern const unsigned char v_mpg_vdrlogo[]; // vdrlogo_720x576.c extern const int v_mpg_vdrlogo_length; bool r = Play_Mpeg2_ES(v_mpg_vdrlogo, v_mpg_vdrlogo_length, VIDEO_STREAM); for(int i=0; i<5 && !Flush(100); i++) ; return r; } bool cXinelibThread::NoSignalDisplay(void) { TRACEF("cXinelibThread::NoSignalDisplay"); extern const unsigned char v_mpg_nosignal[]; // nosignal_720x576.c extern const int v_mpg_nosignal_length; bool r = Play_Mpeg2_ES(v_mpg_nosignal, v_mpg_nosignal_length, VIDEO_STREAM); for(int i=0; i<5 && !Flush(100); i++) ; return r; } // // Xine Control // int cXinelibThread::Xine_Control(const char *cmd, int p1) { char buf[128]; if(snprintf(buf, sizeof(buf), "%s %d", cmd, p1) >= (int)sizeof(buf)) { LOGMSG("Xine_Control %s: message too long !", cmd); return 0; } //buf[sizeof(buf)-1] = 0; return Xine_Control(buf); } int cXinelibThread::Xine_Control(const char *cmd, int64_t p1) { char buf[128]; if(snprintf(buf, sizeof(buf), "%s %" PRId64, cmd, p1) >= (int)sizeof(buf)) { LOGMSG("Xine_Control %s: message too long !", cmd); return 0; } //buf[sizeof(buf)-1] = 0; return Xine_Control(buf); } int cXinelibThread::Xine_Control(const char *cmd, const char *p1) { char buf[1024]; if(snprintf(buf, sizeof(buf), "%s %s", cmd, p1) >= (int)sizeof(buf)) { LOGMSG("Xine_Control %s: message too long !", cmd); return 0; } //buf[sizeof(buf)-1] = 0; return Xine_Control(buf); } bool cXinelibThread::PlayFile(const char *FileName, int Position, bool LoopPlay, ePlayMode PlayMode, int TimeoutMs) { TRACEF("cXinelibThread::PlayFile"); cString vis, buf; switch (PlayMode) { case pmVideoOnly: LOGDBG("cXinelibThread::PlayFile: Video from file, audio from VDR"); vis = "Video"; break; case pmAudioOnly: LOGDBG("cXinelibThread::PlayFile: Audio from file, video from VDR"); vis = "Audio"; break; case pmAudioOnlyBlack: //LOGDBG("cXinelibThread::PlayFile: Audio from file, no video"); vis = "none"; break; case pmAudioVideo: default: if (xc.audio_vis_goom_opts[0] && !strcmp(xc.audio_visualization, "goom")) { vis = cString::sprintf("%s:%s", xc.audio_visualization, xc.audio_vis_goom_opts); } else if (xc.audio_vis_image_mrl[0] && !strcmp(xc.audio_visualization, "image")) { vis = cString::sprintf("%s:%s", xc.audio_visualization, xc.audio_vis_image_mrl); } else { vis = xc.audio_visualization; } break; } m_bEndOfStreamReached = false; buf = cString::sprintf("PLAYFILE %s %d %s %s", LoopPlay ? "Loop" : "", Position, *vis, FileName ?: ""); if (FileName) { Lock(); m_FileName = FileName; m_bPlayingFile = true; m_SpuLangAuto = true; if (m_StatusMonitor) DELETENULL(m_StatusMonitor); m_StatusMonitor = new cFrontendStatusMonitor(m_SpuLangAuto); Unlock(); } int result = PlayFileCtrl(buf, TimeoutMs); if (!FileName || result != 0) { Lock(); m_bPlayingFile = false; m_FileName = NULL; if (m_StatusMonitor) DELETENULL(m_StatusMonitor); Unlock(); } else { if (xc.extsub_size >= 0) Xine_Control("EXTSUBSIZE", xc.extsub_size); // set preferred subtitle language if (Setup.DisplaySubtitles) { const char *langs = I18nLanguageCode(Setup.SubtitleLanguages[0]); if (langs) { char lang1[5]; strn0cpy(lang1, langs, 4); /* truncate */ const char *spu_lang = iso639_2_to_iso639_1(lang1); LOGMSG("Preferred SPU language: %s (%s)", lang1, spu_lang); Xine_Control(cString::sprintf("SPUSTREAM %s", lang1)); if (spu_lang && spu_lang[0] && spu_lang[1] && !spu_lang[2]) Xine_Control(cString::sprintf("SPUSTREAM %s", spu_lang)); } } else { LOGMSG("Preferred SPU language: (none)"); Xine_Control(cString::sprintf("SPUSTREAM %d", ttXSubtitleNone)); } } return Running() && !result; } // // Configuration // void cXinelibThread::Configure(void) { ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); ConfigureVideo(xc.hue, xc.saturation, xc.brightness, xc.sharpness, xc.noise_reduction, xc.contrast, xc.overscan, xc.vo_aspect_ratio); ConfigurePostprocessing("upmix", xc.audio_upmix ? true : false, NULL); ConfigurePostprocessing("autocrop", xc.autocrop ? true : false, xc.AutocropOptions()); ConfigurePostprocessing("swscale", xc.swscale ? true : false, xc.SwScaleOptions()); ConfigurePostprocessing("pp", xc.ffmpeg_pp ? true : false, xc.FfmpegPpOptions()); ConfigurePostprocessing("unsharp",xc.unsharp ? true : false, xc.UnsharpOptions()); ConfigurePostprocessing("denoise3d",xc.denoise3d ? true : false, xc.Denoise3dOptions()); #ifdef ENABLE_TEST_POSTPLUGINS ConfigurePostprocessing("headphone", xc.headphone ? true : false, NULL); #endif Xine_Control(cString::sprintf("SCR %s %d", xc.live_mode_sync ? "Sync" : "NoSync", xc.scr_tuning ? xc.scr_hz : 90000)); Xine_Control("HDMODE", m_bHDMode); Xine_Control("CONFIG END"); } int cXinelibThread::ConfigurePostprocessing(const char *deinterlace_method, int audio_delay, int audio_compression, const int *audio_equalizer, int audio_surround, int speaker_type) { int r = true; if(strcmp(deinterlace_method, "tvtime")) r = ConfigurePostprocessing("tvtime", false, NULL) && r; r = Xine_Control("DEINTERLACE", deinterlace_method) && r; r = Xine_Control("AUDIODELAY", audio_delay) && r; r = Xine_Control("AUDIOCOMPRESSION", audio_compression) && r; r = Xine_Control("AUDIOSURROUND", audio_surround) && r; r = Xine_Control("SPEAKERS", speaker_type) && r; r = Xine_Control(cString::sprintf("EQUALIZER %d %d %d %d %d %d %d %d %d %d", audio_equalizer[0], audio_equalizer[1], audio_equalizer[2], audio_equalizer[3], audio_equalizer[4], audio_equalizer[5], audio_equalizer[6], audio_equalizer[7], audio_equalizer[8], audio_equalizer[9])) && r; if (m_bNoVideo && strcmp(xc.audio_visualization, "none") && strcmp(xc.audio_visualization, "image")) { char *opts = NULL; if(xc.audio_vis_goom_opts[0] && !strcmp(xc.audio_visualization, "goom")) opts = xc.audio_vis_goom_opts; //fe->post_open(fe, xc.audio_visualization, NULL); r = ConfigurePostprocessing(xc.audio_visualization, true, opts) && r; } else { //fe->post_close(fe, NULL, 0); r = ConfigurePostprocessing("AudioVisualization", false, NULL) && r; } if(!strcmp(deinterlace_method, "tvtime")) r = ConfigurePostprocessing("tvtime", true, xc.deinterlace_opts) && r; return r; } int cXinelibThread::ConfigurePostprocessing(const char *name, bool on, const char *args) { cString cmd = cString::sprintf("POST %s %s %s", (name && *name) ? name : "0", on ? "On" : "Off", (on && args) ? args : ""); return Xine_Control(cmd); } int cXinelibThread::ConfigureVideo(int hue, int saturation, int brightness, int sharpness, int noise_reduction, int contrast, int overscan, int vo_aspect_ratio) { Xine_Control("OVERSCAN", overscan); return Xine_Control(cString::sprintf("VIDEO_PROPERTIES %d %d %d %d %d %d %d", hue, saturation, brightness, sharpness, noise_reduction, contrast, vo_aspect_ratio)); } // // Playback files // bool cXinelibThread::EndOfStreamReached(void) { LOCK_THREAD; bool r = m_bEndOfStreamReached; return r; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/examples/�����������������������������������������������������������������������0000755�0001750�0001750�00000000000�13061253352�014042� 5����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/examples/remote.conf.example����������������������������������������������������0000644�0001750�0001750�00000002746�13061253352�017647� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������LIRC.Up up LIRC.Down down LIRC.Left left LIRC.Right right LIRC.Menu menu LIRC.Ok ok LIRC.Back exit KBD.Up 00000000001B5B41 KBD.Down 00000000001B5B42 KBD.Menu 000000000000006D KBD.Ok 000000000000000D KBD.Back 0000000000000062 KBD.Left 00000000001B5B44 KBD.Right 00000000001B5B43 XKeySym.Up Up XKeySym.Down Down XKeySym.Left Left XKeySym.Right Right XKeySym.Menu F1 XKeySym.Ok Return XKeySym.Back BackSpace XKeySym.Red F2 XKeySym.Green F3 XKeySym.Yellow F4 XKeySym.Blue F5 XKeySym.0 0 XKeySym.1 1 XKeySym.2 2 XKeySym.3 3 XKeySym.4 4 XKeySym.5 5 XKeySym.6 6 XKeySym.7 7 XKeySym.8 8 XKeySym.9 9 XKeySym.Play p XKeySym.Pause space XKeySym.Stop s XKeySym.FastFwd f XKeySym.FastRew b XKeySym.Channel+ Prior XKeySym.Channel- Next XKeySym.Volume+ KP_Add XKeySym.Volume- KP_Subtract XKeySym.Mute m XKeySym.Info I XKeySym.Audio A XKeySym.Audio Alt+A XKeySym.Audio Alt+a XKeySym.Subtitles S XKeySym.Schedule F6 XKeySym.Channels F7 XKeySym.Timers F8 XKeySym.Recordings F9 XKeySym.Setup F10 XKeySym.Commands F11 XKeySym.User1 q XKeySym.User2 w XKeySym.User3 e XKeySym.User4 r XKeySym.User5 t XKeySym.User6 y XKeySym.User7 u XKeySym.User8 i XKeySym.User9 o ��������������������������xineliboutput-2.0.0/examples/allowed_hosts.conf�����������������������������������������������������0000644�0001750�0001750�00000000704�13061253352�017561� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # allowed_hosts This file describes a number of host addresses that # are allowed to connect to the xineliboutput plugin # of the Video Disk Recorder (VDR) running on this system. # Syntax: # # IP-Address[/Netmask] # 127.0.0.1 # always accept localhost #192.168.100.0/24 # any host on the local net #204.152.189.113 # a specific host #0.0.0.0/0 # any host on any net (USE THIS WITH CARE!) ������������������������������������������������������������xineliboutput-2.0.0/examples/Live Radio/������������������������������������������������������������0000755�0001750�0001750�00000000000�13061253352�015760� 5����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/examples/Live Radio/BBC World Service.ram���������������������������������������0000644�0001750�0001750�00000000110�13061253352�021470� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rtsp://rmlivev8.bbc.net.uk/farm/*/ev7/live24/worldservice/liveinfent.ra ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/examples/Live Radio/BBC Radio 4 Live.m3u����������������������������������������0000755�0001750�0001750�00000000157�13061253352�021025� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#EXTM3U #EXTINF:111,BBC Radio 4 Live rtsp://rmlivev8bb.bbc.net.uk/farm/*/ev7/live24/radio4/live/r4_dsat_g2.ra �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/equalizer.h���������������������������������������������������������������������0000644�0001750�0001750�00000001254�13061253352�014400� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * equalizer.h: audio equalizer OSD control * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __EQUALIZER_H #define __EQUALIZER_H #include <vdr/config.h> #include <vdr/osdbase.h> class cXinelibDevice; class cEqualizer : public cOsdObject { private: cXinelibDevice *m_Dev; int *m_Values; int m_Current; cOsd *m_Osd; public: cEqualizer(cXinelibDevice *Dev); virtual ~cEqualizer(); virtual void Show(); virtual eOSState ProcessKey(eKeys Key); void DrawBackground(void); void DrawBar(int Index, bool Selected = false); }; #endif // __EQUALIZER_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/equalizer.c���������������������������������������������������������������������0000644�0001750�0001750�00000007433�13061253352�014400� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * equalizer.c: audio equalizer OSD control * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include <vdr/config.h> #include "config.h" #include "device.h" #include "equalizer.h" cEqualizer::cEqualizer(cXinelibDevice *Dev) : cOsdObject() { m_Dev = Dev; m_Values = new int[AUDIO_EQ_count]; memcpy(m_Values, xc.audio_equalizer, sizeof(xc.audio_equalizer)); m_Osd = NULL; m_Current = 0; } cEqualizer::~cEqualizer() { delete m_Osd; delete[] m_Values; } #define OSD_W (220) #define OSD_H (220) #define OSD_X (720-50-OSD_W) #define OSD_Y (576-50-OSD_H) /* dvbdevice requires bpp*width to be n*8 */ #define ADJUST_MIN (-100) #define ADJUST_MAX (100) #define ADJUST_STEP (5) void cEqualizer::Show() { tArea areas [] = { {0, 0, OSD_W - 1, OSD_H - 1, 4} }; m_Osd = cOsdProvider::NewOsd(OSD_X, OSD_Y, 0); if(m_Osd) { if (m_Osd->CanHandleAreas(areas, sizeof(areas) / sizeof(tArea) ) == oeOk) { m_Osd->SetAreas(areas, sizeof(areas) / sizeof(tArea)); m_Osd->Flush(); DrawBackground(); DrawBar(0,true); for(int i=1; i<AUDIO_EQ_count; i++) DrawBar(i); } } } eOSState cEqualizer::ProcessKey(eKeys key) { eOSState state = cOsdObject::ProcessKey(key); if (state == osUnknown) { switch (key & ~k_Repeat) { case kDown: m_Values[m_Current] -= ADJUST_STEP; if(m_Values[m_Current] < ADJUST_MIN) m_Values[m_Current] = ADJUST_MIN; DrawBar(m_Current,true); m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, xc.audio_compression, m_Values, xc.audio_surround, xc.speaker_type); break; case kUp: m_Values[m_Current] += ADJUST_STEP; if(m_Values[m_Current] > ADJUST_MAX) m_Values[m_Current] = ADJUST_MAX; DrawBar(m_Current,true); m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, xc.audio_compression, m_Values, xc.audio_surround, xc.speaker_type); break; case kLeft: if(m_Current>0) { DrawBar(m_Current); m_Current--; DrawBar(m_Current, true); } break; case kRight: if(m_Current+1 < AUDIO_EQ_count) { DrawBar(m_Current); m_Current++; DrawBar(m_Current, true); } break; case kBack: m_Dev->ConfigurePostprocessing(xc.deinterlace_method, xc.audio_delay, xc.audio_compression, xc.audio_equalizer, xc.audio_surround, xc.speaker_type); return osEnd; case kOk: memcpy(xc.audio_equalizer, m_Values, sizeof(xc.audio_equalizer)); return osEnd; default:; // all other keys - do nothing. } } return state; } #define COL_BORDER 0xffb0b0b0 #define COL_BG 0x7f7f7f7f #define COL_BAR 0xff000000 #define COL_BAR_SEL 0xffff0000 #define COL_BAR_ON 0xff00FF00 #define COL_BAR_OFF 0xff000000 #define COL_BAR_BORDER 0xff7f7f7f void cEqualizer::DrawBackground() { // border m_Osd->DrawRectangle(0, 0, OSD_W - 1, OSD_H - 1, COL_BORDER); m_Osd->DrawRectangle(1, 1, OSD_W - 2, OSD_H - 2, COL_BORDER); // background m_Osd->DrawRectangle(2, 2, OSD_W - 3, OSD_H - 3, COL_BG); // line m_Osd->DrawRectangle(5, 10+100-1, OSD_W-6, 10+100, COL_BAR); // commit m_Osd->Flush(); } void cEqualizer::DrawBar(int Index, bool Selected) { // bar if(Selected) m_Osd->DrawRectangle(10+20*Index, 10, 10+20*Index+7, OSD_H - 10, COL_BAR_SEL); else m_Osd->DrawRectangle(10+20*Index, 10, 10+20*Index+7, OSD_H - 10, COL_BAR); // off m_Osd->DrawRectangle(12+20*Index, 10, 10+20*Index+5, OSD_H - 10, COL_BAR_OFF); // on if(m_Values[Index]>0) m_Osd->DrawRectangle(12+20*Index, 10+100-m_Values[Index], 10+20*Index+5, 10+100, COL_BAR_ON); else m_Osd->DrawRectangle(12+20*Index, 10+100, 10+20*Index+5, 10+100-m_Values[Index], COL_BAR_ON); // line m_Osd->DrawRectangle(12+20*Index, 10+100-1, 10+20*Index+5, 10+100, COL_BAR_ON); m_Osd->Flush(); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/device.h������������������������������������������������������������������������0000644�0001750�0001750�00000016531�13061253352�013642� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * device.h: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef __XINELIB_DEVICE_H #define __XINELIB_DEVICE_H #include <vdr/config.h> #include <vdr/device.h> #include <vdr/tools.h> #include <vdr/thread.h> class cXinelibStatusMonitor; class cXinelibThread; class cChannel; class cFunctor; struct video_size_s; struct ts_state_s; typedef enum { miTitle = 0, miTracknumber = 1, miArtist = 2, miAlbum = 3, miDvdTitleNo = 4, miDvdButtons = 5, mi_Count = 6 } eMetainfoType; #define ttXSubtitleNone (-2) #define ttXSubtitleAuto (-1) #define MAX_METAINFO_LEN 63 #define MAX_NUM_PIP 16 class cXinelibDevice : public cDevice { // Singleton private: static cXinelibDevice* m_pInstance; // singleton cXinelibDevice(); // cXinelibDevice(cXinelibDevice&); // no copy constructor public: static cXinelibDevice& Instance(void); // singleton static void Dispose(void); virtual ~cXinelibDevice(); // device start/stop (from cPlugin) public: bool InitDevice(void); bool StartDevice(void); void StopDevice(void); // function calls waiting to be executed in VDR main thread context private: cList<cFunctor> m_MainThreadFunctors; cMutex m_MainThreadLock; public: void MainThreadHook(void); // Primary device switching private: int m_OriginalPrimaryDevice; int m_ForcePrimaryDeviceCnt; void ForcePrimaryDeviceImpl(bool On); public: virtual void MakePrimaryDevice(bool On); bool ForcePrimaryDevice(bool On); // Device capabilities public: virtual bool HasDecoder(void) const { return true; }; virtual bool CanReplay(void) const { return true; }; virtual bool HasIBPTrickSpeed(void) { return xc.ibp_trickspeed; } virtual cRect CanScaleVideo(const cRect &Rect, int Alignment = taCenter); bool SupportsTrueColorOSD(void); // Playback control private: ePlayMode m_PlayMode; int m_TrickSpeed; int m_TrickSpeedMode; cRect m_VideoWindow; public: virtual bool SetPlayMode(ePlayMode PlayMode); ePlayMode GetPlayMode(void) const { return m_PlayMode; }; const cRect& GetVideoWindow(void) { return m_VideoWindow; } protected: virtual void Clear(void); virtual void Play(void); virtual void TrickSpeed(int Speed, bool Forward) { TrickSpeed(Speed); } virtual void TrickSpeed(int Speed); virtual void Freeze(void); virtual bool Flush(int TimeoutMs = 0); virtual int64_t GetSTC(void); virtual void ScaleVideo(const cRect &Rect = cRect::Null); // Video format facilities public: virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat); virtual void SetVideoFormat(bool VideoFormat16_9); #if VDRVERSNUM < 20301 virtual eVideoSystem GetVideoSystem(void); #endif struct video_size_s *m_VideoSize; struct ts_state_s *m_tssVideoSize; virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect); virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect); // Track facilities protected: virtual void SetAudioTrackDevice(eTrackType Type); public: virtual void SetSubtitleTrackDevice(eTrackType Type); // Audio facilities private: int m_AudioChannel; protected: virtual int GetAudioChannelDevice(void) { return m_AudioChannel; } virtual void SetAudioChannelDevice(int AudioChannel); virtual void SetVolumeDevice(int Volume); virtual void SetDigitalAudioDevice(bool On); // Image grabbing public: virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); // SPU decoder private: cSpuDecoder *m_spuDecoder; friend class cXineSpuDecoder; public: virtual cSpuDecoder *GetSpuDecoder(void); // Messages from StatusMonitor: private: cXinelibStatusMonitor *m_statusMonitor; bool m_liveMode; protected: friend class cXinelibStatusMonitor; void SetTvMode(int ChannelNumber); void SetReplayMode(void); void StopOutput(void); // Osd Commands (from cXinelibOsd) public: void OsdCmd(void *cmd); // Configuration private: cList<cXinelibThread> m_clients; cXinelibThread *m_server; cXinelibThread *m_local; public: void ConfigurePostprocessing(const char *deinterlace_method, int audio_delay, int audio_compression, const int *audio_equalizer, int audio_surround, int speaker_type); void ConfigurePostprocessing(const char *name, bool on = true, const char *args = NULL); void ConfigureVideo(int hue, int saturation, int brightness, int sharpness, int noise_reduction, int contrast, int overscan, int vo_aspect_ratio); // local mode: void ConfigureWindow(int fullscreen, int width, int height, int modeswitch, const char *modeline, int aspect, int scale_video); void ConfigureDecoder(int pes_buffers); // remote mode: void Listen(bool activate, int port); // File playback private: ePlayMode m_PlayingFile; public: bool PlayFile(const char *Filename, int Position = 0, bool LoopPlay = false, ePlayMode PlayMode = pmAudioVideo, int TimeoutMs = -1); int PlayFileCtrl(const char *Cmd, int TimeoutMs = -1); bool EndOfStreamReached(void); // Metainfo cache private: char m_MetaInfo[mi_Count][MAX_METAINFO_LEN+1]; public: const char *GetMetaInfo(eMetainfoType Type); void SetMetaInfo(eMetainfoType Type, const char *Value); // Stream data private: bool m_RadioStream; int m_AudioCount; bool m_SkipAudio; bool m_StreamStart; int m_FreeBufs; int PlayAny(const uchar *Data, int Length); bool AcceptVideoPacket(const uchar *Data, int Length); bool AcceptAudioPacket(const uchar *Data, int Length); protected: virtual bool Poll(cPoller &Poller, int TimeoutMs = 0); virtual void StillPicture(const uchar *Data, int Length); // MPEG-PES virtual int PlayVideo(const uchar *Data, int Length); virtual int PlayAudio(const uchar *Data, int Length, uchar Id); virtual int PlaySubtitle(const uchar *Data, int Length); // join multiple TS packets to xineliboutput transport packet cMutex m_TsBufLock; uint8_t m_TsBuf[4096]; uint m_TsBufSize; int TsBufferFlush(void); void TsBufferClear(void); int PlayTsAny(const uchar *Data, int Length); // MPEG-TS virtual int PlayTsVideo(const uchar *Data, int Length); virtual int PlayTsAudio(const uchar *Data, int Length); virtual int PlayTsSubtitle(const uchar *Data, int Length); virtual int PlayTs(const uchar *Data, int Length, bool VideoOnly = false); // Picture-In-Picture protected: uint16_t m_PipPid[MAX_NUM_PIP]; public: int Pip_Open (void); void Pip_Config (int Index, int X, int Y, int W, int H); int Pip_Play (int Index, uint8_t *Data, int Length); void Pip_Close (int Index); }; #endif // __XINELIB_DEVICE_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/device.c������������������������������������������������������������������������0000644�0001750�0001750�00000125073�13061253352�013637� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * device.c: xine-lib output device for the Video Disk Recorder * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #define __STDC_FORMAT_MACROS #define __STDC_CONSTANT_MACROS #include <inttypes.h> #include <vdr/config.h> #include <vdr/thread.h> #include <vdr/dvbspu.h> #include <vdr/channels.h> #include <vdr/skins.h> #include <vdr/status.h> #include <vdr/remote.h> #include <vdr/plugin.h> //#define XINELIBOUTPUT_DEBUG //#define XINELIBOUTPUT_DEBUG_STDERR //#define TRACK_EXEC_TIME //#define FORWARD_DVD_SPUS //#define DEBUG_SWITCHING_TIME //#define LOG_TRICKSPEED #include "logdefs.h" #include "config.h" #include "osd.h" #include "tools/listiter.h" #include "tools/mpeg.h" #include "tools/pes.h" #include "tools/ts.h" #include "tools/functor.h" #include "tools/section_lock.h" #include "frontend_local.h" #include "frontend_svr.h" #include "device.h" #define STILLPICTURE_REPEAT_COUNT 3 #define LOCAL_INIT_TIMEOUT 20 // seconds #define SERVER_INIT_TIMEOUT 5 // seconds #ifdef LOG_TRICKSPEED # define LOGTRICKSPEED(x...) LOGMSG("trs: " x) #else # define LOGTRICKSPEED(x...) #endif //---------------------------- status monitor ------------------------------- class cXinelibStatusMonitor : public cStatus { private: cXinelibStatusMonitor(); cXinelibStatusMonitor(cXinelibStatusMonitor&); public: cXinelibStatusMonitor(cXinelibDevice& device, int cardIndex) : m_Device(device), m_cardIndex(cardIndex) { #ifdef DEBUG_SWITCHING_TIME switchtimeOff = 0LL; switchtimeOn = 0LL; switchingIframe = false; #endif }; protected: virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView); virtual void Replaying(const cControl *Control, const char *Name, const char *FileName, bool On); cXinelibDevice& m_Device; int m_cardIndex; #ifdef DEBUG_SWITCHING_TIME public: int64_t switchtimeOff; int64_t switchtimeOn; bool switchingIframe; void IFrame(void) { if(!switchingIframe) { int64_t now = cTimeMs::Now(); switchingIframe = true; LOGMSG("Channel switch: off -> on %" PRId64 " ms, " "on -> 1. I-frame %" PRId64 " ms", switchtimeOn-switchtimeOff, now-switchtimeOn); } else { int64_t now = cTimeMs::Now(); LOGMSG("Channel switch: on -> 2. I-frame %" PRId64 " ms, " "Total %" PRId64 " ms", now-switchtimeOn, now-switchtimeOff); switchtimeOff = 0LL; switchtimeOn = 0LL; switchingIframe = false; } } #endif }; void cXinelibStatusMonitor::ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView) { TRACEF("cXinelibStatusMonitor::ChannelSwitch"); TRACK_TIME(200); if (ChannelNumber) { if (Device->CardIndex() == m_cardIndex) { #ifdef DEBUG_SWITCHING_TIME switchtimeOn = cTimeMs::Now(); #endif m_Device.SetTvMode(ChannelNumber); TRACE("cXinelibStatusMonitor: Set to TvMode"); } } else { if (Device->CardIndex() == m_cardIndex) { #ifdef DEBUG_SWITCHING_TIME switchtimeOff = cTimeMs::Now(); #endif m_Device.StopOutput(); TRACE("cXinelibStatusMonitor: received stop"); } } } void cXinelibStatusMonitor::Replaying(const cControl *Control, const char *Name, const char *FileName, bool On) { TRACEF("cXinelibStatusMonitor::Replaying"); if (On /*&& Name != NULL*/) { TRACE("cXinelibStatusMonitor: Replaying " << Name << "(" << FileName << ")"); m_Device.SetReplayMode(); } } //----------------------------- device ---------------------------------------- // // Singleton // cXinelibDevice* cXinelibDevice::m_pInstance = NULL; cXinelibDevice& cXinelibDevice::Instance(void) { TRACEF("cXinelibDevice::Instance"); if (!m_pInstance) { m_pInstance = new cXinelibDevice(); TRACE("cXinelibDevice::Instance(): create, cardindex = " << m_pInstance->CardIndex()); } return *m_pInstance; } void cXinelibDevice::Dispose(void) { TRACEF("cXinelibDevice::Dispose"); delete m_pInstance; m_pInstance = NULL; } // // init and shutdown // cXinelibDevice::cXinelibDevice() { TRACEF("cXinelibDevice::cXinelibDevice"); m_statusMonitor = NULL; m_spuDecoder = NULL; m_local = NULL; m_server = NULL; m_OriginalPrimaryDevice = 0; m_ForcePrimaryDeviceCnt = 0; memset(m_MetaInfo, 0, sizeof(m_MetaInfo)); m_PlayMode = pmNone; m_AudioChannel = 0; m_liveMode = true; m_TrickSpeed = -1; m_TrickSpeedMode = 0; m_SkipAudio = false; m_PlayingFile = pmNone; m_StreamStart = true; m_RadioStream = false; m_AudioCount = 0; m_FreeBufs = 0; m_VideoSize = (video_size_t*)calloc(1, sizeof(video_size_t)); m_tssVideoSize = NULL; TsBufferClear(); } cXinelibDevice::~cXinelibDevice() { TRACEF("cXinelibDevice::~cXinelibDevice"); StopDevice(); m_pInstance = NULL; free (m_VideoSize); ts_state_dispose(m_tssVideoSize); } bool cXinelibDevice::InitDevice() { TRACEF("cXinelibDevice::InitDevice"); if (m_local || m_server) { LOGMSG("cXinelibDevice::InitDevice() called twice"); return false; } if (*xc.local_frontend && strncmp(xc.local_frontend, "none", 4)) m_clients.Add(m_local = new cXinelibLocal(this, xc.local_frontend)); if (xc.remote_mode && xc.listen_port > 0) m_clients.Add(m_server = new cXinelibServer(this, xc.listen_port)); return true; } bool cXinelibDevice::StartDevice() { TRACEF("cXinelibDevice::StartDevice"); if(m_local) m_local->Start(); if(m_server) m_server->Start(); // if(dynamic_cast<cXinelibLocal*>(it)) if(m_local) { int timer = 0; while (!m_local->IsReady()) { cCondWait::SleepMs(100); if (!m_local->Active()) { LOGMSG("cXinelibDevice::Start(): Local frontend init failed"); return false; } if (++timer >= LOCAL_INIT_TIMEOUT*10) { LOGMSG("cXinelibDevice::Start(): Local frontend init timeout"); return false; } } if(xc.force_primary_device) ForcePrimaryDevice(true); else if (cDevice::PrimaryDevice() && this != cDevice::PrimaryDevice()) { LOGMSG("WARNING: xineliboutput is not the primary device !"); } } if(m_server) { int timer = 0; while(!m_server->IsReady()) { cCondWait::SleepMs(100); if (!m_server->Active()) { LOGMSG("cXinelibDevice::Start(): Server init failed"); return false; } if(++timer >= SERVER_INIT_TIMEOUT*10) { LOGMSG("cXinelibDevice::Start(): Server init timeout"); return false; } } } ASSERT(m_statusMonitor == NULL, false); m_statusMonitor = new cXinelibStatusMonitor(*this, CardIndex()); LOGDBG("cXinelibDevice::StartDevice(): Device started"); return true; } void cXinelibDevice::StopDevice(void) { TRACEF("cXinelibDevice::StopDevice"); LOGDBG("cXinelibDevice::StopDevice(): Stopping device ..."); if(m_statusMonitor) { delete m_statusMonitor; m_statusMonitor = NULL; } if (m_spuDecoder) { delete m_spuDecoder; m_spuDecoder = NULL; } cXinelibThread *server = m_server; cXinelibThread *local = m_local; m_local = m_server = NULL; cControl::Shutdown(); ForEach(m_clients, &cXinelibThread::SetLiveMode, false); TrickSpeed(-1); if(local) m_clients.Del(local, false); if(server) m_clients.Del(server, false); if(server) delete server; if(local) delete local; m_clients.Clear(); } // // Primary device switching // void cXinelibDevice::MakePrimaryDevice(bool On) { TRACEF("cXinelibDevice::MakePrimaryDevice"); cDevice::MakePrimaryDevice(On); if(On) new cXinelibOsdProvider(this); } bool cXinelibDevice::ForcePrimaryDevice(bool On) { TRACEF("cXinelibDevice::ForcePrimaryDevice"); m_MainThreadLock.Lock(); m_MainThreadFunctors.Add(CreateFunctor(this, &cXinelibDevice::ForcePrimaryDeviceImpl, On)); m_MainThreadLock.Unlock(); if (On) { int timeout = 10; while (this != cDevice::PrimaryDevice() && timeout-- > 0) { isyslog("waiting for primary device ..."); cCondWait::SleepMs(100); } } return xc.force_primary_device || (cDevice::PrimaryDevice() && this == cDevice::PrimaryDevice()); } void cXinelibDevice::ForcePrimaryDeviceImpl(bool On) { TRACEF("cXinelibDevice::ForcePrimaryDeviceImpl"); ASSERT(cThread::IsMainThread(), false); if(On) { m_ForcePrimaryDeviceCnt++; if(xc.force_primary_device) { if(cDevice::PrimaryDevice() && this != cDevice::PrimaryDevice()) { m_OriginalPrimaryDevice = cDevice::PrimaryDevice()->DeviceNumber() + 1; cControl::Shutdown(); LOGMSG("Forcing primary device, original index = %d", m_OriginalPrimaryDevice); if(cOsd::IsOpen()) { LOGMSG("Forcing primary device, old OSD still open !"); xc.main_menu_mode = CloseOsd; cRemote::CallPlugin("xineliboutput"); } SetPrimaryDevice(DeviceNumber() + 1); } } } else /* Off */ { m_ForcePrimaryDeviceCnt--; if(m_ForcePrimaryDeviceCnt < 0) LOGMSG("ForcePrimaryDevice: Internal error (ForcePrimaryDevice < 0)"); else if(m_ForcePrimaryDeviceCnt == 0) { if(m_OriginalPrimaryDevice) { LOGMSG("Restoring original primary device %d", m_OriginalPrimaryDevice); cControl::Shutdown(); if(cOsd::IsOpen()) { LOGMSG("Restoring primary device, xineliboutput OSD still open !"); xc.main_menu_mode = CloseOsd; /* will be executed in future by vdr main thread */ cRemote::CallPlugin("xineliboutput"); } #if VDRVERSNUM >= 20301 LOCK_CHANNELS_READ; const cChannel *channel = Channels->GetByNumber(CurrentChannel()); #else cChannel *channel = Channels.GetByNumber(CurrentChannel()); #endif cDevice::SetPrimaryDevice(m_OriginalPrimaryDevice); PrimaryDevice()->SwitchChannel(channel, true); m_OriginalPrimaryDevice = 0; } } } if (xc.use_suspendoutput && !m_local) { cPlugin *p = cPluginManager::GetPlugin("suspendoutput"); if (p) { bool result = true; if (m_ForcePrimaryDeviceCnt == 0) { LOGDBG("enabling suspendoutput"); result = p->Service("SuspendOutputPlugin-v1.0", (void*)"Suspend"); } if (m_ForcePrimaryDeviceCnt == 1) { LOGDBG("disabling suspendoutput"); result = p->Service("SuspendOutputPlugin-v1.0", (void*)"Resume"); } if (!result) { LOGERR("suspendoutput service failed. " "--auto-suspend option requires vdr-suspendoutput plugin version 2.1.0 or later."); } } else { LOGERR("suspendoutputr plugin not loaded ?"); LOGERR("--auto-suspend option requires vdr-suspendoutput plugin."); } } } // // Execute functors in main thread context // void cXinelibDevice::MainThreadHook(void) { TRACEF("cXinelibDevice::MainThreadHook"); if(m_MainThreadFunctors.First()) { cFunctor *f = NULL; do { m_MainThreadLock.Lock(); if(f) m_MainThreadFunctors.Del(f); f = m_MainThreadFunctors.First(); m_MainThreadLock.Unlock(); if(f) { /*LOGDBG("cXinelibDevice::MainThreadHook: executing functor 0x%lx",(long)f);*/ f->Execute(); } } while(f); } } // // Configuration // void cXinelibDevice::ConfigurePostprocessing(const char *deinterlace_method, int audio_delay, int audio_compression, const int *audio_equalizer, int audio_surround, int speaker_type) { TRACEF("cXinelibDevice::ConfigurePostprocessing"); if(m_local) m_local->ConfigurePostprocessing(deinterlace_method, audio_delay, audio_compression, audio_equalizer, audio_surround, speaker_type); if(m_server) m_server->ConfigurePostprocessing(deinterlace_method, audio_delay, audio_compression, audio_equalizer, audio_surround, speaker_type); } void cXinelibDevice::ConfigurePostprocessing(const char *name, bool on, const char *args) { TRACEF("cXinelibDevice::ConfigurePostprocessing"); if(m_local) m_local->ConfigurePostprocessing(name, on, args); if(m_server) m_server->ConfigurePostprocessing(name, on, args); } void cXinelibDevice::ConfigureVideo(int hue, int saturation, int brightness, int sharpness, int noise_reduction, int contrast, int overscan, int vo_aspect_ratio) { TRACEF("cXinelibDevice::ConfigureVideo"); if(m_local) m_local->ConfigureVideo(hue, saturation, brightness, sharpness, noise_reduction, contrast, overscan, vo_aspect_ratio); if(m_server) m_server->ConfigureVideo(hue, saturation, brightness, sharpness, noise_reduction, contrast, overscan, vo_aspect_ratio); } void cXinelibDevice::ConfigureDecoder(int pes_buffers) { TRACEF("cXinelibDevice::ConfigureDecoder"); if(m_local) m_local->ConfigureDecoder(pes_buffers); //if(m_server) // m_server->ConfigureDecoder(pes_buffers); cXinelibOsdProvider::RefreshOsd(); } void cXinelibDevice::ConfigureWindow(int fullscreen, int width, int height, int modeswitch, const char *modeline, int aspect, int scale_video) { TRACEF("cXinelibDevice::ConfigureWindow"); if((!*xc.local_frontend || !strncmp(xc.local_frontend, "none", 4)) && m_local) { cXinelibThread *tmp = m_local; m_clients.Del(tmp, false); m_local = NULL; cCondWait::SleepMs(5); delete tmp; if(xc.force_primary_device) ForcePrimaryDevice(false); } if(m_local) m_local->ConfigureWindow(fullscreen, width, height, modeswitch, modeline, aspect, scale_video); else if(*xc.local_frontend && strncmp(xc.local_frontend, "none", 4)) { cXinelibThread *tmp = new cXinelibLocal(this, xc.local_frontend); tmp->Start(); m_clients.Add(m_local = tmp); cCondWait::SleepMs(25); while (!m_local->IsReady() && m_local->Active()) cCondWait::SleepMs(25); if (!m_local->Active()) { m_local = NULL; m_clients.Del(tmp, true); Skins.QueueMessage(mtError, tr("Frontend initialization failed"), 10); } else { if(xc.force_primary_device) ForcePrimaryDevice(true); m_local->ConfigureWindow(fullscreen, width, height, modeswitch, modeline, aspect, scale_video); } } } void cXinelibDevice::Listen(bool activate, int port) { TRACEF("cXinelibDevice::Listen"); if(activate && port>0) { if(!m_server) { cXinelibThread *tmp = new cXinelibServer(this, port); tmp->Start(); m_clients.Add(m_server = tmp); cCondWait::SleepMs(10); while (!m_server->IsReady() && m_server->Active()) cCondWait::SleepMs(10); if (!m_server->Active()) { Skins.QueueMessage(mtError, tr("Server initialization failed"), 10); m_server = NULL; m_clients.Del(tmp, true); } } else { if(! m_server->Listen(port)) Skins.QueueMessage(mtError, tr("Server initialization failed"), 10); } } else if( /*((!activate) || port<=0) && */ m_server) { cXinelibThread *tmp = m_server; m_clients.Del(tmp, false); m_server = NULL; cCondWait::SleepMs(5); delete tmp; } } // // OSD // void cXinelibDevice::OsdCmd(void *cmd) { TRACEF("cXinelibDevice::OsdCmd"); TRACK_TIME(250); if(m_server) // call first server, local frontend modifies contents of the message ... m_server->OsdCmd(cmd); if(m_local) m_local->OsdCmd(cmd); } // // Play mode control // void cXinelibDevice::StopOutput(void) { TRACEF("cXinelibDevice::StopOutput"); TRACK_TIME(250); m_RadioStream = false; m_AudioCount = 0; ForEach(m_clients, &cXinelibThread::SetLiveMode, false); Clear(); ForEach(m_clients, &cXinelibThread::QueueBlankDisplay); ForEach(m_clients, &cXinelibThread::SetNoVideo, false); } static bool IsRadioChannel(int ChannelNumber) { #if VDRVERSNUM >= 20301 LOCK_CHANNELS_READ; const cChannel *Channel = Channels->GetByNumber(ChannelNumber); #else const cChannel *Channel = Channels.GetByNumber(ChannelNumber); #endif if (Channel && !Channel->Vpid() && (Channel->Apid(0) || Channel->Apid(1))) return true; return false; } void cXinelibDevice::SetTvMode(int ChannelNumber) { TRACEF("cXinelibDevice::SetTvMode"); TRACK_TIME(250); m_RadioStream = IsRadioChannel(ChannelNumber); if(m_PlayMode == pmAudioOnlyBlack) m_RadioStream = true; TRACE("cXinelibDevice::SetTvMode - isRadio = "<<m_RadioStream); m_StreamStart = true; m_liveMode = true; m_TrickSpeed = -1; m_SkipAudio = false; m_AudioCount = 0; Clear(); ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream); ForEach(m_clients, &cXinelibThread::SetLiveMode, true); ForEach(m_clients, &cXinelibThread::ResumeOutput); } void cXinelibDevice::SetReplayMode(void) { TRACEF("cXinelibDevice::SetReplayMode"); m_RadioStream = true; // first seen replayed video packet resets this m_AudioCount = 15; m_StreamStart = true; m_TrickSpeed = -1; ForEach(m_clients, &cXinelibThread::SetLiveMode, false); Clear(); ForEach(m_clients, &cXinelibThread::SetNoVideo, false /*m_RadioStream*/); if(m_RadioStream && !m_liveMode) ForEach(m_clients, &cXinelibThread::BlankDisplay); ForEach(m_clients, &cXinelibThread::ResumeOutput); m_liveMode = false; } bool cXinelibDevice::SetPlayMode(ePlayMode PlayMode) { TRACEF("cXinelibDevice::SetPlayMode"); #ifdef XINELIBOUTPUT_DEBUG switch (PlayMode) { case pmNone: TRACE("cXinelibDevice::SetPlayMode audio/video from decoder"); break; case pmAudioVideo: TRACE("cXinelibDevice::SetPlayMode audio/video from player"); break; case pmVideoOnly: TRACE("cXinelibDevice::SetPlayMode video from player, audio from decoder"); break; case pmAudioOnly: TRACE("cXinelibDevice::SetPlayMode audio from player, video from decoder"); break; case pmAudioOnlyBlack: TRACE("cXinelibDevice::SetPlayMode audio only from player, no video (black screen)"); break; case pmExtern_THIS_SHOULD_BE_AVOIDED: TRACE("cXinelibDevice::SetPlayMode this should be avoided"); break; } #endif m_PlayMode = PlayMode; TrickSpeed(-1); if (m_PlayMode == pmAudioOnlyBlack) { TRACE("pmAudioOnlyBlack --> BlankDisplay, NoVideo"); ForEach(m_clients, &cXinelibThread::BlankDisplay); ForEach(m_clients, &cXinelibThread::SetNoVideo, true); } else { if(m_liveMode) ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream); else ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream && (m_AudioCount<1)); Clear(); } return true; } // // Playback control // // m_TrickSpeedMode flags #define trs_IPB_frames 0x01 // stream has all frame types #define trs_I_frames 0x02 // stream has only I-frames #define trs_NoAudio 0x08 // no audio in trick speed mode #define trs_Backward 0x40 // palying backwards -- same as regen pts ??? void cXinelibDevice::TrickSpeed(int Speed) { TRACEF("cXinelibDevice::TrickSpeed"); if(m_TrickSpeed != Speed) { int RealSpeed = abs(Speed); LOGTRICKSPEED("TrickSpeed changed from %d to %d [%d]", m_TrickSpeed, Speed, RealSpeed); m_TrickSpeed = Speed; // Possible transitions: // fast <-> play // play <-> pause // pause <-> slow // _and_ from any mode to normal play and pause if(Speed == 8 || Speed == 4 || Speed == 2) { LOGTRICKSPEED(" Slow forward (1/%d speed), IPB-frames", Speed); // do nothing - slow forward is just slow playback of complete stream m_TrickSpeedMode = trs_IPB_frames; // previous state was slow forward or pause --> no need for clear // change decoder and UDP/RTP scheduler clock rates ForEach(m_clients, &cXinelibThread::TrickSpeed, RealSpeed); } else if(Speed == 63 || Speed == 48 || Speed == 24) { RealSpeed = (Speed+11)/12; LOGTRICKSPEED(" Slow backward (1/%d speed), I-frames only", RealSpeed); // previous state was slow backwards or pause --> clear if it was pause // //if(PrevSpeed == 0 && !(m_TrickSpeedMode & trs_PTS_recalc)) { // LOGMSG(" -> Clear"); // ForEach(m_clients, &cXinelibThread::Clear); //} m_TrickSpeedMode = trs_I_frames | trs_Backward | trs_NoAudio; ForEach(m_clients, &cXinelibThread::TrickSpeed, RealSpeed, true); } else if(Speed == 6 || Speed == 3 || Speed == 1) { RealSpeed = 12/Speed; LOGTRICKSPEED(" Fast (%dx speed), direction unknown", RealSpeed); if (m_StreamStart) { LOGTRICKSPEED(" Fast (%dx speed), STREAM START --> BACKWARDS", RealSpeed); } if(RealSpeed > xc.max_trickspeed) { RealSpeed = xc.max_trickspeed; LOGTRICKSPEED(" Trick speed limited to %dx speed", RealSpeed); } if (m_StreamStart || (m_TrickSpeedMode & trs_Backward)) { m_TrickSpeedMode |= trs_I_frames | trs_Backward | trs_NoAudio; ForEach(m_clients, &cXinelibThread::TrickSpeed, -RealSpeed, true); } else { m_TrickSpeedMode |= trs_IPB_frames; ForEach(m_clients, &cXinelibThread::TrickSpeed, -RealSpeed); } } else if(Speed==-1 || Speed == 0) { LOGTRICKSPEED(" Play/Pause"); // change decoder and UDP/RTP scheduler clock rates ForEach(m_clients, &cXinelibThread::TrickSpeed, RealSpeed); // returning from backward mode needs Clear // //if(Speed==-1 && (m_TrickSpeedMode & trs_PTS_recalc)) { // LOGMSG(" -> Clear"); // ForEach(m_clients, &cXinelibThread::Clear); // m_TrickSpeedMode = 0; //} // returning from fast forward mode needs Clear // because of DvbPlayer jumps few seconds back at mode change ... // //if(Speed==-1 && (m_TrickSpeedMode & trs_I_frames)) { // LOGMSG(" -> Clear"); // ForEach(m_clients, &cXinelibThread::Clear); //} m_TrickSpeedMode = 0; } else { LOGTRICKSPEED(" Unknown trickspeed %d !", Speed); m_TrickSpeedMode = 0; m_TrickSpeed = -1; ForEach(m_clients, &cXinelibThread::TrickSpeed, -1); } ForEach(m_clients, &cXinelibThread::Sync); } } void cXinelibDevice::Clear(void) { TRACEF("cXinelibDevice::Clear"); TRACK_TIME(100); TsBufferClear(); m_StreamStart = true; m_FreeBufs = 0; TrickSpeed(-1); ForEach(m_clients, &cXinelibThread::Clear); ForEach(m_clients, &cXinelibThread::SetStillMode, false); } void cXinelibDevice::Play(void) { TRACEF("cXinelibDevice::Play"); m_SkipAudio = false; ForEach(m_clients, &cXinelibThread::SetLiveMode, false); ForEach(m_clients, &cXinelibThread::SetStillMode, false); TrickSpeed(-1); cDevice::Play(); } void cXinelibDevice::Freeze(void) { TRACEF("cXinelibDevice::Freeze"); TsBufferFlush(); TrickSpeed(0); cDevice::Freeze(); } int64_t cXinelibDevice::GetSTC(void) { TRACEF("cXinelibDevice::GetSTC"); if(m_local) return m_local->GetSTC(); if(m_server) return m_server->GetSTC(); return cDevice::GetSTC(); } bool cXinelibDevice::Flush(int TimeoutMs) { TRACEF("cXinelibDevice::Flush"); TRACK_TIME(500); TsBufferFlush(); if(m_TrickSpeed == 0) { ForEach(m_clients, &cXinelibThread::SetLiveMode, false); TrickSpeed(-1); } bool r = ForEach(m_clients, &cXinelibThread::Flush, TimeoutMs, &mand<bool>, true); return r; } // // Playback of files and images // int cXinelibDevice::PlayFileCtrl(const char *Cmd, int TimeoutMs) { TRACEF("cXinelibDevice::PlayFileCtrl"); int result = -1; /*if(m_PlayingFile != pmNone)*/ { if(m_server) result = m_server->PlayFileCtrl(Cmd, TimeoutMs); if(m_local) result = m_local->PlayFileCtrl(Cmd, TimeoutMs); } return result; } bool cXinelibDevice::EndOfStreamReached(void) { if(m_local && !m_local->EndOfStreamReached()) return false; if(m_server && !m_server->EndOfStreamReached()) return false; return true; } bool cXinelibDevice::PlayFile(const char *FileName, int Position, bool LoopPlay, ePlayMode PlayMode, int TimeoutMs) { TRACEF("cXinelibDevice::PlayFile"); TRACE("cXinelibDevice::PlayFile(\"" << FileName << "\")"); bool result = true; if(FileName) { if(m_PlayingFile == pmNone) { m_PlayingFile = PlayMode; if (!xc.IsImageFile(FileName)) StopOutput(); } for(int i = 0; i < mi_Count; i++) m_MetaInfo[i][0] = 0; if(m_server) result = m_server->PlayFile(FileName, Position, LoopPlay, PlayMode, TimeoutMs); if(m_local) result = m_local->PlayFile(FileName, Position, LoopPlay, PlayMode, TimeoutMs); } else if(/*!FileName &&*/m_PlayingFile != pmNone) { if(m_server) result = m_server->PlayFile(NULL, 0, 0, pmNone, TimeoutMs); if(m_local) result = m_local->PlayFile(NULL, 0, 0, pmNone, TimeoutMs); if(!m_liveMode) SetReplayMode(); else SetTvMode(cDevice::CurrentChannel()); m_PlayingFile = pmNone; } return result; } // // Data stream handling // int cXinelibDevice::PlayAny(const uchar *buf, int length) { TRACEF("cXinelibDevice::PlayAny"); TRACK_TIME(100); #if 0 if(m_PlayingFile) return length; #endif if (!buf || length <= 0) return length; // // Need to be sure Poll has been called for every frame: // - cDevice can feed multiple frames after each poll from player/transfer. // - If only part of frames are consumed, rest are fed again after next Poll. // - If there are multiple clients it is possible first client(s) // can queue more frames than last client(s). // -> frame(s) are either lost immediately (last client(s)) // or duplicated after next poll (first client(s)) // if(m_FreeBufs < 1) { cPoller Poller; if(!Poll(Poller,0)) { errno = EAGAIN; return 0; } } bool isMpeg1 = false; if (DATA_IS_PES(buf)) { isMpeg1 = pes_is_mpeg1(buf); int len = pes_packet_len(buf, length); if (len>0 && len != length) LOGMSG("cXinelibDevice::PlayAny: invalid data !"); } if(m_TrickSpeed > 0) { } else if(m_SkipAudio) { /* needed for still images when moving cutting marks */ if (DATA_IS_PES(buf)) pes_change_pts((uchar*)buf, length, INT64_C(0)); } m_FreeBufs --; if(m_local) { length = (isMpeg1 ? m_local->Play_Mpeg1_PES(buf,length) : m_local->Play(buf, length)); } if(m_server && length > 0) { int length2 = isMpeg1 ? m_server->Play_Mpeg1_PES(buf, length) : m_server->Play(buf, length); if(!m_local) return length2; } return length; } /* * hook to PlayTs() to get PAT and PMT */ int cXinelibDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly) { if (!Data || Length < TS_SIZE) { TsBufferFlush(); return cDevice::PlayTs(Data, Length, VideoOnly); } /* Play single TS pack */ int Result = cDevice::PlayTs(Data, TS_SIZE, VideoOnly); if (Result != TS_SIZE) return Result; /* Grab PAT and PMT */ if (TsHasPayload(Data) && TsPayloadOffset(Data) < TS_SIZE) { int Pid = TsPid(Data); if (Pid == PATPID || PatPmtParser()->IsPmtPid(Pid)) { if (m_server) m_server->SetHeader(Data, Result, Pid == 0); if (PlayTsAny(Data, Result) != Result) LOGMSG("Lost PAT/PMT fragment !"); TsBufferFlush(); /* detect radio streams */ int patv, pmtv; if (PatPmtParser()->GetVersions(patv, pmtv)) { if (!PatPmtParser()->Vpid() && PatPmtParser()->Apid(0)) { m_RadioStream = true; m_AudioCount = 0; ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream); } } } } return Result; } /* * TS buffer */ #define TS_BUFFER_LOCK cSectionLock SectionLock(m_TsBufLock) int cXinelibDevice::TsBufferFlush(void) { TS_BUFFER_LOCK; if (m_TsBufSize) { int n; if ((n = PlayAny(m_TsBuf, m_TsBufSize)) == (int)m_TsBufSize) { m_TsBufSize = 0; return n; } if (n) LOGMSG("cXinelibDevice::TsBufferFlush: error: cache not flushed (%d %d)", n, m_TsBufSize); errno = EAGAIN; } return 0; } void cXinelibDevice::TsBufferClear(void) { TS_BUFFER_LOCK; m_TsBufSize = 0; } int cXinelibDevice::PlayTsAny(const uchar *buf, int length) { if (!DATA_IS_TS(buf)) LOGMSG("PlayTsAny(): TS SYNC byte missing !"); if (length != TS_SIZE) LOGMSG("PlayTsAny(): length == %d !", length); TS_BUFFER_LOCK; // cache full ? try to flush it if (m_TsBufSize >= 2048) if (!TsBufferFlush()) return 0; // add packet to cache memcpy(m_TsBuf + m_TsBufSize, buf, length); m_TsBufSize += length; // time to flush ? if (m_TsBufSize >= 2048-TS_SIZE-1) TsBufferFlush(); return length; } int cXinelibDevice::PlayTsSubtitle(const uchar *Data, int Length) { if (!xc.dvb_subtitles) return cDevice::PlayTsSubtitle(Data, Length); return PlayTsAny(Data, Length); } int cXinelibDevice::PlayTsAudio(const uchar *Data, int Length) { if (!AcceptAudioPacket(Data, Length)) return Length; return PlayTsAny(Data, Length); } int cXinelibDevice::PlayTsVideo(const uchar *Data, int Length) { if (ts_PID(Data) == PatPmtParser()->Vpid()) { if (!AcceptVideoPacket(Data, Length)) return Length; if (m_StreamStart) { if (!m_tssVideoSize) m_tssVideoSize = ts_state_init(4096); if (ts_get_video_size(m_tssVideoSize, Data, m_VideoSize, (ts_stream_type)PatPmtParser()->Vtype())) { m_StreamStart = false; LOGMSG("Detected video size %dx%d", m_VideoSize->width, m_VideoSize->height); ForEach(m_clients, &cXinelibThread::SetHDMode, (m_VideoSize->width > 800)); ts_state_dispose(m_tssVideoSize); m_tssVideoSize = NULL; } } } return PlayTsAny(Data, Length); } bool cXinelibDevice::AcceptVideoPacket(const uchar *Data, int Length) { if (!Data || Length < 6) return false; if (m_PlayMode == pmAudioOnlyBlack) return false; if (m_PlayingFile && (m_PlayingFile == pmAudioVideo || m_PlayingFile == pmVideoOnly)) return false; if (m_RadioStream) { m_RadioStream = false; m_AudioCount = 0; ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream); } return true; } bool cXinelibDevice::AcceptAudioPacket(const uchar *Data, int Length) { if (!Data || Length < 6) return false; if (m_PlayingFile && (m_PlayingFile == pmAudioVideo || m_PlayingFile == pmAudioOnly)) return false; // strip audio in trick speed modes and when displaying still images if (m_SkipAudio) return false; if (m_TrickSpeedMode & trs_NoAudio) return false; if (m_RadioStream) { if (m_AudioCount > 0) { m_AudioCount--; if (m_AudioCount <= 0) { LOGDBG("PlayAudio detected radio stream"); ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream); } } } return true; } int cXinelibDevice::PlayVideo(const uchar *buf, int length) { TRACEF("cXinelibDevice::PlayVideo"); TRACK_TIME(100); if (!AcceptVideoPacket(buf, length)) return length; if (!DATA_IS_PES(buf)) { LOGMSG("PlayVideo: data is not PES !"); return length; } if(m_StreamStart) { bool h264 = pes_is_frame_h264(buf, length); if (h264) { LOGMSG("cXinelibDevice::PlayVideo: Detected H.264 video"); } if (pes_get_video_size(buf, length, m_VideoSize, h264)) { m_StreamStart = false; LOGDBG("Detected video size %dx%d", m_VideoSize->width, m_VideoSize->height); ForEach(m_clients, &cXinelibThread::SetHDMode, (m_VideoSize->width > 800)); } } #ifdef DEBUG_SWITCHING_TIME if(m_statusMonitor->switchtimeOff && m_statusMonitor->switchtimeOn) { if (pes_get_picture_type(buf, length) == I_FRAME) m_statusMonitor->IFrame(); } #endif return PlayAny(buf, length); } void cXinelibDevice::StillPicture(const uchar *Data, int Length) { TRACEF("cXinelibDevice::StillPicture"); // skip still images coming in too fast (ex. when moving cutting marks) if(cRemote::HasKeys()) { static int skipped = 0; static uint64_t lastshow = 0; uint64_t now = cTimeMs::Now(); if(now - lastshow < 500) { skipped++; //LOGMSG("Skipping still image (coming in too fast)"); return; } LOGDBG("Forcing still image - skipped %d images", skipped); lastshow = now; skipped = 0; } // Data type bool isPes = DATA_IS_PES(Data) && ((Data[3] & 0xF0) == 0xE0); bool isMpeg1 = isPes && ((Data[6] & 0xC0) != 0x80); bool isH264 = isPes && pes_is_frame_h264(Data, Length); bool isTs = DATA_IS_TS(Data); int i; if(m_PlayingFile && (m_PlayingFile == pmAudioVideo || m_PlayingFile == pmVideoOnly)) return; TsBufferFlush(); ForEach(m_clients, &cXinelibThread::Clear); ForEach(m_clients, &cXinelibThread::SetNoVideo, false); ForEach(m_clients, &cXinelibThread::SetLiveMode, false); ForEach(m_clients, &cXinelibThread::ResumeOutput); ForEach(m_clients, &cXinelibThread::SetStillMode, true); ForEach(m_clients, &cXinelibThread::Sync); m_TrickSpeed = -1; // to make Poll work ... m_SkipAudio = 1; // enables audio and pts stripping for (i = 0; i < STILLPICTURE_REPEAT_COUNT; i++) { if(isMpeg1) { ForEach(m_clients, &cXinelibThread::Play_Mpeg1_PES, Data, Length, &mmin<int>, Length); } else if(isPes) { /*cDevice::*/PlayPes(Data, Length, m_SkipAudio); } else if(isTs) { int written = 0, total = (Length/TS_SIZE)*TS_SIZE; while (written < total) { int n = PlayTs(Data+written, Length-written, m_SkipAudio); if (n > 0) written += n; else cCondWait::SleepMs(5); } TsBufferFlush(); } else { ForEach(m_clients, &cXinelibThread::Play_Mpeg2_ES, Data, Length, VIDEO_STREAM, isH264, &mand<bool>, true); } } TsBufferFlush(); #if 0 // creates empty video PES with pseudo-pts ForEach(m_clients, &cXinelibThread::Play_Mpeg2_ES, Data, 0, VIDEO_STREAM, isH264, &mand<bool>, true); #endif ForEach(m_clients, &cXinelibThread::Flush, 60, &mand<bool>, true); m_TrickSpeed = 0; // --> Play() triggers TrickSpeed change and resets still mode m_SkipAudio = 0; } int cXinelibDevice::PlayAudio(const uchar *buf, int length, uchar Id) { TRACEF("cXinelibDevice::PlayAudio"); TRACK_TIME(100); if (!AcceptAudioPacket(buf, length)) return length; return PlayAny(buf, length); } int cXinelibDevice::PlaySubtitle(const uchar *Data, int Length) { if(!xc.dvb_subtitles) return cDevice::PlaySubtitle(Data, Length); return PlayAny(Data, Length); } bool cXinelibDevice::Poll(cPoller &Poller, int TimeoutMs) { TRACEF("cXinelibDevice::Poll"); TRACK_TIME(400); if(m_PlayingFile == pmAudioVideo) return true; if(m_TrickSpeed == 0) { cCondWait::SleepMs(min(TimeoutMs, 20)); return Poller.Poll(0); } if(!m_local && !m_server) { /* nothing to do... why do I exist ... ? */ //cCondWait::SleepMs(TimeoutMs); //return Poller.Poll(0); return true; } if(m_FreeBufs < 1) { int result = DEFAULT_POLL_SIZE; if(m_local) result = min(result, m_local->Poll(Poller, TimeoutMs)); if(m_server) result = min(result, m_server->Poll(Poller, TimeoutMs)); m_FreeBufs = max(result, 0); } return m_FreeBufs > 0 /*|| Poller.Poll(0)*/; } // // Audio facilities // void cXinelibDevice::SetVolumeDevice(int Volume) { TRACEF("cXinelibDevice::SetVolumeDevice"); ForEach(m_clients, &cXinelibThread::SetVolume, Volume); } void cXinelibDevice::SetAudioTrackDevice(eTrackType Type) { TRACEF("cXinelibDevice::SetAudioTrackDevice"); // track changes are autodetected at xine side } void cXinelibDevice::SetAudioChannelDevice(int AudioChannel) { TRACEF("cXinelibDevice::SetAudioChannelDevice"); if(m_AudioChannel != AudioChannel) { m_AudioChannel = AudioChannel; //LOGDBG("cXinelibDevice::SetAudioChannelDevice --> %d", AudioChannel); #if 0 switch(AudioChannel) { default: //case 0: ConfigurePostprocessing("upmix_mono", false, NULL); case 0: ConfigurePostprocessing("upmix_mono", true, "channel=-1"); break; case 1: ConfigurePostprocessing("upmix_mono", true, "channel=0"); break; case 2: ConfigurePostprocessing("upmix_mono", true, "channel=1"); break; } #else switch(AudioChannel) { default: case 0: ConfigurePostprocessing("audiochannel", false, NULL); break; case 1: ConfigurePostprocessing("audiochannel", true, "channel=0"); break; case 2: ConfigurePostprocessing("audiochannel", true, "channel=1"); break; } #endif } } void cXinelibDevice::SetDigitalAudioDevice(bool On) { TRACEF("cXinelibDevice::SetDigitalAudioDevice"); // track changes are autodetected at xine side } // // Video format facilities // void cXinelibDevice::SetVideoFormat(bool VideoFormat16_9) { TRACEF("cXinelibDevice::SetVideoFormat"); cDevice::SetVideoFormat(VideoFormat16_9); #if 0 // // TODO // if(xc.aspect != ASPECT_AUTO && xc.aspect != ASPECT_DEFAULT) { if(VideoFormat16_9) xc.aspect = ASPECT_16_9; else if(xc.aspect == ASPECT_16_9) xc.aspect = ASPECT_4_3; ConfigureDecoder(,,,xc.aspect,,,); } #endif } void cXinelibDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) { TRACEF("cXinelibDevice::SetVideoDisplayFormat"); cDevice::SetVideoDisplayFormat(VideoDisplayFormat); #if 0 // // TODO // // - set normal, pan&scan, letterbox (only for 4:3?) // if(xc.aspect != ASPECT_AUTO && xc.aspect != ASPECT_DEFAULT) { switch(VideoDisplayFormat) { case vdfPanAndScan: xc.aspect = ASPECT_PAN_SCAN; break; case vdfLetterBox: xc.aspect = ASPECT_4_3; /* borders are added automatically if needed */ break; case vdfCenterCutOut: xc.aspect = ASPECT_CENTER_CUT_OUT; break; } ConfigureDecoder(,,,xc.aspect,,,); } #endif } #if VDRVERSNUM < 20301 eVideoSystem cXinelibDevice::GetVideoSystem(void) { TRACEF("cXinelibDevice::GetVideoSystem"); return cDevice::GetVideoSystem(); } #endif void cXinelibDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect) { Width = m_VideoSize->width; Height = m_VideoSize->height; VideoAspect = 1.0; if (m_VideoSize->pixel_aspect.den) { VideoAspect = (double)m_VideoSize->pixel_aspect.num / (double)m_VideoSize->pixel_aspect.den; VideoAspect *= (double)Width / (double)Height; } } void cXinelibDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) { switch (xc.osd_size) { case OSD_SIZE_720x576: Width = 720; Height = 576; break; case OSD_SIZE_1280x720: Width = 1280; Height = 720; break; case OSD_SIZE_1920x1080: Width = 1920; Height = 1080; break; case OSD_SIZE_stream: if (m_VideoSize->width > 0 && m_VideoSize->height > 0) { Width = max<int>(m_VideoSize->width, 512); Height = max<int>(m_VideoSize->height, 480); break; } case OSD_SIZE_auto: if (xc.osd_width_auto > 0 && xc.osd_height_auto > 0) { Width = xc.osd_width_auto; Height = xc.osd_height_auto; break; } case OSD_SIZE_custom: default: Width = xc.osd_width; Height = xc.osd_height; break; } PixelAspect = 16.0 / 9.0 / (double)Width * (double)Height; } bool cXinelibDevice::SupportsTrueColorOSD(void) { switch (xc.osd_color_depth) { default: case OSD_DEPTH_LUT8: return false; case OSD_DEPTH_TRUECOLOR: return true; case OSD_DEPTH_auto:; } // Automatic mode: // Use TrueColor if all clients support it. // Special handling when no remote clients: // - local frontend supports TrueColor --> use TrueColor // - no local frontend --> use LUT8 if (m_local) { if (!m_local->SupportsTrueColorOSD()) return false; return !m_server || !!m_server->SupportsTrueColorOSD(); } return m_server && (m_server->SupportsTrueColorOSD() == 1); } cRect cXinelibDevice::CanScaleVideo(const cRect &Rect, int Alignment) { return Rect; } void cXinelibDevice::ScaleVideo(const cRect &Rect) { if (m_VideoWindow != Rect) { m_VideoWindow = Rect; cXinelibOsdProvider::RefreshOsd(); } } // // SPU decoder // #ifdef FORWARD_DVD_SPUS # include "spu_decoder.h" #endif cSpuDecoder *cXinelibDevice::GetSpuDecoder(void) { TRACEF("cXinelibDevice::GetSpuDecoder"); if (!m_spuDecoder && IsPrimaryDevice()) { // // TODO // // - use own derived SpuDecoder with special cXinelibOsd // -> always visible // #ifdef FORWARD_DVD_SPUS // forward DVD SPUs to xine without decoding m_spuDecoder = new cFwdSpuDecoder(this); #else m_spuDecoder = new cDvbSpuDecoder(); #endif } return m_spuDecoder; } // // Image Grabbing // uchar *cXinelibDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { TRACEF("cXinelibDevice::GrabImage"); if(m_local) return m_local->GrabImage(Size, Jpeg, Quality, SizeX, SizeY); if(m_server) return m_server->GrabImage(Size, Jpeg, Quality, SizeX, SizeY); return NULL; } // // Available DVD SPU tracks // void cXinelibDevice::SetSubtitleTrackDevice(eTrackType Type) { if (m_PlayingFile == pmAudioVideo || m_PlayingFile == pmVideoOnly) ForEach(m_clients, &cXinelibThread::SetSubtitleTrack, Type); } // // Metainfo // const char *cXinelibDevice::GetMetaInfo(eMetainfoType Type) { if(Type >= 0 && Type < mi_Count) { if ((Type == miTitle) || (Type == miTracknumber && xc.playlist_tracknumber == 1) || (Type == miArtist && xc.playlist_artist == 1) || (Type == miAlbum && xc.playlist_album == 1) || (Type > miAlbum)) { return m_MetaInfo[Type]; } return ""; } LOGMSG("cXinelibDevice::GetMetaInfo: unknown metainfo type"); return ""; } void cXinelibDevice::SetMetaInfo(eMetainfoType Type, const char *Value) { if(Type >= 0 && Type < mi_Count) { /* set to 0 first, so if player is accessing string in middle of copying it will always be 0-terminated (but truncated) */ memset(m_MetaInfo[Type], 0, sizeof(m_MetaInfo[Type])); strn0cpy(m_MetaInfo[Type], Value, MAX_METAINFO_LEN); } else { LOGMSG("cXinelibDevice::SetMetaInfo: unknown metainfo type"); } } // // Picture-In-Picture // int cXinelibDevice::Pip_Open(void) { for (int i = 0; i < MAX_NUM_PIP; i++) if (!m_PipPid[i]) { m_PipPid[i] = i; ForEach(m_clients, &cXinelibThread::Pip_Config, i, -1, -1, -1, -1); return i; } return -1; } void cXinelibDevice::Pip_Config(int Index, int X, int Y, int W, int H) { if (Index >= 0 && Index < MAX_NUM_PIP) { if (m_PipPid[Index]) { ForEach(m_clients, &cXinelibThread::Pip_Config, Index, X, Y, W, H); } } } int cXinelibDevice::Pip_Play(int Index, uint8_t *Data, int Length) { if (Index >= 0 && Index < MAX_NUM_PIP) { if (m_PipPid[Index] && Data && Length > 0) { int len = Length; if (m_local) len = m_local->Play(Data, len, eStreamId(sidPipFirst + Index)); if (m_server) { int len2 = m_server->Play(Data, len, eStreamId(sidPipFirst + Index)); if (!m_local) return len2; } return len; } } return Length; } void cXinelibDevice::Pip_Close(int Index) { if (Index >= 0 && Index < MAX_NUM_PIP) { if (m_PipPid[Index]) { ForEach(m_clients, &cXinelibThread::Pip_Close, Index); m_PipPid[Index] = 0; } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/configure�����������������������������������������������������������������������0000755�0001750�0001750�00000027761�13061253352�014150� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # vdr-xinelibout configure script # # Copyright (c) Petri Hintukainen 2008 # # See the main source file 'xineliboutput.c' for copyright information and # how to reach the author. # # * $Id$ # PKG_CONFIG="pkg-config" makefile="config.mak" header="features.h" logfile="configure.log" debug=no CC="cc" CXX="g++" CFLAGS="" # # tools # toupper(){ echo "$@" | tr abcdefghijklmnopqrstuvwxyz- ABCDEFGHIJKLMNOPQRSTUVWXYZ_ } die(){ log "$@" exit 1 } log(){ echo "$@" echo "$@" >> $logfile } logdbg(){ [ x$debug = xyes ] && log "$@" || echo "$@" >> $logfile } not_in_list(){ key=$1 shift for item in $*; do test $key = $item && return 1 done return 0 } add_flag(){ eval not_in_list $flag \$$flags_list || return 1 logdbg "adding $flag to $flags_list" eval $flags_list=\"\$${flags_list} ${flag}\" } add_flags(){ flags_list=$1 shift for flag in $*; do add_flag $flags_list $flag done } # # enable/disable # set_opt(){ eval HAVE_$(toupper $1)=$2 } set_opts(){ optvalue=$1 shift for optname in $*; do set_opt $optname $optvalue done } enable(){ set_opts yes $* } disable(){ set_opts no $* } enabled(){ ucfeature=$(toupper $1) eval test "x\$HAVE_${ucfeature}" = "xyes" } disabled(){ ucfeature=$(toupper $1) eval test "x\$HAVE_${ucfeature}" = "xno" } # # compile/link tests # generate_test_c(){ hdrname="$1" subsys="$2" func="$3" if test x"$subsys" = xX11 ; then cat <<EOF >testhdr.c #include <X11/Xlib.h> #include <$hdrname> int main(int c, char **v) { $func; return 0; } EOF else cat <<EOF >testhdr.c #include <stdio.h> #include <$hdrname> int main(int c, char **v) { $func; } EOF fi } test_library_c(){ log -n "Checking for $libname ... " generate_test_c "$hdr" "$subsys" "$func" $CC -g testhdr.c -o testhdr $CFLAGS $inc $lib >> $logfile 2>&1 err=$? if test $err = 0; then log "yes" add_flags LIBS_$subsys $lib add_flags CFLAGS_$subsys $inc else log "no" logdbg "--------" logdbg "/* $CC -g testhdr.c -o testhdr $CFLAGS $inc $lib */" logdbg `cat testhdr.c` logdbg "--------" fi rm -f testhdr.c testhdr return $err } # # pkg-config tests # test_pkgconfig(){ disabled pkgconfig && return 1 log -n "Checking for $PKG_CONFIG ... " disable pkgconfig $PKG_CONFIG --version>/dev/null && enable pkgconfig log $HAVE_PKGCONFIG } test_library_pc(){ subsys="$1" libname="$2" log -n "Checking for pkg-config $libname ... " if $PKG_CONFIG --exists $libname; then if $PKG_CONFIG --libs $libname >/dev/null; then add_flags LIBS_$subsys \ `pkg-config --libs-only-L $libname` \ `pkg-config --libs-only-l $libname` add_flags CFLAGS_$subsys `pkg-config --cflags-only-I $libname` log "yes" return 0 fi fi log "no" return 1 } # # generic test # test_library(){ subsys="$1" libname="$2" hdr="$3" lib="$4" func="$5" inc="$6" feature=$(toupper $libname) # do not test if disabled from command-line if disabled $feature; then log "Not checking for $libname" disable $feature return 1 fi disable $feature # try pkg-config first if enabled pkgconfig; then test_library_pc "$subsys" "$libname" && enable "$feature" fi # compile/link test as fallback if disabled $feature; then test_library_c "$subsys" "$libname" "$hdr" "$lib" "$func" "$inc" && enable $feature fi } # # configurable features # SUBSYSTEMS=" x11 fb vdr libxine " FEATURES=" $SUBSYSTEMS libextractor libavutil libjpeg dbus_glib_1 xshm xdpms xinerama xrandr xrender xshape opengl pthread dlfcn vdpau i18n libcap libbluray libcec mce-dbus-names avahi-client " # set defaults enable x11 vdr fb xine i18n libcec # clear log file rm -f $logfile # # Parse command-line arguments # show_help(){ echo "Usage: configure [options]" echo "Options: [defaults in brackets after descriptions]" echo echo " --help print this message" echo " --enable-x11 build X11 frontend (vdr-sxfe) [yes]" echo " --enable-fb build framebuffer frontend (vdr-fbfe) [yes]" echo " --enable-vdr build VDR plugin [yes]" echo " --enable-libxine build xine plugins [yes]" echo echo " --disable-libextractor disable libextractor support (media file metainfo) [no]" echo " --disable-libbluray disable libbluray support (BluRay metainfo) [no]" echo " --disable-libcec disable libcec support (HDMI-CEC remote control) [no]" echo " --disable-libjpeg disable libjpeg support [no]" echo " --disable-dbus-glib-1 disable dbus-glib support [no]" echo " --disable-xshm disable XShm support [no]" echo " --disable-xdpms disable Xdpms support [no]" echo " --disable-xinerama disable Xinerama support [no]" echo " --disable-xrandr disable Xrandr support [no]" echo " --disable-xrender disable Xrender support (HUD OSD) [no]" echo " --disable-xshape disable Xshape support (non-transparent HUD OSD without composite manager) [no]" echo " --disable-opengl disable OpenGL support (transparent HUD OSD without composite manager) [no]" echo " --disable-vdpau disable VDPAU support (X11) [no]" echo " --disable-i18n disable i18n support [no]" echo " --disable-libcap disable libcap support [no]" echo " --disable-avahi-client disable avahi support [no]" echo echo " --debug debug configure script" echo " --disable-pkgconfig do not use pkg-config" echo " --cc=CC select C compiler" echo " --cxx=CXX select C++ compiler" echo " --add-cflags=FLAGS add compiler flags" } for opt do optval="${opt#*=}" logdbg "Command line: $opt [$optval]" case "$opt" in --help) show_help && die ;; --debug) debug=yes logdbg "Debug mode" ;; --cc=?*) CC=$optval logdbg "C compiler: $CC" ;; --cxx=?*) CXX=$optval logdbg "C++ compiler: $CXX" ;; --add-cflags=?*) CFLAGS="$CFLAGS $optval" logdbg "CFLAGS: $CFLAGS" ;; --disable-pkgconfig) disable pkgconfig logdbg "Disabled pkg-config" ;; --enable-?*|--disable-?*) eval `echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g'` logdbg " $action $option" not_in_list $option $FEATURES && die "unknown option $opt" eval $action \${option} ;; -*) die "unknown option $opt" ;; esac done # # maintain deps # check_deps(){ disabled libxine && disable x11 fb libavutil libjpeg libcec disabled x11 && disable dbus-glib-1 xshm xrandr xrender xshape opengl xdpms xinerama vdpau disabled vdr && disable libextractor libcap libbluray avahi-client disabled dlfcn && disable opengl disabled pthread && disable opengl disabled xrender && disable xshape xshm } check_deps # # Run the tests # test_pkgconfig # fake test that should fail [ $debug = yes ] && \ test_library X11 do_error "none.h" "-lnolib" test_library VDR libextractor "extractor.h" "-lextractor" "EXTRACTOR_getKeywords(0,0)" test_library VDR libcap "sys/capability.h" "-lcap" "cap_get_proc()" test_library VDR libbluray "libbluray/bluray.h" "-lbluray" "bd_get_disc_info(0)" test_library VDR avahi-client "avahi-client/publish.h" "-lavahi-common -lavahi-client" "avahi_client_new" test_library XINE libxine "xine.h" "-lxine" "xine_init(0)" test_library DLFCN dlfcn "dlfcn.h" "-ldl" "dlopen(0,0)" if enabled libxine; then log -n "Checking for xine plugin directory ..." if enabled pkgconfig && $PKG_CONFIG libxine --atleast-version 1.1.17; then XINEPLUGINDIR=`$PKG_CONFIG libxine --variable=plugindir` else XINEPLUGINDIR=`xine-config --plugindir` fi log " $XINEPLUGINDIR" test_library AVUTIL libavutil "libavutil/mem.h" "-lavutil" "av_mallocz(1)" test_library JPEG libjpeg "jpeglib.h" "-ljpeg" "jpeg_create_compress(0)" test_library X11 x11 "X11/X.h" "-lX11" "XInitThreads()" test_library PTHREAD pthread "pthread.h" "-lpthread" "pthread_create(0,0,0,0)" if enabled libcec; then test_library CEC libcec "libcec/cecc.h" "-lcec" "libcec_initialise(0)" if disabled libcec; then # try libcec < 3.0.0 enable libcec test_library CEC libcec "libcec/cecc.h" "-lcec" "cec_initialise(0)" fi fi if enabled x11; then test_library X11 xext "X11/extensions/Xext.h" "-lXext" "" test_library X11 xshm "X11/extensions/XShm.h" "-lXext" "XShmQueryExtension(0)" test_library X11 xrandr "X11/extensions/Xrandr.h" "-lXrandr" "XRRGetScreenInfo(0,0)" test_library X11 xrender "X11/extensions/Xrender.h" "-lXrender" "XRenderQueryFormats(0)" test_library X11 xshape "X11/extensions/shape.h" "-lXext" "XShapeQueryExtension(0,0,0)" test_library X11 xdpms "X11/extensions/dpms.h" "-lXext" "DPMSDisable(0)" test_library X11 xinerama "X11/extensions/Xinerama.h" "-lXinerama" "XineramaQueryScreens(0,0)" test_library X11 opengl "GL/glx.h" "-lGL -lGLU" "glXQueryVersion(0,0,0)" test_library none vdpau "vdpau/vdpau_x11.h" "-lvdpau" "vdp_device_create_x11(0,0,0,0)" test_library X11 dbus-glib-1 \ "dbus/dbus-glib.h" \ "-ldbus-glib-1 -lgobject-2.0 -lglib-2.0" \ "dbus_g_bus_get(0,0)" \ "-I/usr/include/dbus-1.0 -I/usr/lib64/dbus-1.0/include -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/lib/glib-2.0/include" enabled dbus-glib-1 && test_library X11 mce-dbus-names "mce/dbus-names.h" "" "" fi fi check_deps # need -lm for ceil/floor in HUD OSD enabled xrender && add_flags "LIBS_X11" "-lm" # need -ldl and -lpthread with opengl enabled opengl && add_flags "LIBS_X11" $LIBS_DLFCN $LIBS_PTHREAD # need -ldl for dlopen() in vdr plugin enabled vdr && add_flags "LIBS_VDR" $LIBS_DLFCN # # Print results # log log "Enabled features:" for feature in $FEATURES; do enabled $feature && log " $feature" done log "Disabled features:" for feature in $FEATURES; do enabled $feature || log " $feature" done log # # create features.h # log "Creating $header ..." cat <<EOF >$header /* Automatically generated by configure - do not modify! */ #ifndef XINELIBOUTPUT_FEATURES_H #define XINELIBOUTPUT_FEATURES_H EOF for feature in $FEATURES; do enabled $feature && echo "#define HAVE_$(toupper $feature) 1">>$header || \ echo "#undef HAVE_$(toupper $feature)">>$header done echo "" >> $header echo "#endif /* XINELIBOUTPUT_FEATURES_H */" >> $header # # create config.mak # log "Creating $makefile ..." echo "# Automatically generated by configure - do not modify!" > $makefile echo >> $makefile # subsystems echo "XINELIBOUTPUT_VDRPLUGIN=$HAVE_VDR" >> $makefile echo "XINELIBOUTPUT_XINEPLUGIN=$HAVE_LIBXINE" >> $makefile echo "XINELIBOUTPUT_X11=$HAVE_X11" >> $makefile echo "XINELIBOUTPUT_FB=$HAVE_FB" >> $makefile echo >> $makefile # features for feature in $FEATURES; do feature="`toupper $feature`" enabled $feature && echo "HAVE_$feature=yes">>$makefile || echo "HAVE_$feature=no">>$makefile done echo >> $makefile # xine plugin dir enabled libxine && echo "XINEPLUGINDIR=$XINEPLUGINDIR" >> $makefile && echo >> $makefile # cc/ld flags echo "CC = $CC">>$makefile echo "CXX = $CXX">>$makefile echo "CFLAGS_XINE += $CFLAGS_XINE">>$makefile echo "CFLAGS_VDR += $CFLAGS_VDR">>$makefile echo "CFLAGS_X11 += $CFLAGS_X11">>$makefile echo "CFLAGS_AVUTIL += $CFLAGS_AVUTIL">>$makefile echo "LIBS_XINE += $LIBS_XINE">>$makefile echo "LIBS_JPEG += $LIBS_JPEG">>$makefile echo "LIBS_AVUTIL += $LIBS_AVUTIL">>$makefile echo "LIBS_PTHREAD += $LIBS_PTHREAD">>$makefile echo "LIBS_VDR += $LIBS_VDR">>$makefile echo "LIBS_X11 += $LIBS_X11">>$makefile echo "LIBS_CEC += $LIBS_CEC">>$makefile ���������������xineliboutput-2.0.0/config.h������������������������������������������������������������������������0000644�0001750�0001750�00000045404�13061253352�013651� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * config.h: Global configuration and user settings * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #ifndef _XINELIB_CONFIG_H_ #define _XINELIB_CONFIG_H_ #include "features.h" #include <string.h> #include <stdint.h> #include <vdr/tools.h> class cOsdObject; // Max number of remote clients #define MAXCLIENTS 10 // Decoder buffer size #define PES_BUFFERS_CUSTOM 0 #define PES_BUFFERS_TINY_50 1 #define PES_BUFFERS_SMALL_250 2 #define PES_BUFFERS_MEDIUM_500 3 #define PES_BUFFERS_LARGE_1000 4 #define PES_BUFFERS_HUGE_2000 5 #define PES_BUFFERS_count 6 // Output window aspect ratio #define ASPECT_AUTO 0 #define ASPECT_DEFAULT 1 #define ASPECT_4_3 2 #define ASPECT_16_9 3 #define ASPECT_16_10 4 #define ASPECT_PAN_SCAN 5 #define ASPECT_CENTER_CUT_OUT 6 #define ASPECT_count 7 // VIDEO OUTPUT ASPECT RATIO #define VO_ASPECT_AUTO 0 #define VO_ASPECT_SQUARE 1 /* 1:1 */ #define VO_ASPECT_4_3 2 /* 4:3 */ #define VO_ASPECT_ANAMORPHIC 3 /* 16:9 */ #define VO_ASPECT_DVB 4 /* 2.11:1 */ #define VO_ASPECT_count 5 // De-interlace method #define DEINTERLACE_NONE 0 #define DEINTERLACE_BOB 1 #define DEINTERLACE_WEAVE 2 #define DEINTERLACE_GREEDY 3 #define DEINTERLACE_ONEFIELD 4 #define DEINTERLACE_ONEFIELD_XV 5 #define DEINTERLACE_LINEARLEND 6 #define DEINTERLACE_TVTIME 7 #define DEINTERLACE_count 8 // Audio driver #define AUDIO_DRIVER_AUTO 0 #define AUDIO_DRIVER_ALSA 1 #define AUDIO_DRIVER_OSS 2 #define AUDIO_DRIVER_NONE 3 #define AUDIO_DRIVER_ESD 4 #define AUDIO_DRIVER_JACK 5 #define AUDIO_DRIVER_count 6 // Video driver #define X11_DRIVER_AUTO 0 #define X11_DRIVER_XSHM 1 #define X11_DRIVER_XV 2 #define X11_DRIVER_VAAPI 3 #define X11_DRIVER_VDPAU 4 #define X11_DRIVER_OPENGL 5 #define X11_DRIVER_XVMC 6 #define X11_DRIVER_XXMC 7 #define X11_DRIVER_SDL 8 #define X11_DRIVER_DIRECTFB 9 #define X11_DRIVER_VIDIX 10 #define X11_DRIVER_NONE 11 #define X11_DRIVER_count 12 #define FB_DRIVER_AUTO 0 #define FB_DRIVER_FB 1 #define FB_DRIVER_DIRECTFB 2 #define FB_DRIVER_SDL 3 #define FB_DRIVER_VIDIXFB 4 #define FB_DRIVER_DXR3 5 #define FB_DRIVER_NONE 6 #define FB_DRIVER_count 7 // Local frontend #define FRONTEND_X11 0 #define FRONTEND_FB 1 #define FRONTEND_NONE 2 #define FRONTEND_count 3 #define DEFAULT_FRONTEND "sxfe" #define LISTEN_PORT 37890 #define LISTEN_PORT_S "37890" #define DISCOVERY_PORT 37890 #define AUDIO_EQ_30HZ 0 #define AUDIO_EQ_60HZ 1 #define AUDIO_EQ_125HZ 2 #define AUDIO_EQ_250HZ 3 #define AUDIO_EQ_500HZ 4 #define AUDIO_EQ_1000HZ 5 #define AUDIO_EQ_2000HZ 6 #define AUDIO_EQ_4000HZ 7 #define AUDIO_EQ_8000HZ 8 #define AUDIO_EQ_16000HZ 9 #define AUDIO_EQ_count 10 #define AUDIO_VIS_NONE 0 #define AUDIO_VIS_GOOM 1 #define AUDIO_VIS_IMAGE 5 #define AUDIO_VIS_count 6 /* speaker arrangements: xine, audio_out_alsa.c */ #define SPEAKERS_MONO 0 #define SPEAKERS_STEREO 1 #define SPEAKERS_HEADPHONES 2 #define SPEAKERS_SURROUND21 3 #define SPEAKERS_SURROUND3 4 #define SPEAKERS_SURROUND4 5 #define SPEAKERS_SURROUND41 6 #define SPEAKERS_SURROUND5 7 #define SPEAKERS_SURROUND51 8 #define SPEAKERS_SURROUND6 9 #define SPEAKERS_SURROUND61 10 #define SPEAKERS_SURROUND71 11 #define SPEAKERS_A52_PASSTHRU 12 #define SPEAKERS_count 13 #define SUBTITLESIZE_count 7 // OSD blending methods #define OSD_BLENDING_SOFTWARE 0 // xine-lib "normal" osd #define OSD_BLENDING_HARDWARE 1 // xine-lib "unscaled osd" #define OSD_BLENDING_count 2 // OSD depth #define OSD_DEPTH_auto 0 #define OSD_DEPTH_LUT8 1 #define OSD_DEPTH_TRUECOLOR 2 #define OSD_DEPTH_count 3 // OSD layers mixing #define OSD_MIXER_NONE 0 #define OSD_MIXER_GRAY 1 #define OSD_MIXER_ALPHA 2 #define OSD_MIXER_GRAYALPHA 3 // OSD_MIXER_GRAY | OSD_MIXER_ALPHA #define OSD_MIXER_FULL 4 #define OSD_MIXER_count 5 // OSD scaling modes #define OSD_SCALING_NONE 0 #define OSD_SCALING_NEAREST 1 #define OSD_SCALING_BILINEAR 2 #define OSD_SCALING_count 3 // OSD size #define OSD_SIZE_auto 0 // frontend display resolution #define OSD_SIZE_720x576 1 #define OSD_SIZE_1280x720 2 #define OSD_SIZE_1920x1080 3 #define OSD_SIZE_custom 4 #define OSD_SIZE_stream 5 #define OSD_SIZE_count 6 // Media player menu (bitmask) #define MEDIA_MENU_FILES (1<<0) #define MEDIA_MENU_MUSIC (1<<1) #define MEDIA_MENU_IMAGES (1<<2) #define MEDIA_MENU_DVD (1<<3) #define MEDIA_MENU_CD (1<<4) #define MEDIA_MENU_BLURAY (1<<5) #define MEDIA_MENU_VIDEO_SETUP (1<<6) #define MEDIA_MENU_AUDIO_SETUP (1<<7) // Video decoder #define DECODER_MPEG2_auto 0 /* use value from frontend config_xineliboutput */ #define DECODER_MPEG2_LIBMPEG2 1 #define DECODER_MPEG2_FFMPEG 2 #define DECODER_MPEG2_count 3 #define DECODER_H264_auto 0 /* use value from frontend config_xineliboutput */ #define DECODER_H264_FFMPEG 1 #define DECODER_H264_COREAVC 2 #define DECODER_H264_count 3 #define FF_H264_SKIP_LOOPFILTER_auto 0 /* use value from frontend config_xineliboutput */ #define FF_H264_SKIP_LOOPFILTER_DEFAULT 1 #define FF_H264_SKIP_LOOPFILTER_NONE 2 #define FF_H264_SKIP_LOOPFILTER_NONREF 3 #define FF_H264_SKIP_LOOPFILTER_BIDIR 4 #define FF_H264_SKIP_LOOPFILTER_NONKEY 5 #define FF_H264_SKIP_LOOPFILTER_ALL 6 #define FF_H264_SKIP_LOOPFILTER_count 7 #define FF_H264_SPEED_OVER_ACCURACY_auto 0 /* use value from frontend config_xineliboutput */ #define FF_H264_SPEED_OVER_ACCURACY_no 1 #define FF_H264_SPEED_OVER_ACCURACY_yes 2 #define FF_H264_SPEED_OVER_ACCURACY_count 3 #define HIDDEN_OPTION(opt) \ (xc.IsOptionHidden(xc.opt)) #define READONLY_OPTION(opt) \ (xc.IsOptionReadOnly(xc.opt)) #define DEFAULT_POLL_SIZE 16 typedef enum { ShowMenu = 0, ShowEq = 1, ShowFiles = 2, ShowMusic = 3, ShowImages = 4, CloseOsd = 5 } eMainMenuMode; class config_t { public: static const char * const s_bufferSize [PES_BUFFERS_count + 1]; static const int i_pesBufferSize [PES_BUFFERS_count + 1]; static const char * const s_aspects [ASPECT_count + 1]; static const char * const s_vo_aspects [VO_ASPECT_count + 1]; static const char * const s_deinterlaceMethods [DEINTERLACE_count + 1]; static const char * const s_deinterlaceMethodNames [DEINTERLACE_count + 1]; static const char * const s_audioDriverNames [AUDIO_DRIVER_count + 1]; static const char * const s_audioDrivers [AUDIO_DRIVER_count + 1]; static const char * const s_videoDriverNamesX11 [X11_DRIVER_count + 1]; static const char * const s_videoDriversX11 [X11_DRIVER_count + 1]; static const char * const s_videoDriverNamesFB [FB_DRIVER_count + 1]; static const char * const s_videoDriversFB [FB_DRIVER_count + 1]; static const char * const s_frontendNames [FRONTEND_count + 1]; static const char * const s_frontends [FRONTEND_count + 1]; static const char * const s_frontend_files [FRONTEND_count + 1]; static const char * const s_audioEqNames [AUDIO_EQ_count + 1]; static const char * const s_audioVisualizations [AUDIO_VIS_count + 1]; static const char * const s_audioVisualizationNames[AUDIO_VIS_count + 1]; static const char * const s_speakerArrangements [SPEAKERS_count + 1]; static const char * const s_subtitleSizes [SUBTITLESIZE_count + 1]; static const char * const s_osdBlendingMethods [OSD_BLENDING_count + 1]; static const char * const s_osdMixers [OSD_MIXER_count + 1]; static const char * const s_osdScalings [OSD_SCALING_count + 1]; static const char * const s_osdSizes [OSD_SIZE_count + 1]; static const char * const s_osdColorDepths [OSD_DEPTH_count + 1]; static const char * const s_decoders_MPEG2 [DECODER_MPEG2_count + 1]; static const char * const s_decoders_H264 [DECODER_H264_count + 1]; static const char * const s_ff_skip_loop_filters [FF_H264_SKIP_LOOPFILTER_count + 1]; static const char * const s_ff_speed_over_accuracy [FF_H264_SPEED_OVER_ACCURACY_count + 1]; static const char * const s_subExts[]; public: // Force xineliboutput to be the primary device int force_primary_device; // Use vdr-suspendoutput: suspend when there are no clients int use_suspendoutput; // OSD state eMainMenuMode main_menu_mode; // used internally to open right sub-menu cOsdObject *pending_menu_action; // used to replace current OSD with another type of OSD object time_t last_hotkey_time; int/*eKeys*/ last_hotkey; // local frontend settings char local_frontend[64]; char video_driver[32]; char video_port[32]; // X11: DISPLAY=... char audio_driver[32]; char audio_port[64]; char *post_plugins; // static post plugins from command line options char *config_file; // config file from command line options int pes_buffers; char modeline[64]; int fullscreen; int modeswitch; int width; int height; int xpos; int ypos; int display_aspect; int scale_video; int exit_on_close; // Terminate VDR when local frontend is closed int use_x_keyboard; // Use X11 keyboard to control VDR (console kbd is handled by VDR) int window_id; // use existing X11 window int hud_osd; // head up display OSD int opengl; // use opengl acceleration for video and HUD OSD int truecoloreverytime; // Audio settings int speaker_type; int audio_delay; // in ms int audio_compression; // 100%(=off)...500% int audio_equalizer[AUDIO_EQ_count]; int audio_surround; // downmix multichannel audio to stereo surround int headphone; // mix audio for headphones int audio_upmix; // upmix stereo to 5.1 int sw_volume_control; // software (xine-lib) or hardware (alsa) volume control and muting // Audio visualization char audio_visualization[64]; char audio_vis_goom_opts[256]; char audio_vis_image_mrl[4096]; // Video settings int ibp_trickspeed; int max_trickspeed; int overscan; // % int hue; // 0...0xffff, -1 == off int saturation; // 0...0xffff, -1 == off int contrast; // 0...0xffff, -1 == off int brightness; // 0...0xffff, -1 == off int sharpness; // 0...0xffff, -1 == off int noise_reduction; // 0...0xffff, -1 == off int vo_aspect_ratio; // OSD settings int hide_main_menu; int osd_size; int osd_width; int osd_height; int osd_width_auto; int osd_height_auto; int osd_color_depth; int osd_mixer; // show multiple OSD layers int osd_scaling; // OSD scaling mode: off, nearest, bilinear int osd_spu_scaling; // SPU OSD scaling mode: off, nearest, bilinear int osd_blending; // OSD blending method int osd_blending_lowresvideo; // Use hardware blending for low-resolution video int alpha_correction; int alpha_correction_abs; int extsub_size; // size of separate subtitles ( -1 = xine default ; 0...6 = { tiny small normal large very large huge } int dvb_subtitles; // send DVB subtitles in data stream (decode+display using xine-lib or external media player) // Media player char media_root_dir[4096]; // restrict file browser char browse_files_dir[4096]; char browse_music_dir[4096]; char browse_images_dir[4096]; int show_hidden_files; int cache_implicit_playlists; // used in playlist.c int enable_id3_scanner; // used in playlist.c int subtitle_vpos; // used in media player. Not saved ! int playlist_tracknumber; int playlist_artist; int playlist_album; int dvd_arrow_keys_control_playback; uint media_menu_items; // enabled items in media player menu (bitmask) int media_enable_delete; // enable Delete in file browser int media_enable_resume; // deinterlacing post plugin char deinterlace_method[32]; char deinterlace_opts[256]; // ffmpeg post processing int ffmpeg_pp; // enable / disable int ffmpeg_pp_quality; // 0...6 char ffmpeg_pp_mode[256]; // automatic 4:3 letterbox -> 16:9 cropping post plugin int autocrop; // enable / disable int autocrop_autodetect; int autocrop_autodetect_rate; int autocrop_soft; int autocrop_soft_start_step; int autocrop_fixedsize; int autocrop_stabilize_time; int autocrop_subs; int autocrop_subs_detect_lifetime; int autocrop_subs_detect_stabilize_time; int autocrop_logo_width; int autocrop_use_driver_crop; int autocrop_use_avards_analysis; int autocrop_overscan_compensate; int autocrop_bar_tone_tolerance; // (video) software scaling int swscale; // enable/disable int swscale_change_aspect; // change video aspect ratio int swscale_resize; // change video size int swscale_width; // output video width int swscale_height; // output video height int swscale_downscale; // allow downscaling // sharpen / soften post plugin int unsharp; // enable / disable int unsharp_luma_matrix_width; // 3..11, should be an odd number int unsharp_luma_matrix_height; // 3..11, should be an odd number int unsharp_luma_amount; // Actually a double between -2.0 and 2.0, but handled as a int between -20 and 20 int unsharp_chroma_matrix_width; // 3..11, should be an odd number int unsharp_chroma_matrix_height; // 3..11, should be an odd number int unsharp_chroma_amount; // Actually a double between -2.0 and 2.0, but handled as a int between -20 and 20 // 3D noise reduction post plugin int denoise3d; // enable / disable int denoise3d_luma; // Actually a double between 0.0 and 10.0, but handled as a int between 0 and 100 int denoise3d_chroma; // Actually a double between 0.0 and 10.0, but handled as a int between 0 and 100 int denoise3d_time; // Actually a double between 0.0 and 10.0, but handled as a int between 0 and 100 int volnorm; // enable/disable volnorm post plugin (normalize audio volume) // Remote server settings int remote_mode; // Allow remote clients (vdr-sxfe, vdr-fbfe, ...) int listen_port; // Port of remote server char remote_local_if[32]; // Listen only on this interface char remote_local_ip[32]; // Bind locally to this IP int remote_keyboard; // Allow remote client to control VDR with keyboard, LIRC, etc. int remote_max_clients; // Max. number of clients int remote_usebcast; // Use broadcasts to find servers automatically int remote_usepipe; // enable local pipes for video transport int remote_usertp; // enable RTP multicast for video transport int remote_useudp; // enable UDP unicast for video transport int remote_usetcp; // enable TCP streams for video transport int remote_http_files; // allow http streaming of media files to xineliboutput clients // (=currently replayed media file from xineliboutput media player) // streaming is used only if client can't access file directly (nfs etc.) int remote_use_rtsp; // allow generic rtsp for primary device. needs enabled udp or rtp int remote_use_rtsp_ctrl;// allow rtsp to control primary device (play/pause/seek...) int remote_use_http; // allow generic http streaming (primary device output) int remote_use_http_ctrl;// allow http to control primary device (play/pause/seek...) // RTP parameters char remote_rtp_addr[32]; //xxx.xxx.xxx.xxx\0 int remote_rtp_port; int remote_rtp_ttl; int remote_rtp_always_on; int remote_rtp_sap; // Advanced settings int live_mode_sync; /* Sync SCR to transponder clock in live mode */ int scr_tuning; /* Fine-tune xine egine SCR (to sync video to graphics output) */ int scr_hz; /* Current SCR speed (Hz), default is 90000 */ int decoder_mpeg2; /* DECODER_MPEG2_... */ int decoder_h264; /* DECODER_H264_... */ int ff_h264_speed_over_accurancy; int ff_h264_skip_loop_filter; /* FF_H264_SKIP_LOOPFILTER_* */ config_t(); bool SetupParse(const char *Name, const char *Value); bool ProcessArgs(int argc, char *argv[]); static bool IsImageFile(const char *); static bool IsAudioFile(const char *); static bool IsVideoFile(const char *); static bool IsPlaylistFile(const char *); static bool IsDvdFolder(const char *); static bool IsBluRayFolder(const char *); static bool IsDvdImage(const char *); static bool IsBluRayImage(const char *); cString AutocropOptions(void); cString SwScaleOptions(void); cString FfmpegPpOptions(void); cString UnsharpOptions(void); cString Denoise3dOptions(void); #if 0 template<typename T> bool IsOptionHidden(T & option) { return hidden_options[(int)((long int)&option - (long int)this)];}; template<typename T> bool IsOptionReadOnly(T & option) { return readonly_options[(int)((long int)&option - (long int)this)];}; #endif protected: bool ProcessArg(const char *Name, const char *Value); static cString m_ProcessedArgs; #if 0 static uint8_t *hidden_options; static uint8_t *readonly_options; template<typename T> void HideOption(T & option) { hidden_options[(int)((long int)&option - (long int)this)] = 1;}; template<typename T> void ReadOnlyOption(T & option) { readonly_options[(int)((long int)&option - (long int)this)] = 1;}; #endif }; // Global instance extern config_t xc; // Find index of string in array of strings static inline int strstra(const char * const str, const char * const stra[], int def_index) { if(str && stra) { int i; for(i=0; stra[i]; i++) if(!strcmp(str,stra[i])) return i; } return def_index; } #endif //_XINELIB_CONFIG_H_ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/config.c������������������������������������������������������������������������0000644�0001750�0001750�00000107720�13061253352�013644� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * config.c: User settings * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id$ * */ #include "features.h" #include <stdio.h> #include <string.h> #include <stdlib.h> #include <getopt.h> #include <vdr/config.h> #include <vdr/videodir.h> #include <vdr/device.h> #include <vdr/i18n.h> #include "logdefs.h" #include "config.h" #include "xine_frontend.h" // HUD_* #define STRN0CPY(dst, src) \ do { \ strn0cpy(dst, src, sizeof(dst)); \ if(strlen(src) >= sizeof(dst)) \ LOGMSG("WARNING: Setting %s truncated to %s !", Name, dst); \ } while(0) #define DEFAULT_DEINTERLACE_OPTS "method=Linear,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1" const int config_t::i_pesBufferSize[ PES_BUFFERS_count+1 ] = { 0, 50, 250, 500, 1000, 2000, 500 }; const char * const config_t::s_bufferSize[ PES_BUFFERS_count+1 ] = { trNOOP("custom"), trNOOP("tiny"), trNOOP("small"), trNOOP("medium"), trNOOP("large"), trNOOP("huge"), NULL }; const char * const config_t::s_aspects[ ASPECT_count+1 ] = { trNOOP("automatic"), trNOOP("default"), "4:3", "16:9", "16:10", trNOOP("Pan&Scan"), trNOOP("CenterCutOut"), NULL }; const char * const config_t::s_vo_aspects[ VO_ASPECT_count+1 ] = { trNOOP("automatic"), trNOOP("square"), "4:3", trNOOP("anamorphic"), trNOOP("DVB"), NULL }; const char * const config_t::s_deinterlaceMethods[ DEINTERLACE_count+1 ] = { "none", "bob", "weave", "greedy", "onefield", "onefield_xv", "linearblend", "tvtime", NULL }; const char * const config_t::s_deinterlaceMethodNames[ DEINTERLACE_count+1 ] = { trNOOP("off"), "Bob", "Weave", "Greedy", "One Field", "One Field XV", "Linear Blend", "TvTime", NULL }; const char * const config_t::s_audioDrivers[ AUDIO_DRIVER_count+1 ] = { "auto", "alsa", "oss", "none", "esd", "jack", NULL }; const char * const config_t::s_audioDriverNames[ AUDIO_DRIVER_count+1 ] = { trNOOP("automatic"), "Alsa", "OSS", trNOOP("no audio"), "ESD", "Jack", NULL }; const char * const config_t::s_videoDriversX11[ X11_DRIVER_count+1 ] = { "auto", "xshm", "xv", "vaapi", "vdpau", "opengl", "xvmc", "xxmc", "sdl", "XDirectFB", "vidix", "none", NULL }; const char * const config_t::s_videoDriverNamesX11[ X11_DRIVER_count+1 ] = { trNOOP("automatic"), "XShm", "Xv", "VAAPI", "VDPAU", "OpenGL", "XvMC", "XvMC+VLD", "SDL", "XDirectFB", "Vidix", trNOOP("no video"), NULL }; const char * const config_t::s_videoDriversFB[ FB_DRIVER_count+1 ] = { "auto", "fb", "DirectFB", "sdl", "vidixfb", "aadxr3", "none", NULL }; const char * const config_t::s_videoDriverNamesFB [ FB_DRIVER_count+1 ] = { trNOOP("automatic"), "Framebuffer", "DirectFB", "SDL", "VidixFB", "DXR3", trNOOP("no video"), NULL }; const char * const config_t::s_frontends[ FRONTEND_count+1 ] = { "sxfe", "fbfe", "none", NULL }; const char * const config_t::s_frontendNames[ FRONTEND_count+1 ] = { "X11 (sxfe)", "Framebuffer (fbfe)", trNOOP("Off"), NULL }; const char * const config_t::s_frontend_files[ FRONTEND_count+1 ] = { "lib" PLUGIN_NAME_I18N "-sxfe.so." XINELIBOUTPUT_VERSION, "lib" PLUGIN_NAME_I18N "-fbfe.so." XINELIBOUTPUT_VERSION, // example: libxineliboutput-sxfe.so.0.4.0 "", NULL }; const char * const config_t::s_audioEqNames[ AUDIO_EQ_count+1 ] = { "30 Hz", "60 Hz", "125 Hz", "250 Hz", "500 Hz", "1 kHz", "2 kHz", "4 kHz", "8 kHz", "16 kHz", NULL }; const char * const config_t::s_audioVisualizations[ AUDIO_VIS_count+1 ] = { "none", "goom", "oscope", "fftscope", "fftgraph", "image", NULL }; const char * const config_t::s_audioVisualizationNames[ AUDIO_VIS_count+1 ] = { trNOOP("Off"), trNOOP("Goom"), trNOOP("Oscilloscope"), trNOOP("FFT Scope"), trNOOP("FFT Graph"), trNOOP("Image"), NULL }; /* xine, audio_alsa_out.c */ const char * const config_t::s_speakerArrangements[ SPEAKERS_count+1 ] = { trNOOP("Mono 1.0"), trNOOP("Stereo 2.0"), trNOOP("Headphones 2.0"), trNOOP("Stereo 2.1"), trNOOP("Surround 3.0"), trNOOP("Surround 4.0"), trNOOP("Surround 4.1"), trNOOP("Surround 5.0"), trNOOP("Surround 5.1"), trNOOP("Surround 6.0"), trNOOP("Surround 6.1"), trNOOP("Surround 7.1"), trNOOP("Pass Through"), NULL }; const char * const config_t::s_subtitleSizes[ SUBTITLESIZE_count+1 ] = { trNOOP("default"), trNOOP("tiny"), trNOOP("small"), trNOOP("medium"), trNOOP("large"), trNOOP("very large"), trNOOP("huge"), NULL }; const char * const config_t::s_subExts[] = { ".sub", ".srt", ".txt", ".ssa", "ass", "smi", ".SUB", ".SRT", ".TXT", ".SSA", "ASS", "SMI", NULL }; const char * const config_t::s_osdBlendingMethods[] = { trNOOP("Software"), trNOOP("Hardware"), NULL }; const char * const config_t::s_osdMixers[] = { trNOOP("no"), trNOOP("grayscale"), // item [1] trNOOP("transparent"), // item [2] trNOOP("transparent grayscale"), // item [3] ([1 | 2]) trNOOP("yes"), NULL }; const char * const config_t::s_osdScalings[] = { trNOOP("no"), trNOOP("nearest"), // item [1] trNOOP("bilinear"), // item [2] NULL }; const char * const config_t::s_osdColorDepths[] = { trNOOP("automatic"), trNOOP("LUT8"), trNOOP("TrueColor"), NULL }; const char * const config_t::s_osdSizes[] = { trNOOP("automatic"), "720x576", "1280x720", "1920x1080", trNOOP("custom"), trNOOP("video stream"), NULL }; const char * const config_t::s_decoders_MPEG2[] = { trNOOP("automatic"), "libmpeg2", "FFmpeg", NULL }; const char * const config_t::s_decoders_H264[] = { trNOOP("automatic"), "FFmpeg", "CoreAVC", NULL }; const char * const config_t::s_ff_skip_loop_filters[] = { trNOOP("automatic"), trNOOP("default"), trNOOP("none"), trNOOP("nonref"), trNOOP("bidir"), trNOOP("nonkey"), trNOOP("all"), NULL }; const char * const config_t::s_ff_speed_over_accuracy[] = { trNOOP("automatic"), trNOOP("yes"), trNOOP("no"), }; static const char exts_playlist[][4] = { "asx", "m3u", "pls", "ram", }; static const char exts_audio[][8] = { "ac3", "asf", "au", "aud", "flac", "mpa", "mpega", "mp2", "mp3", "m4a", "ogg", "ogm", "ra", "spx", "wav", "wma", }; static const char exts_video[][8] = { "asf", "avi", "dat", "divx", "dv", "fli", "flv", "iso", /* maybe dvd */ "mkv", "mov", "mpeg", "mpg", "mpv", "mp4", "m2v", "m2t", "m2ts", "m4v", "mts", "pes", "rm", "ts", "vdr", "vob", "wmv", "xvid", }; static const char exts_image[][8] = { "bmp", "gif", "jpeg", "jpg", "mng", "png", "tiff", }; #define DEF_EXT_IS(TYPE) \ static bool ext_is_ ## TYPE(const char *ext) \ { \ for(unsigned int i=0; i<sizeof(exts_ ## TYPE)/sizeof(exts_ ## TYPE[0]); i++) \ if(!strcasecmp(ext, exts_ ## TYPE[i])) \ return true; \ return false; \ } DEF_EXT_IS(playlist) DEF_EXT_IS(audio) DEF_EXT_IS(video) DEF_EXT_IS(image) static const char *get_extension(const char *fname) { if(fname) { const char *pos = strrchr(fname, '.'); if(pos) return pos+1; } return NULL; } static char *strcatrealloc(char *dest, const char *src) { if (!src || !*src) return dest; size_t l = (dest ? strlen(dest) : 0) + strlen(src) + 1; if(dest) { dest = (char *)realloc(dest, l); strcat(dest, src); } else { dest = (char*)malloc(l); strcpy(dest, src); } return dest; } bool config_t::IsPlaylistFile(const char *fname) { const char *ext = get_extension(fname); return ext && ext_is_playlist(ext); } bool config_t::IsAudioFile(const char *fname) { const char *ext = get_extension(fname); return ext && (ext_is_audio(ext) || ext_is_playlist(ext)); } bool config_t::IsVideoFile(const char *fname) { const char *ext = get_extension(fname); return ext && (ext_is_video(ext) || ext_is_audio(ext) || ext_is_playlist(ext)); } bool config_t::IsImageFile(const char *fname) { const char *ext = get_extension(fname); return ext && (ext_is_image(ext) || ext_is_playlist(ext)); } bool config_t::IsDvdImage(const char *fname) { const char *ext = get_extension(fname); return (ext && !strcasecmp(ext, "iso")) ? true : false; } bool config_t::IsBluRayImage(const char *fname) { if (IsDvdImage(fname)) { struct stat st; if (!stat(fname, &st)) { return st.st_size > 10*1024*1024*1024ll; } } return false; } bool config_t::IsDvdFolder(const char *fname) { struct stat st; cString folder = cString::sprintf("%s/VIDEO_TS/", fname); if (stat(folder, &st) != 0) { folder = cString::sprintf("%s/video_ts/", fname); if (stat(folder, &st) != 0) return false; } if (stat(cString::sprintf("%s/video_ts.ifo", *folder), &st) == 0) return true; if (stat(cString::sprintf("%s/VIDEO_TS.IFO", *folder), &st) == 0) return true; return false; } bool config_t::IsBluRayFolder(const char *fname) { struct stat st; cString folder = cString::sprintf("%s/BDMV/", fname); if (stat(folder, &st) != 0) { folder = cString::sprintf("%s/bdmv/", fname); if (stat(folder, &st) != 0) return false; } if (stat(cString::sprintf("%s/MovieObject.bdmv", *folder), &st) == 0) return true; if (stat(cString::sprintf("%s/movieobject.bdmv", *folder), &st) == 0) return true; if (stat(cString::sprintf("%s/MOVIEOBJECT.BDMV", *folder), &st) == 0) return true; return false; } cString config_t::AutocropOptions(void) { if (!autocrop) return NULL; return cString::sprintf( "enable_autodetect=%d,autodetect_rate=%d," "soft_start=%d,soft_start_step=%d," "stabilize=%d,stabilize_time=%d," "enable_subs_detect=%d,subs_detect_lifetime=%d,subs_detect_stabilize_time=%d," "logo_width=%d,overscan_compensate=%d,use_driver_crop=%d," "use_avards_analysis=%d,bar_tone_tolerance=%d", autocrop_autodetect, autocrop_autodetect_rate, autocrop_soft, autocrop_soft_start_step, autocrop_fixedsize, autocrop_stabilize_time, autocrop_subs, autocrop_subs_detect_lifetime, autocrop_subs_detect_stabilize_time, autocrop_logo_width, autocrop_overscan_compensate, autocrop_use_driver_crop, autocrop_use_avards_analysis, autocrop_bar_tone_tolerance); } cString config_t::SwScaleOptions(void) { if (!swscale) return NULL; return cString::sprintf("output_aspect=%s,output_width=%d,output_height=%d,no_downscaling=%d", swscale_change_aspect ? "auto" : "0.0", swscale_resize ? swscale_width : 0, swscale_resize ? swscale_height : 0, swscale_downscale ? 0 : 1 ); } cString config_t::FfmpegPpOptions(void) { if (!ffmpeg_pp) return NULL; if(*ffmpeg_pp_mode) return cString::sprintf("quality=%d,mode=%s", ffmpeg_pp_quality, ffmpeg_pp_mode); return cString::sprintf("quality=%d", ffmpeg_pp_quality); } cString config_t::UnsharpOptions(void) { if (!unsharp) return NULL; return cString::sprintf("luma_matrix_width=%d,luma_matrix_height=%d,luma_amount=%1.1f," "chroma_matrix_width=%d,chroma_matrix_height=%d,chroma_amount=%1.1f", unsharp_luma_matrix_width, unsharp_luma_matrix_height, ((float)unsharp_luma_amount)/10.0, unsharp_chroma_matrix_width, unsharp_chroma_matrix_height, ((float)unsharp_chroma_amount)/10.0); } cString config_t::Denoise3dOptions(void) { if (!denoise3d) return NULL; return cString::sprintf("luma=%1.1f,chroma=%1.1f,time=%1.1f", ((float)denoise3d_luma)/10.0, ((float)denoise3d_chroma)/10.0, ((float)denoise3d_time)/10.0); } config_t::config_t() { memset(this, 0, sizeof(config_t)); strn0cpy(local_frontend, s_frontends[FRONTEND_X11], sizeof(local_frontend)); strn0cpy(video_driver , s_videoDriversX11[X11_DRIVER_XV], sizeof(video_driver)); strn0cpy(video_port , "0.0", sizeof(video_port)); strn0cpy(modeline , "", sizeof(modeline)); strn0cpy(audio_driver , s_audioDrivers[AUDIO_DRIVER_ALSA], sizeof(audio_driver)); strn0cpy(audio_port , "default", sizeof(audio_port)); speaker_type = SPEAKERS_STEREO; post_plugins = NULL; config_file = NULL; audio_delay = 0; audio_compression = 0; memset(audio_equalizer,0,sizeof(audio_equalizer)); strn0cpy(audio_visualization, "goom", sizeof(audio_visualization)); strn0cpy(audio_vis_goom_opts, "fps:25,width:720,height:576", sizeof(audio_vis_goom_opts)); strn0cpy(audio_vis_image_mrl, "file:/usr/share/xine/visuals/default.avi", sizeof(audio_vis_image_mrl)); headphone = 0; audio_upmix = 0; audio_surround = 0; sw_volume_control = 0; pes_buffers = i_pesBufferSize[PES_BUFFERS_SMALL_250]; strn0cpy(deinterlace_method, s_deinterlaceMethods[DEINTERLACE_NONE], sizeof(deinterlace_method)); strn0cpy(deinterlace_opts, DEFAULT_DEINTERLACE_OPTS, sizeof(deinterlace_opts)); ffmpeg_pp = 0; ffmpeg_pp_quality = 3; strn0cpy(ffmpeg_pp_mode, "de", sizeof(ffmpeg_pp_mode)); subtitle_vpos = 0; unsharp = 0; unsharp_luma_matrix_width = 5; unsharp_luma_matrix_height = 5; unsharp_luma_amount = 0; unsharp_chroma_matrix_width = 3; unsharp_chroma_matrix_height = 3; unsharp_chroma_amount = 0; denoise3d = 0; denoise3d_luma = 40; denoise3d_chroma = 30; denoise3d_time = 60; display_aspect = 0; /* auto */ hide_main_menu = 0; osd_size = OSD_SIZE_auto; osd_width = 720; osd_height = 576; osd_width_auto = 0; osd_height_auto = 0; osd_color_depth = OSD_DEPTH_auto; osd_mixer = OSD_MIXER_FULL; osd_scaling = OSD_SCALING_NEAREST; osd_spu_scaling = OSD_SCALING_NEAREST; hud_osd = 0; opengl = 0; osd_blending = OSD_BLENDING_SOFTWARE; osd_blending_lowresvideo = OSD_BLENDING_HARDWARE; extsub_size = -1; dvb_subtitles = 0; alpha_correction = 0; alpha_correction_abs = 0; fullscreen = 0; modeswitch = 0; width = 720; height = 576; scale_video = 0; autocrop = 0; autocrop_autodetect = 1; autocrop_autodetect_rate = 4; autocrop_soft = 1; autocrop_soft_start_step = 4; autocrop_fixedsize = 1; autocrop_stabilize_time = (5*25); autocrop_subs = 1; autocrop_subs_detect_lifetime = (60*25); autocrop_subs_detect_stabilize_time = 12; autocrop_logo_width = 20; autocrop_use_driver_crop = 0; autocrop_use_avards_analysis = 0; autocrop_overscan_compensate = 0; autocrop_bar_tone_tolerance = 0; swscale = 0; // enable/disable swscale_change_aspect = 0; // change video aspect ratio swscale_resize = 0; // change video size swscale_width = 720; // output video width swscale_height = 576; // output video height swscale_downscale = 0; // allow downscaling remote_mode = 0; listen_port = LISTEN_PORT; remote_keyboard = 1; remote_max_clients = MAXCLIENTS; remote_usetcp = 1; remote_useudp = 1; remote_usertp = 1; remote_usepipe = 1; remote_usebcast = 1; remote_http_files = 1; /* allow http streaming of media files to xineliboutput clients * (currently replayed media file from xineliboutput media player) * - will be used if client can't access file directly (nfs etc.) */ strn0cpy(remote_rtp_addr, "224.0.1.9", sizeof(remote_rtp_addr)); remote_rtp_port = (LISTEN_PORT) & (0xfffe); /* even ports only */ remote_rtp_ttl = 1; remote_rtp_always_on = 0; remote_rtp_sap = 1; remote_use_rtsp = 1; // allow generic rtsp for primary device. needs enabled udp or rtp remote_use_rtsp_ctrl = 0; // allow rtsp to control primary device (play/pause/seek...) remote_use_http = 1; // allow generic http streaming (primary device output) remote_use_http_ctrl = 0; // allow http to control primary device (play/pause/seek...) remote_local_if[0] = 0; // use only this interface - undefined -> any/all remote_local_ip[0] = 0; // bind locally to this IP - undefined -> any/all use_x_keyboard = 1; window_id = WINDOW_ID_NONE; // video settings ibp_trickspeed = 1; max_trickspeed = 12; overscan = 0; hue = -1; saturation = -1; contrast = -1; brightness = -1; sharpness = -1; noise_reduction = -1; vo_aspect_ratio = 0; live_mode_sync = 1; // Sync SCR to transponder clock in live mode scr_tuning = 0; // Fine-tune xine egine SCR (to sync video to graphics output) scr_hz = 90000; // Current SCR speed (Hz), default is 90000 decoder_mpeg2 = DECODER_MPEG2_auto; decoder_h264 = DECODER_H264_auto; ff_h264_speed_over_accurancy = FF_H264_SPEED_OVER_ACCURACY_auto; ff_h264_skip_loop_filter = FF_H264_SKIP_LOOPFILTER_auto; strn0cpy(media_root_dir, "/", sizeof(media_root_dir)); #if defined(APIVERSNUM) && (APIVERSNUM >= 20102) const char *VideoDirectory = cVideoDirectory::Name() ? cVideoDirectory::Name() : VIDEODIR; #endif strn0cpy(browse_files_dir, VideoDirectory, sizeof(browse_files_dir)); strn0cpy(browse_music_dir, VideoDirectory, sizeof(browse_music_dir)); strn0cpy(browse_images_dir, VideoDirectory, sizeof(browse_images_dir)); show_hidden_files = 0; cache_implicit_playlists = 1; enable_id3_scanner = 1; dvd_arrow_keys_control_playback = 1; media_menu_items = ~0; media_enable_delete = 0; media_enable_resume = 1; main_menu_mode = ShowMenu; last_hotkey = -1;//kNone; force_primary_device = 0; use_suspendoutput = 0; }; #if 0 static uint8_t g_hidden_options[sizeof(config_t)] = {0}; static uint8_t g_readonly_options[sizeof(config_t)] = {0}; uint8_t *config_t::hidden_options = &g_hidden_options[0]; uint8_t *config_t::readonly_options = &g_readonly_options[0]; #endif cString config_t::m_ProcessedArgs; bool config_t::ProcessArg(const char *Name, const char *Value) { if (Name && Value && SetupParse(Name, Value)) { m_ProcessedArgs = cString::sprintf("%s%s ", *m_ProcessedArgs ? *m_ProcessedArgs : " ", Name); return true; } return false; } bool config_t::ProcessArgs(int argc, char *argv[]) { static const char short_options[] = "fDw:h:l:mr:A:V:d:P:C:pct"; static const struct option long_options[] = { { "fullscreen", no_argument, NULL, 'f' }, { "hud", optional_argument, NULL, 'D' }, { "opengl", no_argument, NULL, 'O' }, { "width", required_argument, NULL, 'w' }, { "height", required_argument, NULL, 'h' }, { "geometry", required_argument, NULL, 'g' }, //{ "xkeyboard", no_argument, NULL, 'k' }, //{ "noxkeyboard", no_argument, NULL, 'K' }, { "local", required_argument, NULL, 'l' }, //{ "modeline", required_argument, NULL, 'm' }, { "remote", required_argument, NULL, 'r' }, { "audio", required_argument, NULL, 'A' }, { "video", required_argument, NULL, 'V' }, { "display", required_argument, NULL, 'd' }, { "window", required_argument, NULL, 'W' }, { "modeswitch", no_argument, NULL, 'm' }, { "post", required_argument, NULL, 'P' }, { "config", required_argument, NULL, 'C' }, { "primary", no_argument, NULL, 'p' }, { "auto-suspend", no_argument, NULL, 's' }, { "exit-on-close",no_argument, NULL, 'c' }, { "truecolor", no_argument, NULL, 't' }, { NULL, no_argument, NULL, 0 } }; int c; while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { switch (c) { case 'd': ProcessArg("Video.Port", optarg); break; case 'W': ProcessArg("X11.WindowId", optarg); break; case 'm': ProcessArg("VideoModeSwitching", "1"); #ifndef HAVE_XRANDR LOGMSG("Video mode switching not supported"); #endif break; case 'f': ProcessArg("Fullscreen", "1"); break; case 't': ProcessArg("truecoloreverytime", "1"); break; case 'D': ProcessArg("X11.HUDOSD", "1"); if (optarg && strstr(optarg, "xshape")) { ProcessArg("XShapeHUDOSD", "1"); #ifndef HAVE_XSHAPE LOGMSG("XShape HUD OSD not supported\n"); #endif } if (optarg && strstr(optarg, "opengl")) { ProcessArg("OpenglHUDOSD", "1"); #ifndef HAVE_OPENGL LOGMSG("OpenGL HUD OSD not supported\n"); #endif } #ifndef HAVE_XRENDER else LOGMSG("HUD OSD not supported\n"); #endif break; case 'O': ProcessArg("OpenglAlways", "1"); #ifndef HAVE_OPENGL LOGMSG("OpenGL not supported\n"); #endif break; case 'w': //ProcessArg("Fullscreen", "0"); ProcessArg("X11.WindowWidth", optarg); break; case 'h': //ProcessArg("Fullscreen", "0"); ProcessArg("X11.WindowHeight", optarg); break; case 'g': { int _width = width, _height = height, _xpos = 0, _ypos = 0; sscanf (optarg, "%dx%d+%d+%d", &_width, &_height, &_xpos, &_ypos); ProcessArg("X11.WindowWidth", *cString::sprintf("%d", _width)); ProcessArg("X11.WindowHeight", *cString::sprintf("%d", _height)); ProcessArg("X11.XPos", *cString::sprintf("%d", _xpos)); ProcessArg("X11.YPos", *cString::sprintf("%d", _ypos)); } break; //case 'k': ProcessArg("X11.UseKeyboard", "1"); // break; //case 'K': ProcessArg("X11.UseKeyboard", "0"); // break; case 'l': ProcessArg("Frontend", optarg); break; //case 'm': ProcessArg("Modeline", optarg); // break; case 'r': if(strcmp(optarg, "none")) { if(strchr(optarg, ':')) { char *tmp = strdup(optarg); char *pt = strchr(tmp,':'); *pt++ = 0; ProcessArg("Remote.ListenPort", pt); ProcessArg("RemoteMode", listen_port>0 ? "1" : "0"); ProcessArg("Remote.LocalIP", tmp); free(tmp); LOGMSG("Listening on address \'%s\' port %d", remote_local_ip, listen_port); } else { ProcessArg("Remote.ListenPort", optarg); ProcessArg("RemoteMode", listen_port>0 ? "1" : "0"); } } else ProcessArg("RemoteMode", "0"); break; case 'V': ProcessArg("Video.Driver", optarg); break; case 'A': if(strchr(optarg,':')) { char *tmp = strdup(optarg); char *pt = strchr(tmp,':'); *pt = 0; ProcessArg("Audio.Driver", tmp); ProcessArg("Audio.Port", pt+1); free(tmp); } else ProcessArg("Audio.Driver", optarg); break; case 'P': if(post_plugins) post_plugins = strcatrealloc(post_plugins, ";"); post_plugins = strcatrealloc(post_plugins, optarg); break; case 'C': config_file = strdup(optarg); break; case 'p': ProcessArg("ForcePrimaryDevice", "1"); break; case 's': ProcessArg("AutoSuspendOutput", "1"); break; case 'c': exit_on_close = 1; break; default: return false; } } return true; } bool config_t::SetupParse(const char *Name, const char *Value) { const char *pt; if(*m_ProcessedArgs && NULL != (pt=strstr(m_ProcessedArgs+1, Name)) && *(pt-1) == ' ' && *(pt+strlen(Name)) == ' ') { LOGDBG("Skipping configuration entry %s=%s (overridden in command line)", Name, Value); return true; } if (!strcasecmp(Name, "Frontend")) STRN0CPY(local_frontend, Value); else if (!strcasecmp(Name, "Modeline")) STRN0CPY(modeline, Value); else if (!strcasecmp(Name, "VideoModeSwitching")) modeswitch = atoi(Value); else if (!strcasecmp(Name, "Fullscreen")) fullscreen = atoi(Value); else if (!strcasecmp(Name, "DisplayAspect")) display_aspect = strstra(Value, s_aspects, 0); else if (!strcasecmp(Name, "ForcePrimaryDevice")) force_primary_device = atoi(Value); else if (!strcasecmp(Name, "AutoSuspendOutput")) use_suspendoutput = atoi(Value); else if (!strcasecmp(Name, "X11.WindowId")) window_id = (!strcmp(Value, "root")) ? WINDOW_ID_ROOT : atoi(Value); else if (!strcasecmp(Name, "X11.WindowWidth")) width = atoi(Value); else if (!strcasecmp(Name, "X11.WindowHeight")) height = atoi(Value); else if (!strcasecmp(Name, "X11.XPos")) xpos = atoi(Value); else if (!strcasecmp(Name, "X11.YPos")) ypos = atoi(Value); else if (!strcasecmp(Name, "X11.UseKeyboard")) use_x_keyboard = atoi(Value); else if (!strcasecmp(Name, "X11.HUDOSD")) hud_osd |= (atoi(Value) ? HUD_COMPOSITE : 0); else if (!strcasecmp(Name, "X11.OpenglAlways")) opengl = atoi(Value); else if (!strcasecmp(Name, "X11.OpenglHUDOSD")) hud_osd |= (atoi(Value) ? HUD_OPENGL : 0); else if (!strcasecmp(Name, "X11.XShapeHUDOSD")) hud_osd |= (atoi(Value) ? HUD_XSHAPE : 0); else if (!strcasecmp(Name, "truecoloreverytime")) truecoloreverytime = atoi(Value); else if (!strcasecmp(Name, "Audio.Driver")) STRN0CPY(audio_driver, Value); else if (!strcasecmp(Name, "Audio.Port")) STRN0CPY(audio_port, Value); else if (!strcasecmp(Name, "Audio.Speakers")) speaker_type = strstra(Value, s_speakerArrangements, SPEAKERS_STEREO); else if (!strcasecmp(Name, "Audio.Delay")) audio_delay = atoi(Value); else if (!strcasecmp(Name, "Audio.Compression")) audio_compression = atoi(Value); else if (!strcasecmp(Name, "Audio.Visualization.GoomOpts")) STRN0CPY(audio_vis_goom_opts, Value); else if (!strcasecmp(Name, "Audio.Visualization.ImageMRL")) STRN0CPY(audio_vis_image_mrl, Value); else if (!strcasecmp(Name, "Audio.Visualization")) STRN0CPY(audio_visualization, Value); else if (!strcasecmp(Name, "Audio.Surround")) audio_surround = atoi(Value); else if (!strcasecmp(Name, "Audio.Upmix")) audio_upmix = atoi(Value); else if (!strcasecmp(Name, "Audio.Headphone")) headphone = atoi(Value); else if (!strcasecmp(Name, "Audio.SoftwareVolumeControl")) sw_volume_control = atoi(Value); else if (!strcasecmp(Name, "OSD.HideMainMenu")) hide_main_menu = atoi(Value); else if (!strcasecmp(Name, "OSD.Size")) osd_size = strstra(Value, s_osdSizes, 0); else if (!strcasecmp(Name, "OSD.Width")) osd_width = atoi(Value); else if (!strcasecmp(Name, "OSD.Height")) osd_height = atoi(Value); else if (!strcasecmp(Name, "OSD.ColorDepth")) osd_color_depth = strstra(Value, s_osdColorDepths, 0); else if (!strcasecmp(Name, "OSD.LayersVisible")) osd_mixer = atoi(Value); else if (!strcasecmp(Name, "OSD.Scaling")) osd_scaling = atoi(Value); else if (!strcasecmp(Name, "OSD.ScalingSPU")) osd_spu_scaling = atoi(Value); else if (!strcasecmp(Name, "OSD.Blending")) osd_blending = atoi(Value); else if (!strcasecmp(Name, "OSD.BlendingLowRes")) osd_blending_lowresvideo = atoi(Value); #if 1 // < 1.0.1 else if (!strcasecmp(Name, "OSD.UnscaledAlways")) osd_blending = atoi(Value); else if (!strcasecmp(Name, "OSD.UnscaledLowRes")) osd_blending_lowresvideo = atoi(Value); #endif else if (!strcasecmp(Name, "OSD.AlphaCorrection")) alpha_correction = atoi(Value); else if (!strcasecmp(Name, "OSD.AlphaCorrectionAbs")) alpha_correction_abs = atoi(Value); else if (!strcasecmp(Name, "OSD.ExtSubSize")) extsub_size = atoi(Value); else if (!strcasecmp(Name, "OSD.DvbSubtitles")) dvb_subtitles = atoi(Value); else if (!strcasecmp(Name, "RemoteMode")) remote_mode = atoi(Value); else if (!strcasecmp(Name, "Remote.ListenPort")) listen_port = atoi(Value); else if (!strcasecmp(Name, "Remote.Keyboard")) remote_keyboard = atoi(Value); else if (!strcasecmp(Name, "Remote.MaxClients")) remote_max_clients = atoi(Value); else if (!strcasecmp(Name, "Remote.UseTcp")) remote_usetcp = atoi(Value); else if (!strcasecmp(Name, "Remote.UseUdp")) remote_useudp = atoi(Value); else if (!strcasecmp(Name, "Remote.UseRtp")) remote_usertp = atoi(Value); else if (!strcasecmp(Name, "Remote.UsePipe")) remote_usepipe= atoi(Value); else if (!strcasecmp(Name, "Remote.UseHttp")) remote_http_files = atoi(Value); else if (!strcasecmp(Name, "Remote.UseBroadcast")) remote_usebcast = atoi(Value); else if (!strcasecmp(Name, "Remote.Rtp.Address")) STRN0CPY(remote_rtp_addr, Value); else if (!strcasecmp(Name, "Remote.Rtp.Port")) remote_rtp_port = (atoi(Value)) & (0xfffe); else if (!strcasecmp(Name, "Remote.Rtp.TTL")) remote_rtp_ttl = atoi(Value); else if (!strcasecmp(Name, "Remote.Rtp.AlwaysOn")) remote_rtp_always_on = atoi(Value); else if (!strcasecmp(Name, "Remote.Rtp.SapAnnouncements")) remote_rtp_sap = atoi(Value); else if (!strcasecmp(Name, "Remote.AllowRtsp")) remote_use_rtsp = atoi(Value); else if (!strcasecmp(Name, "Remote.AllowRtspCtrl")) remote_use_rtsp_ctrl = atoi(Value); else if (!strcasecmp(Name, "Remote.AllowHttp")) remote_use_http = atoi(Value); else if (!strcasecmp(Name, "Remote.AllowHttpCtrl")) remote_use_http_ctrl = atoi(Value); else if (!strcasecmp(Name, "Remote.Iface")) STRN0CPY(remote_local_if, Value); else if (!strcasecmp(Name, "Remote.LocalIP")) STRN0CPY(remote_local_ip, Value); else if (!strcasecmp(Name, "Decoder.PesBuffers")) pes_buffers=atoi(Value); else if (!strcasecmp(Name, "Video.Driver")) STRN0CPY(video_driver, Value); else if (!strcasecmp(Name, "Video.Port")) STRN0CPY(video_port, Value); else if (!strcasecmp(Name, "Video.Scale")) scale_video = atoi(Value); else if (!strcasecmp(Name, "Video.DeinterlaceOptions")) STRN0CPY(deinterlace_opts, Value); else if (!strcasecmp(Name, "Video.Deinterlace")) STRN0CPY(deinterlace_method, Value); else if (!strcasecmp(Name, "Video.AutoCrop")) autocrop = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.AutoDetect")) autocrop_autodetect = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.AutoDetectRate")) autocrop_autodetect_rate = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.SoftStart")) autocrop_soft = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.SoftStartStep")) autocrop_soft_start_step = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.FixedSize")) autocrop_fixedsize = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.StabilizeTime")) autocrop_stabilize_time = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.DetectSubs")) autocrop_subs = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.SubsDetectLifetime")) autocrop_subs_detect_lifetime = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.SubsDetectStabilizeTime")) autocrop_subs_detect_stabilize_time = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.LogoWidth")) autocrop_logo_width = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.UseDriverCrop")) autocrop_use_driver_crop = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.UseAvardsAnalysis")) autocrop_use_avards_analysis = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.OverscanCompensate")) autocrop_overscan_compensate = atoi(Value); else if (!strcasecmp(Name, "Video.AutoCrop.BarToneTolerance")) autocrop_bar_tone_tolerance = atoi(Value); else if (!strcasecmp(Name, "Video.SwScale")) swscale = atoi(Value); else if (!strcasecmp(Name, "Video.SwScale.Aspect")) swscale_change_aspect = atoi(Value); else if (!strcasecmp(Name, "Video.SwScale.Resize")) swscale_resize = atoi(Value); else if (!strcasecmp(Name, "Video.SwScale.Downscale")) swscale_downscale = atoi(Value); else if (!strcasecmp(Name, "Video.SwScale.Width")) swscale_width = atoi(Value); else if (!strcasecmp(Name, "Video.SwScale.Height")) swscale_height = atoi(Value); else if (!strcasecmp(Name, "Video.HUE")) hue = atoi(Value); else if (!strcasecmp(Name, "Video.Saturation")) saturation = atoi(Value); else if (!strcasecmp(Name, "Video.Contrast")) contrast = atoi(Value); else if (!strcasecmp(Name, "Video.Brightness")) brightness = atoi(Value); else if (!strcasecmp(Name, "Video.Sharpness")) sharpness = atoi(Value); else if (!strcasecmp(Name, "Video.NoiseReduction")) noise_reduction = atoi(Value); else if (!strcasecmp(Name, "Video.Overscan")) overscan = atoi(Value); else if (!strcasecmp(Name, "Video.IBPTrickSpeed")) ibp_trickspeed = atoi(Value); else if (!strcasecmp(Name, "Video.MaxTrickSpeed")) max_trickspeed = atoi(Value); else if (!strcasecmp(Name, "Video.AspectRatio")) vo_aspect_ratio = atoi(Value); else if (!strcasecmp(Name, "Video.Decoder.MPEG2")) decoder_mpeg2 = strstra(Value, s_decoders_MPEG2, 0); else if (!strcasecmp(Name, "Video.Decoder.H264")) decoder_h264 = strstra(Value, s_decoders_H264, 0); else if (!strcasecmp(Name, "Video.Decoder.H264.SpeedOverAccuracy")) ff_h264_speed_over_accurancy = strstra(Value, s_ff_speed_over_accuracy, 0); else if (!strcasecmp(Name, "Video.Decoder.H264.SkipLoopFilter")) ff_h264_skip_loop_filter = strstra(Value, s_ff_skip_loop_filters, 0); else if (!strcasecmp(Name, "Post.pp.Enable")) ffmpeg_pp = atoi(Value); else if (!strcasecmp(Name, "Post.pp.Quality")) ffmpeg_pp_quality = atoi(Value); else if (!strcasecmp(Name, "Post.pp.Mode")) STRN0CPY(ffmpeg_pp_mode, Value); else if (!strcasecmp(Name, "Post.unsharp.Enable")) unsharp = atoi(Value); else if (!strcasecmp(Name, "Post.unsharp.luma_matrix_width")) unsharp_luma_matrix_width = atoi(Value); else if (!strcasecmp(Name, "Post.unsharp.luma_matrix_height")) unsharp_luma_matrix_height = atoi(Value); else if (!strcasecmp(Name, "Post.unsharp.luma_amount")) unsharp_luma_amount = atoi(Value); else if (!strcasecmp(Name, "Post.unsharp.chroma_matrix_width")) unsharp_chroma_matrix_width = atoi(Value); else if (!strcasecmp(Name, "Post.unsharp.chroma_matrix_height")) unsharp_chroma_matrix_height = atoi(Value); else if (!strcasecmp(Name, "Post.unsharp.chroma_amount")) unsharp_chroma_amount = atoi(Value); else if (!strcasecmp(Name, "Post.denoise3d.Enable")) denoise3d = atoi(Value); else if (!strcasecmp(Name, "Post.denoise3d.luma")) denoise3d_luma = atoi(Value); else if (!strcasecmp(Name, "Post.denoise3d.chroma")) denoise3d_chroma = atoi(Value); else if (!strcasecmp(Name, "Post.denoise3d.time")) denoise3d_time = atoi(Value); else if (!strcasecmp(Name, "Media.RootDir")) STRN0CPY(media_root_dir, Value); else if (!strcasecmp(Name, "Media.BrowseFilesDir")) STRN0CPY(browse_files_dir, Value); else if (!strcasecmp(Name, "Media.BrowseMusicDir")) STRN0CPY(browse_music_dir, Value); else if (!strcasecmp(Name, "Media.BrowseImagesDir")) STRN0CPY(browse_images_dir, Value); else if (!strcasecmp(Name, "Media.ShowHiddenFiles")) show_hidden_files = atoi(Value); else if (!strcasecmp(Name, "Media.CacheImplicitPlaylists")) cache_implicit_playlists = atoi(Value); else if (!strcasecmp(Name, "Media.EnableID3Scanner")) enable_id3_scanner = atoi(Value); else if (!strcasecmp(Name, "Media.DVD.ArrowKeysControlPlayback")) dvd_arrow_keys_control_playback = atoi(Value); else if (!strcasecmp(Name, "Media.MenuItems")) media_menu_items = atoi(Value); else if (!strcasecmp(Name, "Media.EnableDelete")) media_enable_delete = atoi(Value); else if (!strcasecmp(Name, "Media.EnableResume")) media_enable_resume = atoi(Value); else if (!strcasecmp(Name, "Playlist.Tracknumber")) playlist_tracknumber = atoi(Value); else if (!strcasecmp(Name, "Playlist.Artist")) playlist_artist = atoi(Value); else if (!strcasecmp(Name, "Playlist.Album")) playlist_album = atoi(Value); else if (!strcasecmp(Name, "Advanced.LiveModeSync")) live_mode_sync = atoi(Value); else if (!strcasecmp(Name, "Advanced.AdjustSCR")) scr_tuning = atoi(Value); else if (!strcasecmp(Name, "Advanced.SCRSpeed")) scr_hz = atoi(Value); else if (!strcasecmp(Name, "Audio.Equalizer")) sscanf(Value,"%d %d %d %d %d %d %d %d %d %d", audio_equalizer ,audio_equalizer+1, audio_equalizer+2,audio_equalizer+3, audio_equalizer+4,audio_equalizer+5, audio_equalizer+6,audio_equalizer+7, audio_equalizer+8,audio_equalizer+9); else return false; return true; } /* Global instance */ config_t xc; ������������������������������������������������xineliboutput-2.0.0/black_720x576.mpg���������������������������������������������������������������0000644�0001750�0001750�00000021157�13061253352�015035� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,@3p�������# �����@���������3hѣ 74aF04hцƍ0xѣFk,hѣ 74aF04hцƍ0xѣFo4h d4aF04hцƍ0xѣFo4h Fahѣ 54hцƍ0xѣFo4h Fahѣ 74aF0(ѣFo4h Fahѣ 74aF04hц4h Fahѣ 74aF04hцƍ0xѣF`�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�� hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц��! hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц��" hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц��# hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц��$ hѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hцƍ0xѣFo4h Fahѣ 74aF04hц�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/README��������������������������������������������������������������������������0000644�0001750�0001750�00000063050�13061253352�013110� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This is a "plugin" for the Video Disk Recorder (VDR). Written by: Petri Hintukainen <phintuka@users.sourceforge.net> Project's homepage: http://www.sourceforge.net/projects/xineliboutput Latest version available at: http://prdownloads.sourceforge.net/xineliboutput/ See the file COPYING for license information. Description X11 and Linux framebuffer front-end for VDR. Plugin displays video and OSD in X/Xv/XvMC window, Linux framebuffer/DirectFB/vidixfb or DXR3 card. Support for local and remote frontends. Built-in image and media player supports playback of most known media files (avi/mp3/divx/jpeg/...), DVDs and radio/video streams (http, rtsp, ...) directly from VDR. Requirements - vdr-1.6.0 or later (use "1.0.x" branch for older vdr versions) (vdr is required only at server side) - xine-lib 1.1.1 or later (xine-lib is not required for server in network-only usage) - Enough CPU power and memory to decode streams (PII 400Mhz + 64M should be enough with Xv or DirectFB) Optional: - X server with Composite and Xrender extensions, compositing window manager or composite manager (xcompmgr). (Required for HUD OSD to blend high-quality OSD using graphics hardware) - X server with OpenGL support (Required for OpenGL video output and OpenGL HUD OSD) - libextractor 0.5.20 or later (http://libextractor.sourceforge.net). (used for media file metadata extraction in media player) - libjpeg for grabbing in JPEG format WARNING Remote (network) mode should be used only in firewalled environment; it gives anyone full control to VDR ! Full access is granted to all hosts listed in allowed_hosts.conf. By default only connections from localhost (127.0.0.1) are allowed. Multicast streaming can flood your internet connection and/or wireless LAN. If there is no router (or intelligent switch ?) all multicast packets will be broadcasted to all network links. This will flood slow network links: - Internet connection if outgoing bandwith is < 10 Mbit/s - Wireless LAN (11 or 54 Mbit/s). By default multicast TTL is set to 1 so multicast packets should be stopped to first router regardless of network configuration. Git Latest fixes are available from sourceforge.net public GIT repository. GIT checkout command: git clone git://git.code.sf.net/p/xineliboutput/git xineliboutput-git Public CVS CVS is not updated anymore. Buildtime options VDR, X11 and xine-lib are auto-detected by the build system. By default all possible plugins and executables are build. Default configuration can be overridden by running configure script manually. List of all configurable features can be acquired by running ./configure --help For long-time use it is preferred to set configure options in Make.config file. Make.config is first read from VDR source directory and then from xineliboutput plugin source directory. Basic Make.config entries: enable/disable building of VDR plugin: XINELIBOUTPUT_CONFIGURE_OPTS += --enable-vdr / --disable-vdr enable/disable X11 frontends: XINELIBOUTPUT_CONFIGURE_OPTS += --enable-x11 / --disable-x11 enable/disable framebuffer frontends: XINELIBOUTPUT_CONFIGURE_OPTS += --enable-fb / --disable-fb enable/disable xine (input)plugin: XINELIBOUTPUT_CONFIGURE_OPTS += --enable-libxine / --disable-libxine It is possible to compile only remote frontends with command "make frontends". Building frontends is possible without VDR. Only xine-lib and corresponding development package or headers are required. Installing - IMPORTANT XINE'S DYNAMIC LIBRARIES AND FRONTEND EXECUTABLES ARE NOT INSTALLED AUTOMATICALLY. It is important to copy required libraries to right place either by hand or by executing "make install" in plugin's source directory. Installing binaries and libraries usually requires root permissions. PLUGIN WILL NOT WORK UNLESS ALL FILES HAVE BEEN INSTALLED ! To be able to use remote frontends each client's IP address must be defined in VDR's svdrphosts.conf. Full access is allowed to all hosts listed in svdrphosts.conf. Connections from any other hosts are rejected. Usage examples (VDR plugin) If no arguments are given, both X11 and framebuffer frontends are tried. First working frontend is used with best available video driver. Complete list of available command-line arguments can be obtained with "vdr --help". Only local frontend, X11/Xv video, alsa audio: vdr -P"xineliboutput --local=sxfe --video=xv --audio=alsa --remote=none" Only local frontend, (slow) X11 video, oss audio: vdr -P"xineliboutput --local=sxfe --video=xshm --audio=oss --remote=none" Only local frontend, DirectFB: vdr -P"xineliboutput --local=fbfe --video=DirectFB --remote=none" Only remote frontend(s): vdr -P"xineliboutput --local=none --remote=37890" Local and remote frontends: vdr -P"xineliboutput --local=sxfe --remote=37890" or vdr -P"xineliboutput --local=fbfe --remote=37890" Using remote frontends Two remote frontends are included, vdr-fbfe for framebuffer and vdr-sxfe for X11. Complete list of available command-line arguments can be obtained with "vdr-??fe --help". Frontend should find server automatically (from local subnet) and negotiate best available transport. If frontend does not find server (or specific transport should be used), mrl must be given on command line. NOTE: RTP is used only when requested with rtp: mrl or --rtp command-line option. Examples: Search for VDR (xineliboutput) server, connect to it and negotiate best available transport. Use best available audio and video driver. vdr-fbfe or vdr-sxfe Connect to 192.168.1.3 default port and negotiate best available transport vdr-fbfe xvdr://192.168.1.3 Connect to 192.168.2.100, port 12550 and use TCP transport vdr-fbfe xvdr+tcp://192.168.2.100:12550 Automatically search for VDR server and use UDP transport vdr-fbfe xvdr+udp: or vdr-fbfe --udp Available transports for video/audio pipe Use local pipe; server and front-end must be running on same machine. rtp Use RTP/UDP multicast for data and TCP for control. Multiple frontends can receive same stream. udp Use UDP unicast for data and TCP for control. tcp Use TCP protocol for control and data. Both channels use same server port and are opened by client. Forwarding lirc keys to server Use option --lirc with optional lircd socket name to forward LIRC commands from client to server. Audio driver Use alsa: vdr-fbfe --audio alsa Use alsa (and specific card/sub-device): vdr-fbfe --audio alsa:plughw:1,1 Video driver (and display / device): With X11 frontend (vdr-sxfe): vdr-sxfe --video [xshm | xv | xvmc | xxmc | vidix | vdpau | XDirectFB | opengl | sdl | none [:display]] Examples: --video xv --video xvmc:127.0.0.1:1.0 With framebuffer frontend (vdr-fbfe): vdr-fbfe --video [fb | DirectFB | sdl | vidixfb | dxr3 | aadxr3 | none [:fb_device]] Examples: --video DirectFB --video fb:/dev/fb/1 --video vidixfb --video aadxr3 De-interlacing If deinterlacing post plugin options are not given at command line, deinterlacing is controlled by VDR plugin configuration menu settings. De-interlacing can also be forced on or off with command-line option --post tvtime. Examples: vdr-sxfe --post tvtime:method=Linear,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1 vdr -P"xineliboutput --post=tvtime:method=Linear,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1" Disable deinterlacing: vdr-sxfe --post tvtime:enable=0 VDPAU All video scaling, cropping, and postprocessing options must be disabled if the VDPAU output device is used. De-interlacing can be enabled with command-line option --post tvtime: Examples: vdr-sxfe --video vdpau --post tvtime:method=use_vo_driver vdr -P"xineliboutput --video=vdpau --post=tvtime:method=use_vo_driver" HUD OSD HUD OSD implements high-quality OSD using modern graphics hardware. OSD is scaled and blended using hardware, so it adds no extra CPU overhead. OSD is always blended to output (display) resolution, so it remains sharp and detailed even with low-resolution video. It can also support transparency in the same way as full-featured DVB cards. HUD OSD must be enabled with command-line option (--hud). Scaling options can be configured in xineliboutput plugin setup menu, OSD settings page. There are three possible configurations for using HUD OSD. First one is to use a X server with composite / Xrender extensions and a composite manager. This is the default when the hud is enabled with --hud. The second one is to use a X server with XShape support. This configuration is enabled with the --hud=shape option. The third one is to use a X server with OpenGL support. The option --hud=opengl enables this configuration. The first configuration requires a composite manager to get transparency support. The second configuration can be used if the composite manager slows down the video play back. The HUD OSD has the same quality, but does not support transparency. The third configuration does not require the composite/Xrender extension. Hence, it can also be used with VDPAU NVIDIA graphic cards that produce video tearing with enabled composite extension. OpenGL is only used if the OSD is displayed. For video playback, the normal output to a window via the xine-lib is used. Therefore, normal video playback is as smooth as the xine-lib playback allows. OpenGL playback requires additional computing power from the cpu and graphic card, and can result in occasionally frame drops. If your hardware configuration is powerful enough, you can also try to use the --opengl option. It does both pure video playback and OSD drawing via OpenGL. The advantage is that there are no frame drops if the OSD opens and closes, because there is no switching between window-based and OpenGL-based playback. Requirements: - First configuration (--hud): - X server with Composite and Xrender extensions. Composite extension must be enabled in Xorg config. - Composite window manager (compiz, beryl, or properly configured xfce4, metacity, ...) or separate composite manager (xcompmgr) for transparency support. - Second configuration (--hud=xshape) - X server with Composite, Xrender and XShape extension. - no window manager required. - Third configuration (--hud=opengl or --opengl) - X server with OpenGL support - no window manager required. - Compatible graphics hardware and drivers. HUD OSD has been tested with: nVidia GF FX5700LE (driver version 169.09) Intel G965 (GMA-X3000) (driver version 2.2.1, textured XVideo) nVidia GT 425M (driver version 290.10, VDPAU) metacity 2.23.2 xcompmgr 1.1.3 NOTE: - Drawing video (even without OSD) may be slower when composite extension is enabled. - A composite window manager can also slow down the video playback. In case of problems, switch off the window manager to see if playback improves. - Try to adjust OSD size and offsets to get rid of possible graphical corruption. - For true HD-resolution OSD VDR needs to be patched. - OpenGL-based HUD requires that the xine-lib video out driver supports redirecting video frames to a pixmap. VDPAU is known to work (tested with changeset 11949:0e68b56727d6 from 2011-12-21 of xine-lib 1.2). XV does currently not support it. - If you see black frames when opening the HUD with the --hud=opengl option: The video out driver frees all resources when the request to switch to a pixmap arrives. This clean up can also include clearing the window, resulting in a black frame until the OpenGL drawing takes over. The VDPAU output drivers behaves in this way. There are two options to fix this: Either enable the BackingStore of the X-Server (Option "BackingStore" TRUE in the Device Section for NVIDIA cards) or patch the xine-lib to not immediately free the resources. There is an example patch for VDPAU included in the patches directory (xinelib_vdpau_black_frame.patch). Please note that enabling the backing store might also require to enable the Composite extension. - Tearing free OpenGL playback / HUD requires that the "Sync to VBLank" option is set. For nvidia cards, this can be done via the command "nvidia-settings -a SyncToVBlank=1" HUD OSD was contributed by Antti Seppälä and Rolf Ahrenberg. HUD OSD XShape and OpenGL extensions were contributed by Matthias Grünewald. Using with xine-ui (xine, fbxine, gxine, ...) Examples: xine "xvdr://127.0.0.1#nocache" xine "xvdr+tcp://127.0.0.1:37890#nocache" xine "xvdr+udp://127.0.0.1:37890#nocache" "#nocache" should always be appended to end of mrl. Remote mode must be enabled in VDR plugin. Some configuration options are not available when using third-party frontends. Using with other media players (mplayer, vlc, ...) Primary device video and audio (without OSD or subtitles) can be streamed from plugin control port to almost any media player using http or rtsp. Session Announcement Protocol (SAP) compatible players should detect stream automatically and add it to playlist or bookmarks when RTP transmission is active (tested with vlc). Tested players: Linux: mplayer, vlc, xine Windows: vlc Examples: mplayer http://192.168.1.3:37890 vlc http://192.168.1.3:37890 vlc rtsp://192.168.1.3:37890 vlc rtp://@224.0.1.9:37890 Controlling VDR With local frontend, vdr-sxfe and vdr-fbfe: Keyboard input from console is mapped to VDR keyboard input. If VDR was compiled or configured without keyboard support, console keyboard input does not work. Keyboard input from X11 window is mapped to XKeySym remote. Keys are mapped to VDR keys in remote.conf file. Simple example of X11 key mappings is included in examples directory. It should be possible to use VDR's remote controller learning mode by pressing some key just after VDR has been started. Learning mode does not work with remote frontends. Keyboard input can be disabled in configuration menu. There are separate entries for local and remote frontends. With xine-ui: Keyboard shortcuts and remote events from xine menus are automatically forwarded to VDR and translated to VDR keys. Translation to VDR keys is static and defined in xine_input_vdr.c. Frontend key bindings Esc Close frontend (vdr-fbfe / fdr-sxfe) Mouse left button double-click Toggle between fullscreen / window mode (vdr-sxfe only) Mouse right button click Toggle between normal window / always on top / borderless window (vdr-sxfe only) Close Window Close frontend (fdr-sxfe only) Image viewer key bindings Left/Prev Previous image Right/Next Next image Up/Down Jump 5 images forward/backward Yellow Delete current image Back Return to image list Stop/Blue Exit image viewer Play Start slide show Pause Stop slide show FastFwd/FastRew Start slide show; Increase/decrease slide show speed; Change slideshow direction Ok Toggle replay display mode Media player key bindings for video files Back Return to file list Red Open playlist if more than one file in the playlist, otherwise jump to beginning of file Green Jump 1 min back Yellow Jump 1 min forward Stop/Blue Stop replay User7 Random play / normal play 1, User8 Jump 20 s back 3, User9 Jump 20 s forward 2 Move subtitles up 5 Move subtitles down Down/Pause Pause replay Up/Play Play Ok Toggle replay display mode Next Skip to next file when replaying playlist Prev Skip to previous file when replaying playlist FastRew/Left Play slower FastFwd/Right Play faster Media player key bindings for audio files Back Return to file list Red Open playlist Green Jump 1 min back Yellow Jump 1 min forward Stop/Blue Stop replay 0...9 Use to select a file from the playlist according to its position on the playlist Down/Pause Pause replay Up/Play Play Ok Toggle replay display mode Next/Right Skip to next file Prev/Left Skip to previous file or restart the currently playing file if more than three seconds has been played back already FastRew/FastFwd Play faster/slower User7 Random play / normal play If media file includes multiple subtitles (DVD, .mkv file, ...), subtitle language can be selected with VDR Subtitle key or from DVD subtitle menu. Plugin uses VDR's preferred subtitle language settings. DVD player key bindings Up/Down/Left/Right/Ok/Back DVD menu navigation when DVD menu is active Red Access DVD menu(s) Green Jump 1 min back Yellow Jump 1 min forward Stop/Blue/Back Stop replay Ok / Info Toggle replay display mode 1 / User8 Jump 20 s back 3 / User9 Jump 20 s forward Pause / Down Pause replay Play / Up Play 6 / Next, Next chapter 4 / Prev Previous chapter 9 Next title 7 Previous title Info Show progress display FastRew/FastFwd, Left/Right Play faster/slower DVD playback DVD images Media player supports playing DVDs directly from hard disk. Found DVD folders are marked with 'D' in media player file list. Plugin detects folders as DVDs if there is file Name_Of_DVD/VIDEO_TS/VIDEO_TS.IFO. It is also possible to replay DVD as VDR recording by creating empty recording directory and renaming or symlinking .VOBs of selected title to 00?.vdr files. DVD menus (VTS_??_0.VOB) should _not_ be copied. Audio can be selected from main menu just as with normal VDR recordings. For seeking it is necessarily to create index.vdr file with genindex or similar tool. DVD discs "Real" DVD discs (accessible from /dev/dvd) can be played from xineliboutput plugin menu. In case of remote frontend (vdr-sxfe/vdr-fbfe) DVD drive of _remote client_ is used. Audio track can be selected from VDR audio track menu (keys "Menu" + "Green" or "Audio") or from DVD menu. DVD subtitle language can be selected with VDR Subtitle key or from DVD subtitle menu. Plugin uses VDR's preferred subtitle language settings. Aspect ratio setting default Aspect ratio is calculated from display resolution. 4:3 4:3 video is scaled to fill whole window; 16:9 video has black bars at top and bottom 16:9 16:9 video is scaled to fill whole window; 4:3 video has black bars at left and right. 16:10 auto 4:3 and 16:9 are scaled to fill whole window. (useful if TV can "smart scale" 4:3 video to 16:9) Shortcut key macros It is possible to change some settings and execute actions with user-defined key macros and VDR User? keys. Supported settings and corresponding key sequences in VDR keymacros.conf format are: Start replaying DVD (User? @xineliboutput Red 0) Start replaying Title 1 from DVD (User? @xineliboutput Red 1) <reserved> (User? @xineliboutput Red 2) Toggle aspect ratio (User? @xineliboutput Red 3) Toggle letterbox -> 16:9 cropping (User? @xineliboutput Red 4) Toggle stereo -> 5.1 upmix (User? @xineliboutput Red 5) Toggle 5.1 -> surround downmix (User? @xineliboutput Red 6) Toggle de-interlacing (User? @xineliboutput Red 7) Toggle local frontend on/off (User? @xineliboutput Red 8) Start replaying default playlist or file pointed by symlink $(CONFDIR)/plugins/xineliboutput/default_playlist (User? @xineliboutput Red 9) Increase audio delay (User? @xineliboutput Red Up) Decrease audio delay (User? @xineliboutput Red Down) Toggle the video aspect ratio (User? @xineliboutput Red Right) Special frontend control keys When frontend is started with --hotkeys command-line option, following keyboard and LIRC keys are interpreted by vdr-sxfe/vdr-fbfe: Keyboard (console and X11 window) f, F Toggle fullscreen state d, D Toggle deinterlacing LIRC Fullscreen Toggle fullscreen state Deinterlace Toggle deinterlacing Quit Close program [ this run-time option replaces old build-time options INTERPRET_LIRC_KEYS and XINELIBOUTPUT_FE_FULLSCREEN_TOGGLE ] Xine-specific settings All xine-specific settings can be changed by editing file $(HOME)/.xine/config_xineliboutput. Default mpeg2 decoder (libmpeg2) can be switched to ffmpeg mpeg2 decoder by increasing ffmpeg decoder priority: engine.decoder_priorities.ffmpegvideo:1 (ffmpeg decoder is slower but handles errors better). Slave mode vdr-sxfe and vdr-fbfe implement simple slave mode. Slave mode is activated with command-line option --slave. In slave mode program reads CRLF-terminated commands from standard input instead of using keyboard as VDR remote controller. Supported commands are: HITK <vdrkey> Send key press event to VDR FULLSCREEN Toggle fullscreen state DEINTERLACE Toggle deinterlacing QUIT Close program Video can be drawn to existing X11 window with vdr-sxfe option --wid=<x_window_id> Distributed set-up - multiple clients and/or servers Simple multi-head setup When there is no need to watch different recordings / channels at different clients at the same time, just running vdr-[sx/fb]fe at each client is enough. In this case the same video + OSD is mirrored to all clients and all clients control the same (shared) VDR. Real multi-user setup When there is a need to have multiple independently controlled clients (each with separate video and OSD), running multiple instances of VDR is required. It doesn't matter if all VDR instances run at server or at each client. However, there are some benefits when running all instances of VDR on the same server: - less maintenance: only one installation of VDR and plugins is required - posibility to use simpler, diskless clients with less memory - Faster cutting / DVD burning / ... as there is no network between VDR and disks - no need to export and mount /video to every client - overall resource usage is lower - ... It is preferred to allow recording only at the "master" vdr. Recording the same timer on two VDR instances will most likely corrupt the recording. Besides that, doing all recordings directly from DVB card (no streamdev in middle) makes things simpler and less error prone. It is probably even impossible to do several recordings from different transponders using single streamdev instance. Timersync plugin disables recording on client VDRs. All timers are still visible at each client and timers can be created/modified at any client just as with the single VDR setup. Timersync plugin synchronizes all timer modifications between VDR instances and takes care that all recordings are made by the "master" vdr. Still, all kind of autotimer plugins etc. that generate timers should be activated only at server vdr (there shouldn't be any reasons to run multiple instances of such plugins). Simplified example: (xinelibout and streamdev plugins required) Start 3 VDRs at server: "Master" VDR: controls all DVB cards, does all recordings, server for client 1 vdr -c /etc/vdr \ -P"xineliboutput --local=none --remote=37890" \ -Pstreamdev-server VDR server for client 2: vdr -c /etc/vdr2 \ -D 10 -p 2102 \ -P"xineliboutput --local=none --remote=37892" \ -Pstreamdev-client VDR server for client 3 vdr -c /etc/vdr3 \ -D 10 -p 2103 \ -P"xineliboutput --local=none --remote=37894" \ -Pstreamdev-client + all possible other options and plugins. - Using -D 10 option for client VDR instances "forces" all DVB cards for master VDR. - Each VDR instance must have its own configuration directory (-c option). - Each xineliboutput server uses different port - Streamdev plugin is used to provide live view for client VDR's. It is not required to just watch recordings. To correctly configure vdr-streamdev plugin, see streamdev plugin's README. - Using suspendoutput plugin with some proper timeout value in VDR instances might be good idea - it releases streamdev VTP connection and server-side DVB devices for other use when the client is not in use. Starting clients: Client 1: vdr-sxfe Client 2: vdr-sxfe xvdr://<server ip>:37892 Client 3: vdr-sxfe xvdr://<server ip>:37894 - If RTP is used between vdr and vdr-sxfe, using separate RTP address or port for each xineliboutput server instance might be good idea. VDR Logo The VDR logo was designed by Jan Grell. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/Makefile������������������������������������������������������������������������0000644�0001750�0001750�00000031131�13061253352�013663� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # Makefile for a Video Disk Recorder plugin # # See the main source file 'xineliboutput.c' for copyright information and # how to reach the author. # # $Id$ # # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. # By default the main source file also carries this name. PLUGIN = xineliboutput _default: all # Keep VDR Makefile happy - it requires $(LIBDIR)/.$(APIVERSION) somewhere in this file ... # check for Apple Darwin ARCH_APPLE_DARWIN = no ifeq ($(shell gcc -dumpmachine | grep -q 'apple-darwin' && echo "1" || echo "0"), 1) ARCH_APPLE_DARWIN = yes endif # # Override configuration here or in ../../../Make.config # #NOSIGNAL_IMAGE_FILE=/usr/share/vdr/xineliboutput/nosignal.mpv #STARTUP_IMAGE_FILE=/usr/share/vdr/xineliboutput/logodisplay.mpv XINELIBOUTPUT_CONFIGURE_OPTS = ### ### The version number of this plugin (taken from the main source file): ### VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | cut -d'"' -f2) ### ### The C++ compiler and options: ### CXX ?= g++ CC ?= gcc OPTFLAGS ?= ifeq ($(ARCH_APPLE_DARWIN), yes) CXXFLAGS ?= -O3 -pipe -Wall -Woverloaded-virtual -fPIC -g -fno-common -bundle -flat_namespace -undefined suppress CFLAGS ?= -O3 -pipe -Wall -fPIC -g -fno-common -bundle -flat_namespace -undefined suppress LDFLAGS_SO ?= -fvisibility=hidden else CXXFLAGS ?= -O3 -pipe -Wall -Woverloaded-virtual -fPIC -g CFLAGS ?= -O3 -pipe -Wall -fPIC -g LDFLAGS_SO ?= -fvisibility=hidden LDFLAGS_SO += -shared endif ### ### The directory environment: ### # Use package data if installed...otherwise assume we're under the VDR source directory: PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr)) BINDIR = $(call PKGCFG,bindir) LIBDIR = $(call PKGCFG,libdir) LOCDIR = $(call PKGCFG,locdir) PLGCFG = $(call PKGCFG,plgcfg) VIDEODIR = $(call PKGCFG,videodir) TMPDIR ?= /tmp INSTALL ?= install ### ### Allow user defined options to overwrite defaults: ### -include Make.config ifeq ($(strip $(BINDIR)),) BINDIR = /usr/bin endif ### ### check for VDR ### APIVERSION = $(call PKGCFG,apiversion) VDR_TREE = no ifeq ($(strip $(APIVERSION)),) $(warning ********************************************************) $(warning VDR not detected ! VDR plugins will not be compiled. ) $(warning ********************************************************) CONFIGURE_OPTS += --disable-vdr else export CFLAGS = $(call PKGCFG,cflags) export CXXFLAGS = $(call PKGCFG,cxxflags) ifeq ($(VDRDIR),) $(warning Building outside VDR source tree) else $(warning Building inside VDR source tree) VDR_TREE = yes endif endif ### Allow user defined options to overwrite defaults: -include $(PLGCFG) ### ### run configure script ### config.mak: Makefile configure @echo Running configure @sh configure --cc="$(CC)" --cxx="$(CXX)" $(CONFIGURE_OPTS) $(XINELIBOUTPUT_CONFIGURE_OPTS) -include config.mak ### ### The name of the distribution archive: ### ARCHIVE = $(PLUGIN)-$(VERSION) PACKAGE = vdr-$(ARCHIVE) ### ### The name of executable and libraries ### VDRPLUGIN = libvdr-$(PLUGIN).so.$(APIVERSION) VDRPLUGIN_SXFE = lib$(PLUGIN)-sxfe.so.$(VERSION) VDRPLUGIN_FBFE = lib$(PLUGIN)-fbfe.so.$(VERSION) VDRSXFE = vdr-sxfe VDRFBFE = vdr-fbfe XINEINPUTVDR = xineplug_inp_xvdr.so XINEPOSTAUTOCROP = xineplug_post_autocrop.so XINEPOSTSWSCALE = xineplug_post_swscale.so XINEPOSTAUDIOCHANNEL = xineplug_post_audiochannel.so ### ### which programs and libs to build ### TARGETS_VDR = TARGETS_FE = TARGETS_XINE = ifeq ($(XINELIBOUTPUT_VDRPLUGIN), yes) TARGETS_VDR += $(VDRPLUGIN) endif ifeq ($(XINELIBOUTPUT_XINEPLUGIN), yes) TARGETS_XINE += $(XINEINPUTVDR) $(XINEPOSTAUTOCROP) $(XINEPOSTSWSCALE) $(XINEPOSTAUDIOCHANNEL) endif ifeq ($(XINELIBOUTPUT_X11), yes) TARGETS_FE += $(VDRSXFE) ifeq ($(XINELIBOUTPUT_VDRPLUGIN), yes) TARGETS_VDR += $(VDRPLUGIN_SXFE) endif endif ifeq ($(XINELIBOUTPUT_FB), yes) TARGETS_FE += $(VDRFBFE) ifeq ($(XINELIBOUTPUT_VDRPLUGIN), yes) TARGETS_VDR += $(VDRPLUGIN_FBFE) endif endif ### ### Includes and Defines (add further entries here): ### ifeq ($(ARCH_APPLE_DARWIN), yes) INCLUDES += -I/sw/include LIBDIRS += -L/sw/lib LIBS += $(LIBDIRS) else LIBS += -lrt endif DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' \ -D_REENTRANT -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ -DVIDEODIR='"$(VIDEODIR)"' -DXINELIBOUTPUT_VERSION='"$(VERSION)"' ifdef NOSIGNAL_IMAGE_FILE DEFINES += -DNOSIGNAL_IMAGE_FILE='"$(NOSIGNAL_IMAGE_FILE)"' endif ifdef STARTUP_IMAGE_FILE DEFINES += -DSTARTUP_IMAGE_FILE='"$(STARTUP_IMAGE_FILE)"' endif ### ### The object files (add further files here): ### # VDR plugin ifeq ($(XINELIBOUTPUT_VDRPLUGIN), yes) OBJS = $(PLUGIN).o device.o frontend.o osd.o config.o menu.o setup_menu.o \ menuitems.o media_player.o equalizer.o \ frontend_local.o frontend_svr.o tools/avahi.o \ tools/cxsocket.o tools/udp_pes_scheduler.o \ tools/backgroundwriter.o tools/playlist.o tools/http.o \ tools/vdrdiscovery.o tools/time_pts.o tools.o \ tools/metainfo_menu.o logdefs.o tools/rle.o OBJS_MPG = black_720x576.o nosignal_720x576.o vdrlogo_720x576.o endif # frontends OBJS_FE_SO = xine_frontend.o logdefs.o \ xine/post.o xine/vo_hook.o xine/vo_osdscaler.o xine/vo_osdreorder.o xine/vo_lastpts.o \ xine/vo_frameoutput.o \ tools/rle.o OBJS_FE = $(OBJS_FE_SO) \ xine_frontend_main.o \ xine_frontend_lirc.o xine_frontend_kbd.o xine_frontend_cec.o \ tools/vdrdiscovery.o OBJS_SXFE_SO = xine_sxfe_frontend.o $(OBJS_FE_SO) OBJS_SXFE = xine_sxfe_frontend.o $(OBJS_FE) OBJS_FBFE_SO = xine_fbfe_frontend.o $(OBJS_FE_SO) OBJS_FBFE = xine_fbfe_frontend.o $(OBJS_FE) ifneq ($(HAVE_DBUS_GLIB_1), no) OBJS_SXFE += tools/gnome_screensaver.o OBJS_SXFE_SO += tools/gnome_screensaver.o endif # xine plugins OBJS_XINEINPUTVDR = xine_input_vdr.o xine/demux_xvdr.o \ xine/ts2es.o xine/demux_xvdr_tsdata.o \ xine/adjustable_scr.o xine/xvdr_metronom.o xine/osd_manager.o \ tools/rle.o tools/ts.o tools/pes.o tools/mpeg.o tools/h264.o tools/h265.o OBJS_XINE = $(OBJS_XINEINPUTVDR) xine_post_autocrop.o xine_post_swscale.o xine_post_audiochannel.o ### ### Implicit rules: ### %.o: %.c $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $(CFLAGS_VDR) -o $@ $< ### ### Dependencies: ### MAKEDEP = $(CXX) -MM -MG DEPFILE = .dependencies $(DEPFILE): Makefile config.mak @rm -f $@ @for i in $(OBJS:%.o=%.c) $(OBJS_SXFE:%.o=%.c) $(OBJS_FBFE:%.o=%.c) $(OBJS_XINE:%.o=%.c) ; do \ $(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) -MT "`dirname $$i`/`basename $$i .c`.o" $$i >>$@ ; \ done -include $(DEPFILE) DEFINES += -Wall ### ### Rules: ### mpg2c: mpg2c.c $(CC) $(CFLAGS) $(LDFLAGS) mpg2c.c -o $@ # data black_720x576.c: mpg2c black_720x576.mpg @./mpg2c black black_720x576.mpg black_720x576.c nosignal_720x576.c: mpg2c nosignal_720x576.mpg @./mpg2c nosignal nosignal_720x576.mpg nosignal_720x576.c vdrlogo_720x576.c: mpg2c vdrlogo_720x576.mpg @./mpg2c vdrlogo vdrlogo_720x576.mpg vdrlogo_720x576.c # C code (xine plugins and frontends) $(sort $(OBJS_SXFE) $(OBJS_FBFE) $(OBJS_XINE)): %.o: %.c $(CC) $(CFLAGS) -c $(DEFINES) $(INCLUDES) $(CFLAGS_X11) $(CFLAGS_AVUTIL) $(OPTFLAGS) -o $@ $< ### Internationalization (I18N): PODIR = po ifeq ($(XINELIBOUTPUT_VDRPLUGIN), yes) I18Npo = $(wildcard $(PODIR)/*.po) I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) I18Npot = $(PODIR)/$(PLUGIN).pot %.mo: %.po msgfmt -c -o $@ $< $(I18Npot): $(wildcard *.c) xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<phintuka@users.sourceforge.net>' -o $@ $^ %.po: $(I18Npot) msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $< @touch $@ $(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo @echo Installing $^ install -D -m644 $< $@ endif .PHONY: i18n i18n: $(I18Nmo) $(I18Npot) install-i18n: $(I18Nmsgs) ### ### targets ### XINELIBOUTPUT_INSTALL_MSG = \ $(warning *********************** xineliboutput ***************************) \ $(warning Xine plugins and frontends will not be installed automatically. ) \ $(warning To install files execute "make install" in ) \ $(warning $(shell echo `pwd`)) \ $(warning *****************************************************************) install : XINELIBOUTPUT_INSTALL_MSG = .PHONY: all all: config $(TARGETS_VDR) frontends i18n frontends: config $(TARGETS_FE) $(TARGETS_XINE) $(XINELIBOUTPUT_INSTALL_MSG) config: config.mak .PHONY: config .PHONY: frontends install dist clean # # VDR plugin # $(VDRPLUGIN): $(OBJS) $(OBJS_MPG) $(CXX) $(CXXFLAGS) $(LDFLAGS_SO) $(LDFLAGS) -shared $(OBJS) $(OBJS_MPG) $(LIBS) $(LIBS_VDR) -o $@ ifeq ($(VDR_TREE), yes) $(INSTALL) $@ $(LIBDIR)/ endif install-lib: $(TARGETS_VDR) ifeq ($(XINELIBOUTPUT_VDRPLUGIN), yes) @echo Installing $^ @mkdir -p $(DESTDIR)$(LIBDIR) install -D $^ $(DESTDIR)$(LIBDIR)/ endif install: install-lib install-i18n # # vdr-sxfe # $(VDRPLUGIN_SXFE): $(OBJS_SXFE_SO) $(CC) $(CFLAGS) $(LDFLAGS_SO) $(LDFLAGS) $(OBJS_SXFE_SO) $(LIBS_X11) $(LIBS_XINE) $(LIBS_JPEG) -o $@ ifeq ($(VDR_TREE), yes) $(INSTALL) $@ $(LIBDIR)/ endif $(VDRSXFE): $(OBJS_SXFE) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS_SXFE) $(LIBS_X11) $(LIBS_XINE) $(LIBS_JPEG) $(LIBS_CEC) $(LIBS_PTHREAD) -o $@ # # vdr-fbfe # $(VDRPLUGIN_FBFE): $(OBJS_FBFE_SO) $(CC) $(CFLAGS) $(LDFLAGS_SO) $(LDFLAGS) $(OBJS_FBFE_SO) $(LIBS_XINE) $(LIBS_JPEG) -o $@ ifeq ($(VDR_TREE), yes) $(INSTALL) $@ $(LIBDIR)/ endif $(VDRFBFE): $(OBJS_FBFE) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS_FBFE) $(LIBS_XINE) $(LIBS_JPEG) $(LIBS_CEC) $(LIBS_PTHREAD) -o $@ # # xine plugins # $(XINEINPUTVDR): $(OBJS_XINEINPUTVDR) $(CC) $(CFLAGS) $(LDFLAGS_SO) $(LDFLAGS) $(OBJS_XINEINPUTVDR) $(LIBS_XINE) $(LIBS_AVUTIL) $(LIBS_PTHREAD) -o $@ $(XINEPOSTAUTOCROP): xine_post_autocrop.o $(CC) $(CFLAGS) $(LDFLAGS_SO) $(LDFLAGS) $< -o $@ $(LIBS_XINE) $(XINEPOSTSWSCALE): xine_post_swscale.o $(CC) $(CFLAGS) $(LDFLAGS_SO) $(LDFLAGS) $< -o $@ $(LIBS_XINE) $(XINEPOSTAUDIOCHANNEL): xine_post_audiochannel.o $(CC) $(CFLAGS) $(LDFLAGS_SO) $(LDFLAGS) $< -o $@ $(LIBS_XINE) # # install # install: all ifeq ($(XINELIBOUTPUT_XINEPLUGIN), yes) @mkdir -p $(DESTDIR)$(XINEPLUGINDIR)/post @echo Installing $(DESTDIR)$(XINEPLUGINDIR)/$(XINEINPUTVDR) @-rm -rf $(DESTDIR)$(XINEPLUGINDIR)/$(XINEINPUTVDR) @$(INSTALL) -m 0644 $(XINEINPUTVDR) $(DESTDIR)$(XINEPLUGINDIR)/$(XINEINPUTVDR) @echo Installing $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTAUTOCROP) @-rm -rf $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTAUTOCROP) @$(INSTALL) -m 0644 $(XINEPOSTAUTOCROP) $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTAUTOCROP) @echo Installing $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTSWSCALE) @-rm -rf $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTSWSCALE) @$(INSTALL) -m 0644 $(XINEPOSTSWSCALE) $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTSWSCALE) @echo Installing $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTAUDIOCHANNEL) @-rm -rf $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTAUDIOCHANNEL) @$(INSTALL) -m 0644 $(XINEPOSTAUDIOCHANNEL) $(DESTDIR)$(XINEPLUGINDIR)/post/$(XINEPOSTAUDIOCHANNEL) endif ifeq ($(XINELIBOUTPUT_FB), yes) @echo Installing $(DESTDIR)$(BINDIR)/vdr-fbfe @mkdir -p $(DESTDIR)$(BINDIR) @-rm -rf $(DESTDIR)$(BINDIR)/vdr-fbfe @$(INSTALL) -m 0755 vdr-fbfe $(DESTDIR)$(BINDIR)/vdr-fbfe endif ifeq ($(XINELIBOUTPUT_X11), yes) @echo Installing $(DESTDIR)$(BINDIR)/vdr-sxfe @mkdir -p $(DESTDIR)$(BINDIR) @-rm -rf $(DESTDIR)$(BINDIR)/vdr-sxfe @$(INSTALL) -m 0755 vdr-sxfe $(DESTDIR)$(BINDIR)/vdr-sxfe endif dist: $(I18Npo) clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE) @tar czf $(PACKAGE).tgz --exclude=CVS -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @echo Distribution package created as $(PACKAGE).tgz clean: @-rm -f $(DEPFILE) *.so* *.o *.tgz core* *~ *.flc *.bak \ tools/*.o tools/*~ tools/*.flc xine/*.o xine/*~ \ xine/*.flc $(VDR_FBFE) $(VDR_SXFE) mpg2c black_720x576.c \ nosignal_720x576.c vdrlogo_720x576.c vdr-sxfe vdr-fbfe \ features.h config.mak configure.log @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������xineliboutput-2.0.0/HISTORY�������������������������������������������������������������������������0000644�0001750�0001750�00000062773�13061253352�013327� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������VDR Plugin 'xineliboutput' Revision History ------------------------------------------- 2017-03-12: Version 2.0.0 - Moved from CVS to GIT (see README) - Support vdr-2.3.1 - Dropped support for vdr < 2.0 - Fixed problems with xine-lib 1.2.7+ - Fixed ts demuxing with some video streams (Thanks to Niedermeier Guenter). - Fixed primary device switching. - Fixed segfault when playing audio-only stream with visualization (Thanks to Tobias Grimm). - Fixed toggling fullscreen mode. - Fixed lost post processing when stream is reconnected. - Fixed HUD OSD crash with UHD resolution video output. - Added mDNS announces (tested with VLC). - Added an option to trigger vdr-suspendoutput when there are no connected clients. - Added HDMI-CEC input support (libcec). - Added support for cDevice::ScaleVideo(). - Added an option to disable media player resume file creation. - Added PRIM command into SVDRP interface. - Added option to use True Color OSD when no client is connected. - Added support for HEVC. - Improved H.264 support. - Bug fixes. - Build system updates. - Translation updates. 2013-08-20: Version 1.1.0 - Added simple touchscreen remote controller to vdr-sxfe - Added XShape support for HUD OSD: opaque HUD OSD when no compositing manager (Thanks to grueni75) - Added support for background image streams (display images or videos while playing music) (Thanks to Helmar Gerloni) - Added manual ordering to playlist menu (Thanks to Marco Skambraks) - Hide mouse cursor after 2 seconds mouse inactivity (Thanks to Gerald Dachs) - Added support for Alt and Ctrl key modifiers to X11 keyboard remote controller. - Added support for yaepg(hd) video window with HUD OSD (Thanks to grueni75) - New, smoother live mode SCR sync algorithm (Thanks to Dirk Brenken) - Scale or re-position SD subtitles on HD OSD (Thanks to Rolf Ahrenberg) - Added an option to select subtitle OSD scaling or re-positioning - Use xineliboutput/allowed_hosts.conf instead of svdrphosts.conf - Implemented demuxer with mpeg-ts support - Initial support for vdr-1.7.15 - Support for multilayer OSD - Improved OSD scaling (OSD reacts now immediately to frame size changes) - Replaced cIConv with VDR's cCharConv. - Supports only for vdr-1.6.0 or later. - Implemented build-time configuration script - Fixed aspect ratio problems when using ffmpeg mpeg2 decoder - H.264 updates - Added support for .ass subtitles to media player (Thanks to zaverel) - Added support for .smi subtitles to media player - Added OpenGL HUD OSD support: HUD OSD without compositing / window manager (thanks to grueni75) - Added tearing-free OpenGL video output support with high-quality OSD (thanks to grueni75) 2011-07-10: Version 1.0.7 (branch-1_0_x) Backported bugfixes from CVS trunk: - Accept short X11 display names (:0) - Improved vdr server discovery when server is listening on all interfaces Features: - Updated Italian translations (Thanks to Diego Pierotto) - Default to UDP transport. RTP is enabled only if requested on command line. - Improved automatic BluRay subtitle selection - React to X11 display size changes - Do not show hidden files in media player file browser - Added config entry to show hidden files in media player file browser - Added --geometry and --window options to vdr plugin. - Allow setting window position when starting in fullscreen mode. 2011-02-28: Version 1.0.6 (CVS branch-1_0_x for vdr.1.4.x and vdr-1.6.x) Backported bugfixes from CVS trunk: - Fixed VDR OSD when playing DVDs - Fixed compilation with libextractor 0.6.0 (Thanks to Martin Knoll) - Fixed blanking - Fixed grab with jpeg7 and later - Fixed segfault when grabbing - Fixed unaligned access in swscale post plugin - Fixed remote access thru NAT firewall - Fixed OSD sending problems in remote mode - Fixed handling of kBack in DVD player (Reported by Marco Skambracks) - Fixed generating LIRC key release events (bug #2919256) (Thanks to Claudius Peschke) - Fixed mixer volume being reset at vdr-sxfe start when using software volume control (Thanks to Juergen Lock) - Fixed lot of compiler warnings (Thanks to Winfried Koehler) - Reject clients when not primary device - Do not lose the key pressed while displaying "Queued to playlist" message (Thanks to Marco Skambraks) - Escape only raw file names, not complete mrls (bug #2670927) - Add mrl prefix ("file:") to escaped subtitle file name - Query CDDA track count before starting playback - Create frontend config directory if it does not exist (bug #2796595) Features: - Added Chinese translations (Thanks to NanFeng) - Added Estonian translations (Thanks to Arthur Konovalov) - Added Ukrainian translations (Thanks to Yarema aka Knedlyk) - Added FreeBSD compatibility (Thanks to Juergen Lock and Joerg Pulz) - Added support for libextractor 0.6.0 API (Thanks to Martin Knoll) - Updated logo (Thanks to Stefan Wagner) - Configurable media player menu - Improved automatic letterbox cropping (Thanks to Andreas Auras) - Replaced build-time options INTERPRET_LIRC_KEYS and FE_TOGGLE_FULLSCREEN with run-time option --hotkeys - Added system shutdown functionality to frontends - Added support for video in X11 root window - Added support for entering text using letter keys - Added "Rewind" key to media files menu - Added DVD menu navigation keys option - Added support for BluRay and m2ts files - Added --terminal, --buffers and --geometry options to vdr-??fe - Added Gnome screensaver support to vdr-sxfe (Thanks to Alex Stansfield) - Added sharpness and noise reduction setup options for VDPAU - Added image grabbing in PNM format - Added image grabbing support for VDPAU (Thanks to Andreas Auras) - Added VDPAU support to letterbox cropping (Thanks to Andreas Auras) - Added '-C'/'--config' command-line option to specify xine's configuration file. - Added an option to limit number of remote clients - Added an option to adjust the stepping value that is used for metronom live mode sync (Thanks to Andreas Auras) - Added an option to use smoother SCR tuning (Thanks to Thomas Hilber and Paul Menzel) (smooth tuning can be enabled with xine-lib config key media.xvdr.smooth_scr_tuning) - Added an option to use xine-lib DVB subtitles decoder 2010-02-07: Version 1.0.5 (branch-1_0_5) Backported bugfixes from CVS trunk: - Fixed compilation with gcc-4.4.0 and recent glibc - Fixed memory leaks - Fixed setting focus in vdr-sxfe fullscreen mode - Fixed sending complex OSDs - Fixed image player when playing non-local mrls (Thanks to Rolf Ahrenberg) - Changed the default grab quality to match VDR's documentation (Thanks to Rolf Ahrenberg) Features: - Added preliminary SVDRP interface (Thanks to Rolf Ahrenberg) - Added StartFrontend-1.0 service (Thanks to durchflieger) 2009-02-12: Version 1.0.4 (branch-1_0_x) Backported bugfixes from CVS trunk: - Updated Italian translations (Thanks to Diego Pierotto) - Fixed default grab quality (Thanks to Jochen Dolze, patch #2454827) - Added math library (-lm) to vdr-sxfe when building with Xrender / HUD OSD support (Thanks to Anssi Hannula) - Reduced H.264 logging - Fixed CD track count query - Fixed mrl backwards compability 2008-10-24: Version 1.0.3 (branch-1_0_x) Backported bugfixes from CVS trunk: - Added missing sxfe display locks (Thanks to Antti Seppälä) - Modified HUD OSD scaling parameters (Thanks to Rolf Ahrenberg) - Fixes to HUD OSD drawing (Thanks to Rolf Ahrenberg) - Fixed --aspect=auto:path_to_script option (Thanks to Armin Diedering) - Fixed playing first track of audio CD (requires xine-lib 1.1.5) - OS X build fixes (Thanks to Tero Siironen) - Updated finnish translations (Thanks to Rolf Ahrenberg) - Updated Italian translations (Thanks to Diego Pierotto) 2008-10-04: Version 1.0.2 (branch-1_0_x) Backported bugfixes from CVS trunk: - Fixed segfault when committing uninitialized OSD (Thanks to Rolf Ahrenberg) - Fixed buffer errors when switching from HD channel to SD channel - Fixed selecting DVD subtitles language from DVD menus - Fixed setting DVD subtitles preferred language - Fixed missing VDR OSD while playing DVDs - Fixed DVD menu navigation when menu is not in title 0 - Fixed infinite loop when trying to replay unaccessible DVD - Fixed infinite loop when trying to play only one unacessible or very short music file - Fixed replaying images from network sources (http://, ...) (Thanks to Rolf Ahrenberg) - Fixed segfault when media file meta info contains thumbnails (Thanks to Petri Helin) - Fixed smooth trickspeed setup menu entry with VDR-1.5.10+ (Thanks to Timo Eskola) - Fixed vdr-sxfe icon in 64-bit systems - Fixed updating window title when protocol is part of mrl - Fixed German translation for "Play DVD disc >>" (Thanks to Helmar Gerloni) - Increased timeout when opening media files from network sources (Thanks to Tobias Grimm) - OS X build fixes (Thanks to Tero Siironen) - xine-lib 1.2 updates - Added WM class hint ("VDR") to vdr-sxfe windows (Thanks to Rolf Ahrenberg) 2008-05-07: Version 1.0.1 - Fixed freezes while zapping and/or seeking - Added metainfo menu to media player (Petri Helin) 2008-04-14: Version 1.0.0 - Added Italian translations (Thanks to Diego Pierotto) - Added Czech translations (Thanks to Maya) - Added HUD OSD (Blend OSD using graphics hardware) and --hud command-line option. (Thanks to Antti Seppälä and Rolf Ahrenberg) - Added support for libextractor metadata parsing library (Petri Helin) - Added service interface for launching media player (Suggested by Tobias Grimm) - Added configuration options for unsharp and denoise3d post plugins (Petri Helin) - Fixed the case when watching image files and the first one just flashes and gets replaced by black image (Petri Helin) - Added support for VDR 1.5.x and 1.6.0 - Dropped legacy code for vdr-1.3.x - Added support for xine-lib up to 1.1.11.1 - Added support for xine-lib 1.2 hg branch - Changed mrl syntax from xvdr[:proto]:// to xvdr[+proto]:// - Added video softwarwe scaling support - Added non-linear 4:3 -> 16:9 "smart" scaling - Added configure option for video aspect ratio (Petri Helin) - Added media player options for enabling or disabling metainfo types, metainfo scanner and metainfo caching (Petri Helin) - Added support for smooth fast forward (Thanks to Timo Eskola) - Added setup option to limit trick speed - Removed support for arts audio output - Added setup option to change external subtitle (.sub/.srt) font size - Added command-line option for binding to specific local interface address - Initial support for H.264 video - Support for HD-resolution OSD (Petri Helin). Requires patched vdr. - Increased local frontend initialization timeout (Thanks to Mikko Vartiainen) - Removed configuration option to disable OSD downscaling - Improved media player playlist handling and menu (Petri Helin) - Fixed DVD menu domain detection (Petri Helin) - Fixed DVD title and chapter information shown on the OSD (Petri Helin) - Improved key mappings for audio player (Petri Helin) - Allow users to add single files to playlist as well as whole directories (Petri Helin) - Fixed "TCP fifo full" problem - Added hotkeys for audio delay (Thanks to Timo Eskola) - Enabled streaming of external subtitle files for remote frontends - Added support for multithreaded video decoding. Auto-detect number of CPUs. - Show address of current VDR server in X title bar - Added support to control HW aspect ratio with external script - Added support to output video and OSD to existing X11 window - Allow fine-tuning of SCR (should reduce frame drops/duplications and make video smoother) - Support using ffmpeg mpeg2 video decoder instead of libmpeg2 decoder - Improved PTS warp detection - Restore DPMS state at exit - Fixed sxfe window position when returning from fullscreen mode (Thanks to Timo Eskola) 2007-05-17: Version 1.0.0rc2 - Workaround for xine-lib demux_mpeg_block PTS wrap issue (this should fix daily picture freezes) - Support for denoise3d and unsharp post plugins (Thanks to Petri Helin) - Fixed media player random play kNext (next file) handling (Thanks to Petri Helin) - Fixed media player MsgReplaying status messages (Thanks to Petri Helin) - Fixed closing DVD player with Back key (Thanks to Petri Helin) - Improved media player resume file creation - Fixed UDP segfault - Mac OS X build fixes (Thanks to Tero Siironen) - Updated remote.conf example - Added support for xine-lib software volume control (useful with digital audio output) - Removed (unused) decoder priority setting - Fixed yuy2 frame grabbing - Adapted for xine-lib 1.1.5 - Added vdr-sxfe fullscreen and de-interlace toggling with lirc keys "Fullscreen" and "Deinterlace" 2007-03-17: Version 1.0.0rc1 - PLUGIN HOMEPAGE CHANGED - Fixed audio CD replay (cdda:/) - Adapted for xine-lib 1.1.4 - Adapted for vdr-1.5.1 (Thanks to Rolf Ahrenberg) - Mac OS X compability fixes (Thanks to Tero Siironen) - DXR3 added to list of output devices (experimental; Thanks to Ville Skyttä) - Subtitle selection menu updated to use VDR audio menu skin - New subtitle macro key handling (identical to VDR kAudio) - Added preferred subtitle language selection for media player - Updated trick speed modes and still image handling - Remote mode can now survive longer network delays - Fixed tvtime options (Thanks to Petri Helin) - Fixed using video driver "none" - Simple "slave mode" for remote frontends - Added automatic re-connection to remote frontends (--reconnect option) - Slow down DVD drive speed - Initialize video size from stream info when playing slave streams, (Thanks to Antti Seppälä) - Makefile shell scripts modified to run in dash (Ubuntu). (Thanks to realKano@directbox.com) - Increased frame-based buffering time after channel changes - Use iconv to translate id3 tags from utf8 to VDR charset - Use /dev/dsp as OSS default device (Thanks to Ville Skyttä) - Lot of small fixes and enhancements, complete log in CVS 2007-01-07: Version 1.0.0pre7 - Added possibility to add files to playlist - Added playlist menu to media player - Added "Play Audio CD" / "Play remote Audio CD" entry to plugin menu - Makefile modified to allow overriding default directory environment. Using "install" to install files. (Thanks to Timo Weingärtner) - Added metainfo caching to media player. Cached metainfo is stored to ".xineliboutput-playlist.m3u" files by default. - Added support for playlists inside playlists (ex. http://.../?.pls entries in playlists) - Added playlist HTTP download support to playlists (curl required) - lirc forwarding updated (synced with vdr-1.4.3-2): added re-connecting to lircd. - Lirc forwarding key repeat fixed (Thanks to Timo Ruottinen). - Display metainfo (ID3 etc) instead of file name in (audio)player - Fixed deadlock in audio post plugin loading and wiring (in some cases plugins were loaded multiple times) - Fixed buffer overflow problems in xineliboutput device polling - Fixed buffer overflow problems in UDP packet scheduler queue - Several new media file types added to media player - Subtitle type .ssa added - Parsing for .pls, .asx and .ram playlists added - Eliminated some warnings when compiling to 64bit (Thanks to Anssi Hannula) - Vidix(fb) added to supported video drivers (Thanks to Antti Seppälä) - Media player/audio file browser does not anymore show video files - When replaying music files, replay moves to next file automatically - Decoder setup menu moved to Local setup menu (decoder settings have effect on local frontend only) - Implemented simple RTSP streaming support (rtsp://vdr-host:xineliboutput-port/) - Implemented simple HTTP streaming support (http://vdr-host:xineliboutput-port/) - Fixed control channel disconnection detection in frontend_svr.c - Media player: Try to detect when navigating in DVD menus and change functions of Up/Down/Left/Right/Ok/Back keys when in menus - Added RFC2974 SAP (Session Announcement Protocol) implementation - Now using RFC3550 RTP headers when multicasting 2006-10-20: Version 1.0.0pre6 - Display Audio track languages when replaying DVDs - Display DVD SPU track language names instead track numbers - Improved shortcut key support - Fixed garbage in bottom of image when using autocrop - Fixed SCR tunning when only one TCP/PIPE client - Fixed unscaled OSD scaling to display size when low-resolution video or different aspect ratio - Allow overriding default startup image - German translations (Thanks to Udo Richter) - Setup menu re-arranged - Command-line given post plugin parameters are never changed at runtime - Added string length checks to several places - Added missing frame buffer device selection - Support for ffmpeg post processing - Easier configuration for tvtime post plugin in menu - Autoplay list support for xine (patch from sf feature request #1561688) - Removed busy loop from vdr-fbfe/vdr-sxfe when console was unavailable 2006-09-17: Version 1.0.0pre5 - Allow overriding default no signal image - Several fixes to post plugin handling - Fixed --audio=driver:port parsing in vdr plugin - Fixed overscan when image does not fill whole output window (bug #1556912) - Implemented simple MMX/SSE and YUY2 detection routines to autocrop - Main menu re-arranged (Thanks to Petri Helin) - Fixed display blanking aspect ratio (bug #1554070) - Lirc receiver accepts shorter LIRC commands (bug #1540896) - Fixed immediate re-configuration when setup entries are changed with repeated keys (Thanks to Petri Helin) - Implemented audio channel selection as xine post plugin (to select only left or right channel from stereo) - Several minor fixes / enhancements to autocrop plugin 2006-09-06: Version 1.0.0pre4 - WARNING: updated command-line options ! - Support for HD (larger buffers with HD content) - Simple playlist support to media player - Fixed starting replay of new file while old file is still playing - Fixed segfault in OSD downscaling - Added SPU track selection for DVDs without menu - Forcing order and location of video filter post plugins autocrop and tvtime in post plugin chain - Fixed replaying some older VDR recordings (video PID != 0xE0) (Reported by Petri Helin) - Fixed deadlock when closing xine input plugin and threads in TCP mode (reported by Tobias Grimm) - Fixed wrong XKeySym remote learning trigger when using fbfe (reported by Voitto Tuomainen) - Minor updates to playlists and file replay OSD handling - Added trick speed modes (forward only) to DVD player - Improved X11 fullscreen <-> window mode switching - Improved display blanking: blank image is now generated using last seen video frame size and aspect ratio. This should reduce OSD resizings and re-positioning when switching channels. 2006-08-25: Version 1.0.0pre3 - Fixed segfault when grabbing with remote-only frontends - Configurable speaker configuration and spdif passthru - Support for playlists (.m3u or whole directory) in media player - Separate menu item for playing music - Configuration options for letterbox cropping - Added audio visualization support for media files (originally supported only with DVB radio) - Implemented image grabbing for remote frontends - Fixed restoring primary device when using tcp transport - Executing primary device switching in main thread context - Added configurable overscan option (%) to crop frame borders when using displays without overscan 2006-08-16: Version 1.0.0pre2 - Xine plugins and frontends are not installed automatically. (Suggested by Udo Richter). - Added support for AC3 passthrough (thanks to Petri Helin) - Automatic 4:3 letterbox to 16:9 cropping pre-version - Added daemon mode to stand-alone frontends - Removed possible busy loop from lirc receiver thread - New localized texts - Fixed local media player when remote server is active and there are no clients - Option for audio-only playback (discards video) - Fixed DVD navigation in local mode (Thanks to Petri Helin) - Completed simple playlist support (play all media files in folder ; play .m3u playlist) 2006-07-23: Verson 1.0.0pre1 - Added DVD playback and navigation support to media player - Added support for DVD subtitles in VDR recordings - Fixed audio surround mode (Thanks to Petri Helin) - Added option to disable keyboard input in vdr-fbfe and vdr-sxfe (required when running as daemon) - Fixed OSD updating, closing and re-scaling under high system load 2006-07-05: Version 0.99 - Finished implementing audio stream switching - Improved VDR server detection and refused connection handling - Added option to close VDR when local frontend window is closed - Added always-on-top mode, window title and icon to vdr-sxfe 2006-06-12: Version 0.99rc5 - OSD endian problems fixed - More x64 fixes (Thanks to Anssi Hannula) - Better TCP and PIPE disconnection detection - Fixed possible race conditions in xine input plugin - Fixed image player (reported by Petri Helin) - Improved (?) X11 fullscreen mode - X11 fullscreen mode can be toggled by double-clicking window 2006-06-03: Version 0.99rc4 - Fixed missing audio after trick speed modes (thanks to Tero Saarni) - Fixed fullscreen size detection with frame buffer / DirectFB - Added unscaled OSD capability check for framebuffer / DirectFB 2006-06-02: Version 0.99rc3 - OSD is scaled to display resolution instead of video resolution when using unscaled OSD - OSD is re-centered when resolution is slighty different from 720x576 - Added configuration options for multicast parameters (address, port, TTL) - Added option to set multicast transmission always on for third-party clients (vlc, ...) - Some new x64 fixes - Several configuration menu fixes - Fixed compilation problem with xine-lib < 1.1.2 - Fixed missing audio after trick speed modes (thanks to Tero Saarni) 2006-05-18: Version 0.99rc2 - Fixed control input deadlock when using xine-ui - Experimental automatic primary device mode included - Fixed remote frontend keyboard handling for escape sequences - Added several new command-line options to stand-alone frontends - Adapted for vdr 1.4.0 - Fixed automatic server discovery - Fixed fullscreen mode when screen resolution != 720x576 - Fixed --local=none option (reported by Ulf Betlehem) - Faster channel switching - Fixed --post option - Fixed several endian problems and byte ordering in network modes (reported by Carsten Rietzschel) - Fixed segmentation fault when frontend was executed from path (reported by Carsten Rietzschel) - Fixed amd64 compilation problems (reported by Voitto Tuomainen) - Many finnish menu texts updated (patch provided by Rolf Ahrenberg) - Socket option SO_REUSEADDR added to several places to speed up VDR restart 2006-04-08: Version 0.99rc - Fixed compilation problems with gcc 3.4.5 (timer.h, osd.c). (reported by Gavin Hamill and Voitto Tuomainen) - Suspend mode removed (it is now implemented only as separate plugin) - Fixed X11 keyboard input handling - Faster seeks and channel switches in udp and rtp streaming modes - Fixed disconnection when playing to end of avi file - Fixed multi-speed modes (reported by Vladimir Monchenko) - Updated russian translations (thanks to Vladimir Monchenko) - Pipe transport fixed 2006-03-28: Version 0.99pre - Russian translations (thanks to Vladimir Monchenko) - New de-interlacing methods and options - Updated for vdr 1.3.43 - Support for xine post-processing plugins - Stand-alone frontends can now automatically find vdr (xineliboutput) server from network. - Makefile modified to auto-detect vdr. If vdr is not installed, only stand-alone frontends and xine plugin are compiled. (use "make frontends" in plugin source directory) - Xine plugin is automatically copied to xine's plugin directory instead of statically linking it to frontends. -> any xine frontend can be used. - All logging goes to syslog and honors vdr's logging level 2005-11-22: Version 0.4 - Updated for xine-lib 1.1.0 and 1.1.1 - Framebuffer and DirectFB frontend is now tested and working. - Remote frontends tested and working (TCP/UDP/RTP). - README updated. - Frontend is now loaded dynamically if needed. VDR part of plugin is not anymore linked against xine-lib and X11. -> plugin itself can be compiled and used without installing xine and/or X11 to VDR machine. Frontends still need X11 and/or xine-lib. - Makefile modified to auto-detect xine-lib and X11. If X11 is not installed, only framebuffer frontend is compiled. If xine-lib is not installed, frontends are not compiled (-> only remote frontends can be used). - Updated for vdr 1.3.34 - Stand-alone frontends (sxfe and fbfe) can now forward lirc keys to VDR. - New command-line parameters: frontend type, video driver and audio driver. - Command-line parameters now override saved configuration parameters. - Updated for xine-lib 1.0.1 2005-02-17: Version 0.3 - Updated for vdr 1.3.19 and xine-lib 1.0 - Direct playback of all xine-supported media types (+ automatic loading of external .srt/.sub subtitles) - Support for http/rtsp network streams - Image viewer - New configuration options - Improved OSD support for low-resolution video - OSD performance optimizations - Improved X server detection - Support for xxmc output driver - Support for DVB radio streams - Support for audio visualization plugins 2004-08-19: Version 0.2 - Tested with VDR versions 1.2.6, 1.3.7 and 1.3.12 - Modified to compile with xine-lib 1.0.0 (rc4) - New configuration options - Decoder can be stopped manually or using inactivity timer - Support for image grabbing - Support for localization (i18n) 2003-12-20: Version 0.1 - Modified to compile with xine-lib 1.0.0 (rc2) - Support for Xshm, Xv and XvMC. - Audio driver and port can be selected (alsa,oss,...) - X11 display location configurable (in setup.conf, OSD menu or usind DISPLAY environment variable) - Support for unscaled OSD (requires XShape X server extension) - Support for deinterlacing - Multiple bug fixes - Many new configuration options - Support for DVD plugin (GetSTC) - Support for VDR Make.config 2003-09-10 - C Compilation problems fixed - OSD scaling fixed 2003-05-23: Version 0.0.0 - Initial revision. �����xineliboutput-2.0.0/COPYING�������������������������������������������������������������������������0000644�0001750�0001750�00000043106�13061253352�013263� 0����������������������������������������������������������������������������������������������������ustar �ph������������������������������ph��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������