mplayer.c
author nathan
Sat, 29 Dec 2007 14:49:09 +0100
branchtrunk
changeset 2 4c1f7b705009
parent 0 474a1293c3c0
child 9 dc75c2890a31
permissions -rw-r--r--
release 0.10.1
     1 /*
     2  * MP3/MPlayer plugin to VDR (C++)
     3  *
     4  * (C) 2001-2007 Stefan Huelswitt <s.huelswitt@gmx.de>
     5  *
     6  * This code is free software; you can redistribute it and/or
     7  * modify it under the terms of the GNU General Public License
     8  * as published by the Free Software Foundation; either version 2
     9  * of the License, or (at your option) any later version.
    10  *
    11  * This code is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License
    17  * along with this program; if not, write to the Free Software
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    19  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    20  */
    21 
    22 #include <getopt.h>
    23 #include <malloc.h>
    24 #include <stdlib.h>
    25 #include <ctype.h>
    26 
    27 #include "common.h"
    28 
    29 #include <vdr/plugin.h>
    30 #include <vdr/player.h>
    31 #include <vdr/status.h>
    32 #include <vdr/font.h>
    33 #include <vdr/osdbase.h>
    34 #include <vdr/menuitems.h>
    35 #ifdef HAVE_BEAUTYPATCH
    36 #include <vdr/fontsym.h>
    37 #endif
    38 #if APIVERSNUM >= 10307
    39 #include <vdr/skins.h>
    40 #endif
    41 #if APIVERSNUM >= 10332
    42 #include <vdr/remote.h>
    43 #endif
    44 
    45 #if APIVERSNUM > 10307
    46 #include <vdr/menu.h>
    47 #elif APIVERSNUM == 10307
    48 class cMenuText : public cOsdMenu {
    49 private:
    50   char *text;
    51 public:
    52   cMenuText(const char *Title, const char *Text, eDvbFont Font = fontOsd);
    53   virtual ~cMenuText();
    54   void SetText(const char *Text);
    55   virtual void Display(void);
    56   virtual eOSState ProcessKey(eKeys Key);
    57   };
    58 #else
    59 class cMenuText : public cOsdMenu {
    60 public:
    61   cMenuText(const char *Title, const char *Text, eDvbFont Font = fontOsd);
    62   virtual eOSState ProcessKey(eKeys Key);
    63   };
    64 #endif
    65 
    66 #include "setup.h"
    67 #include "setup-mplayer.h"
    68 #include "menu.h"
    69 #include "player-mplayer.h"
    70 #include "data.h"
    71 #include "data-src.h"
    72 #include "i18n.h"
    73 #include "version.h"
    74 #include "service.h"
    75 
    76 const char *sourcesSub=0;
    77 cFileSources MPlaySources;
    78 
    79 static const char *plugin_name=0;
    80 
    81 // --- cMenuSetupMPlayer --------------------------------------------------------
    82 
    83 class cMenuSetupMPlayer : public cMenuSetupPage {
    84 private:
    85   cMPlayerSetup data;
    86   const char *res[3];
    87 protected:
    88   virtual void Store(void);
    89 public:
    90   cMenuSetupMPlayer(void);
    91   };
    92 
    93 cMenuSetupMPlayer::cMenuSetupMPlayer(void)
    94 {
    95   data=MPlayerSetup;
    96   SetSection(tr("MPlayer"));
    97   Add(new cMenuEditBoolItem(tr("Setup.MPlayer$Control mode"),  &data.SlaveMode, tr("Traditional"), tr("Slave")));
    98 #if APIVERSNUM < 10307
    99   Add(new cMenuEditIntItem( tr("Setup.MPlayer$OSD position"),  &data.OsdPos, 0, 6));
   100 #endif
   101   res[0]=tr("disabled");
   102   res[1]=tr("global only");
   103   res[2]=tr("local first");
   104   Add(new cMenuEditStraItem(tr("Setup.MPlayer$Resume mode"),   &data.ResumeMode, 3, res));
   105   Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"),         &data.HideMainMenu));
   106   for(int i=0; i<10; i++) {
   107     char name[32];
   108     snprintf(name,sizeof(name),"%s %d",tr("Setup.MPlayer$Slave command key"),i);
   109     static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789!\"§$%&/()=?{}[]\\+*~#',;.:-_<>|@´`^°" };
   110     Add(new cMenuEditStrItem(name, data.KeyCmd[i],MAX_KEYCMD,allowed));
   111     }
   112 }
   113 
   114 void cMenuSetupMPlayer::Store(void)
   115 {
   116   MPlayerSetup=data;
   117   SetupStore("ControlMode", MPlayerSetup.SlaveMode);
   118   SetupStore("HideMainMenu",MPlayerSetup.HideMainMenu);
   119   SetupStore("ResumeMode",  MPlayerSetup.ResumeMode);
   120 #if APIVERSNUM < 10307
   121   SetupStore("OsdPos",      MPlayerSetup.OsdPos);
   122 #endif
   123   for(int i=0; i<10; i++) {
   124     char name[16];
   125     snprintf(name,sizeof(name),"KeyCmd%d",i);
   126     SetupStore(name,MPlayerSetup.KeyCmd[i]);
   127     }
   128 }
   129 
   130 // --- cMPlayerControl ---------------------------------------------------------
   131 
   132 class cMPlayerControl : public cControl {
   133 private:
   134   static cFileObj *file;
   135   static bool rewind;
   136   cMPlayerPlayer *player;
   137 #if APIVERSNUM >= 10307
   138   cSkinDisplayReplay *display;
   139 #endif
   140   bool visible, modeOnly, haveBeauty;
   141   time_t timeoutShow;
   142   int lastCurrent, lastTotal;
   143   char *lastReplayMsg;
   144   //
   145   bool jumpactive, jumphide, jumpmode;
   146   int jumpval;
   147   //
   148   void Stop(void);
   149   void ShowTimed(int Seconds=0);
   150   void DisplayAtBottom(const char *s);
   151   void ShowProgress(void);
   152   void ShowMode(void);
   153   void ShowTitle(void);
   154   void Jump(void);
   155   void JumpProcess(eKeys Key);
   156   void JumpDisplay(void);
   157 public:
   158   cMPlayerControl(void);
   159   virtual ~cMPlayerControl();
   160   virtual eOSState ProcessKey(eKeys Key);
   161   virtual void Show(void) { ShowTimed(); }
   162   virtual void Hide(void);
   163   static void SetFile(const cFileObj *File, bool Rewind);
   164   };
   165 
   166 cFileObj *cMPlayerControl::file=0;
   167 bool cMPlayerControl::rewind=false;
   168 
   169 cMPlayerControl::cMPlayerControl(void)
   170 :cControl(player=new cMPlayerPlayer(file,rewind))
   171 {
   172   visible=modeOnly=jumpactive=haveBeauty=false;
   173   lastReplayMsg=0;
   174 #if APIVERSNUM >= 10307
   175   display=0;
   176 #else
   177 #ifdef HAVE_BEAUTYPATCH
   178 #if APIVERSNUM >= 10300
   179   const cFont *sym=cFont::GetFont(fontSym);
   180   const cFont *osd=cFont::GetFont(fontOsd);
   181   const cFont::tCharData *symD=sym->CharData(32);
   182   const cFont::tCharData *osdD=osd->CharData(32);
   183 #else //APIVERSNUM >= 10300
   184   cFont *sym=new cFont(fontSym);
   185   cFont *osd=new cFont(fontOsd);
   186   const cFont::tCharData *symD=sym->CharData(32);
   187   const cFont::tCharData *osdD=osd->CharData(32);
   188   delete sym;
   189   delete osd;
   190 #endif //APIVERSNUM >= 10300
   191   if(symD != osdD) haveBeauty=true;
   192   d(printf("mplayer: beauty patch %sdetected\n",haveBeauty?"":"NOT "))
   193 #endif //HAVE_BEAUTYPATCH
   194 #endif //APIVERSNUM >= 10307
   195   ShowTitle();
   196 }
   197 
   198 cMPlayerControl::~cMPlayerControl()
   199 {
   200   Stop();
   201 #if APIVERSNUM >= 10338
   202   cStatus::MsgReplaying(this,0,0,false);
   203 #else
   204   cStatus::MsgReplaying(this, NULL);
   205 #endif
   206   free(lastReplayMsg);
   207 }
   208 
   209 void cMPlayerControl::SetFile(const cFileObj *File, bool Rewind)
   210 {
   211   delete file;
   212   file=File ? new cFileObj(File) : 0;
   213   rewind=Rewind;
   214 }
   215 
   216 void cMPlayerControl::Stop(void)
   217 {
   218   delete player; player=0;
   219 }
   220 
   221 void cMPlayerControl::ShowTimed(int Seconds)
   222 {
   223   if(modeOnly) Hide();
   224   if(!visible) {
   225     ShowProgress();
   226     timeoutShow = Seconds>0 ? time(0)+Seconds : 0;
   227     }
   228 }
   229 
   230 void cMPlayerControl::Hide(void)
   231 {
   232   if(visible) {
   233 #if APIVERSNUM >= 10307
   234     delete display; display=0;
   235 #else
   236     Interface->Close();
   237 #endif
   238     visible=modeOnly=false;
   239 #if APIVERSNUM >= 10500
   240     SetNeedsFastResponse(false);
   241 #else
   242     needsFastResponse=false;
   243 #endif
   244     }
   245 }
   246 
   247 void cMPlayerControl::DisplayAtBottom(const char *s)
   248 {
   249 #if APIVERSNUM < 10307
   250   const int p=modeOnly ? 0 : 2;
   251   if(s) {
   252     const int d=max(Width()-cOsd::WidthInCells(s),0) / 2;
   253     if(modeOnly) Interface->Fill(0, p, Interface->Width(), 1, clrTransparent);
   254     Interface->Write(d, p, s);
   255     }
   256   else
   257     Interface->Fill(12, p, Width() - 22, 1, clrBackground);
   258 #endif
   259 }
   260 
   261 void cMPlayerControl::ShowTitle(void)
   262 {
   263   const char *path=0;
   264   bool release=true;
   265   if(player) path=player->GetCurrentName();
   266   if(!path) {
   267     path=file->FullPath();
   268     release=false;
   269     }
   270   if(path) {
   271     const char *name=rindex(path,'/');
   272     if(name) name++; else name=path;
   273     if(!lastReplayMsg || strcmp(lastReplayMsg,path)) {
   274 #if APIVERSNUM >= 10338
   275       cStatus::MsgReplaying(this,name,path,true);
   276 #else
   277       cStatus::MsgReplaying(this,path);
   278 #endif
   279       free(lastReplayMsg);
   280       lastReplayMsg=strdup(path);
   281       }
   282     if(visible) {
   283 #if APIVERSNUM >= 10307
   284       if(display) display->SetTitle(name);
   285 #else
   286       int n=strlen(name);
   287       if(n>Width()) {
   288         n=n-Width()+4; if(n<0) n=0;
   289         char str[72];
   290         snprintf(str,sizeof(str),"... %s",name+n);
   291         Interface->Write(0,0,str);
   292         }
   293       else Interface->Write(0,0,name);
   294 #endif
   295       }
   296     }
   297   if(release) free((void *)path);
   298 }
   299 
   300 void cMPlayerControl::ShowProgress(void)
   301 {
   302   int Current, Total;
   303 
   304   if(GetIndex(Current,Total) && Total>0) {
   305     bool flush=false;
   306     if(!visible) {
   307 #if APIVERSNUM >= 10307
   308       display=Skins.Current()->DisplayReplay(false);
   309 #else
   310       Interface->Open(Setup.OSDwidth,-MPlayerSetup.OsdPos-3);
   311       Interface->Clear();
   312       if(MPlayerSetup.OsdPos>0) Interface->Fill(0,3,Interface->Width(),MPlayerSetup.OsdPos,clrTransparent);
   313 #endif
   314       visible=true; modeOnly=false;
   315 #if APIVERSNUM >= 10500
   316       SetNeedsFastResponse(true);
   317 #else
   318       needsFastResponse=true;
   319 #endif
   320       lastCurrent=lastTotal=-1;
   321       flush=true;
   322       }
   323 
   324     if(abs(Current-lastCurrent)>12) {
   325 #if APIVERSNUM >= 10307
   326       if(Total>0) display->SetProgress(Current, Total);
   327       display->SetCurrent(IndexToHMSF(Current));
   328       display->SetTotal(IndexToHMSF(Total));
   329       bool Play, Forward;
   330       int Speed;
   331       if(GetReplayMode(Play,Forward,Speed)) 
   332         display->SetMode(Play, Forward, Speed);
   333 #else
   334       cProgressBar ProgressBar(Width() * cOsd::CellWidth(), cOsd::LineHeight(), Current, Total);
   335       Interface->SetBitmap(0, cOsd::LineHeight(), ProgressBar);
   336       Interface->Write(0,2,IndexToHMSF(Current));
   337       Interface->Write(-7,2,IndexToHMSF(Total));
   338 #endif
   339       ShowTitle();
   340       flush=true;
   341       lastCurrent=Current; lastTotal=Total;
   342       }
   343     if(flush) 
   344 #if APIVERSNUM >= 10307
   345       Skins.Flush();
   346 #else
   347       Interface->Flush();
   348 #endif
   349     ShowMode();
   350     }
   351 }
   352 
   353 #if APIVERSNUM < 10307
   354 #ifdef HAVE_BEAUTYPATCH
   355 int forwSym[] = { FSYM_FORW,FSYM_FORW1,FSYM_FORW2,FSYM_FORW3 };
   356 int backSym[] = { FSYM_BACK,FSYM_BACK1,FSYM_BACK2,FSYM_BACK3 };
   357 #endif
   358 #endif
   359 
   360 void cMPlayerControl::ShowMode(void)
   361 {
   362   if(Setup.ShowReplayMode && !jumpactive) {
   363     bool Play, Forward;
   364     int Speed;
   365     if(GetReplayMode(Play, Forward, Speed)) {
   366        bool NormalPlay = (Play && Speed == -1);
   367 
   368        if(!visible) {
   369          if(NormalPlay) return;
   370 #if APIVERSNUM >= 10307
   371          display = Skins.Current()->DisplayReplay(true);
   372 #else
   373          Interface->Open(0,-MPlayerSetup.OsdPos-1);
   374 #endif
   375          visible=modeOnly=true;
   376          }
   377 
   378        if(modeOnly && !timeoutShow && NormalPlay) timeoutShow=time(0)+SELECTHIDE_TIMEOUT;
   379 
   380 #if APIVERSNUM >= 10307
   381        display->SetMode(Play, Forward, Speed);
   382 #else
   383        char buf[16];
   384        eDvbFont OldFont;
   385 #ifdef HAVE_BEAUTYPATCH
   386        if(haveBeauty) {
   387          int i=0;
   388          if(!(Width()&1)) buf[i++]=' ';
   389          buf[i]=FSYM_EMPTY; if(Speed>=0 && !Forward) buf[i]=backSym[Speed];
   390          i++;
   391          buf[i++]=Play?(Speed==-1?FSYM_PLAY:FSYM_EMPTY):FSYM_PAUSE;
   392          buf[i]=FSYM_EMPTY; if(Speed>=0 && Forward) buf[i]=forwSym[Speed];
   393          i++;
   394          if(!(Width()&1)) buf[i++]=' ';
   395          buf[i]=0;
   396          OldFont = Interface->SetFont(fontSym);
   397          }
   398        else {
   399 #endif //HAVE_BEAUTYPATCH
   400          const char *Mode;
   401          if (Speed == -1) Mode = Play    ? "  >  " : " ||  ";
   402          else if (Play)   Mode = Forward ? " X>> " : " <<X ";
   403          else             Mode = Forward ? " X|> " : " <|X ";
   404          strn0cpy(buf, Mode, sizeof(buf));
   405          char *p = strchr(buf, 'X');
   406          if(p) *p = Speed > 0 ? '1' + Speed - 1 : ' ';
   407          OldFont = Interface->SetFont(fontFix);
   408 #ifdef HAVE_BEAUTYPATCH
   409          }
   410 #endif //HAVE_BEAUTYPATCH
   411        DisplayAtBottom(buf);
   412        Interface->SetFont(OldFont);
   413 #endif //APIVERSNUM >= 10307
   414        }
   415     }
   416 }
   417 
   418 void cMPlayerControl::JumpDisplay(void)
   419 {
   420   char buf[64];
   421   const char *j=trVDR("Jump: "), u=jumpmode?'%':'m';
   422   if(!jumpval) sprintf(buf,"%s- %c",  j,u);
   423   else         sprintf(buf,"%s%d- %c",j,jumpval,u);
   424 #if APIVERSNUM >= 10307
   425   display->SetJump(buf);
   426 #else
   427   DisplayAtBottom(buf);
   428 #endif
   429 }
   430 
   431 void cMPlayerControl::JumpProcess(eKeys Key)
   432 {
   433   const int n=Key-k0;
   434   switch (Key) {
   435     case k0 ... k9:
   436       {
   437       const int max=jumpmode?100:lastTotal;
   438       if(jumpval*10+n <= max) jumpval=jumpval*10+n;
   439       JumpDisplay();
   440       }
   441       break;
   442     case kBlue:
   443       jumpmode=!jumpmode; jumpval=0;
   444       DisplayAtBottom(0); JumpDisplay();
   445       break;
   446     case kPlay:
   447     case kUp:
   448       player->Goto(jumpval*(jumpmode?1:60),jumpmode,false);
   449       jumpactive=false;
   450       break;
   451     case kFastRew:
   452     case kFastFwd:
   453     case kLeft:
   454     case kRight:
   455       if(!jumpmode) {
   456         player->SkipSeconds(jumpval*60 * ((Key==kLeft || Key==kFastRew) ? -1:1));
   457         jumpactive=false;
   458         }
   459       break;
   460     default:
   461       jumpactive=false;
   462       break;
   463     }
   464 
   465   if(!jumpactive) {
   466     if(jumphide) Hide();
   467     else 
   468 #if APIVERSNUM >= 10307
   469       display->SetJump(0);
   470 #else
   471       DisplayAtBottom(0);
   472 #endif
   473     }
   474 }
   475 
   476 void cMPlayerControl::Jump(void)
   477 {
   478   jumpval=0; jumphide=jumpmode=false;
   479   if(!visible) {
   480     ShowTimed(); if(!visible) return;
   481     jumphide=true;
   482     }
   483   JumpDisplay();
   484   jumpactive=true;
   485 }
   486 
   487 eOSState cMPlayerControl::ProcessKey(eKeys Key)
   488 {
   489   if(!player->Active()) { Hide(); Stop(); return osEnd; }
   490 
   491   if(!player->SlaveMode()) {
   492     if(Key==kBlue) { Hide(); Stop(); return osEnd; }
   493     }
   494   else {
   495     if(visible) {
   496       if(timeoutShow && time(0)>timeoutShow) {
   497         Hide(); ShowMode();
   498         timeoutShow = 0;
   499         }
   500       else {
   501         if(modeOnly) ShowMode();
   502         else ShowProgress();
   503         }
   504       }
   505     else ShowTitle();
   506 
   507     if(jumpactive && Key != kNone) {
   508       JumpProcess(Key);
   509       return osContinue;
   510       }
   511 
   512     bool DoShowMode = true;
   513     switch (Key) {
   514       case kPlay:
   515       case kUp:      player->Play(); break;
   516 
   517       case kPause:
   518       case kDown:    player->Pause(); break;
   519 
   520       case kFastRew|k_Repeat:
   521       case kFastRew:
   522       case kLeft|k_Repeat:
   523       case kLeft:    player->SkipSeconds(-10); break;
   524 
   525       case kFastFwd|k_Repeat:
   526       case kFastFwd:
   527       case kRight|k_Repeat:
   528       case kRight:   player->SkipSeconds(10); break;
   529 
   530       case kRed:     Jump(); break;
   531 
   532       case kGreen|k_Repeat:                      // temporary use
   533       case kGreen:   player->SkipSeconds(-60); break;
   534       case kYellow|k_Repeat:
   535       case kYellow:  player->SkipSeconds(60); break;
   536   //    case kGreen|k_Repeat:                      // reserved for future use
   537   //    case kGreen:   player->SkipPrev(); break;
   538   //    case kYellow|k_Repeat:
   539   //    case kYellow:  player->SkipNext(); break;
   540 
   541       case kBack:
   542 #if APIVERSNUM >= 10332
   543                      Hide();
   544                      cRemote::CallPlugin(plugin_name);
   545                      return osBack;
   546 #endif
   547       case kStop:
   548       case kBlue:    Hide(); Stop(); return osEnd;
   549 
   550       default:
   551         DoShowMode = false;
   552         switch(Key) {
   553           case kOk: if(visible && !modeOnly) { Hide(); DoShowMode=true; }
   554                     else ShowTimed();
   555                     break;
   556           case k0:
   557           case k1:
   558           case k2:
   559           case k3:
   560           case k4:
   561           case k5:
   562           case k6:
   563           case k7:
   564           case k8:
   565           case k9:  {
   566                     const char *cmd=MPlayerSetup.KeyCmd[Key-k0];
   567                     if(cmd[0]) player->KeyCmd(cmd);
   568                     }
   569                     break;
   570           default:  break;
   571           }
   572         break;
   573       }
   574 
   575     if(DoShowMode) ShowMode();
   576     }
   577   return osContinue;
   578 }
   579 
   580 // --- cMenuMPlayAid -----------------------------------------------------------
   581 
   582 class cMenuMPlayAid : public cOsdMenu {
   583 public:
   584   cMenuMPlayAid(void);
   585   virtual eOSState ProcessKey(eKeys Key);
   586   };
   587 
   588 cMenuMPlayAid::cMenuMPlayAid(void)
   589 :cOsdMenu(tr("MPlayer Audio ID"),20)
   590 {
   591   Add(new cMenuEditIntItem(tr("Audiostream ID"),&MPlayerAid,-1,255));
   592   Display();
   593 }
   594 
   595 eOSState cMenuMPlayAid::ProcessKey(eKeys Key)
   596 {
   597   eOSState state=cOsdMenu::ProcessKey(Key);
   598   if(state==osUnknown) {
   599     switch(Key) {
   600       case kOk: state=osBack; break;
   601       default:  break;
   602       }
   603     }
   604   return state;
   605 }
   606 
   607 // --- cMenuMPlayBrowse ---------------------------------------------------------
   608 
   609 class cMenuMPlayBrowse : public cMenuBrowse {
   610 private:
   611   bool sourcing, aidedit;
   612   eOSState Source(bool second);
   613   eOSState Summary(void);
   614 protected:
   615   virtual void SetButtons(void);
   616 public:
   617   cMenuMPlayBrowse(void);
   618   virtual eOSState ProcessKey(eKeys Key);
   619   };
   620 
   621 static const char *excl_sum[] = { ".*","*.summary","*.txt","*.nfo",0 };
   622 
   623 cMenuMPlayBrowse::cMenuMPlayBrowse(void)
   624 :cMenuBrowse(MPlaySources.GetSource(),false,false,tr("MPlayer browser"),excl_sum)
   625 {
   626   sourcing=aidedit=false;
   627   SetButtons();
   628 }
   629 
   630 void cMenuMPlayBrowse::SetButtons(void)
   631 {
   632   static char blue[12];
   633   snprintf(blue,sizeof(blue),MPlayerAid>=0 ? "AID:%d" : "AID:def",MPlayerAid);
   634   SetHelp(trVDR(BUTTON"Play"), MPlayerSetup.ResumeMode ? trVDR(BUTTON"Rewind"):0, tr("Source"), blue);
   635   Display();
   636 }
   637 
   638 eOSState cMenuMPlayBrowse::Source(bool second)
   639 {
   640   if(HasSubMenu()) return osContinue;
   641 
   642   if(!second) {
   643     sourcing=true;
   644     return AddSubMenu(new cMenuSource(&MPlaySources,tr("MPlayer source")));
   645     }
   646   sourcing=false;
   647   cFileSource *src=cMenuSource::GetSelected();
   648   if(src) {
   649     MPlaySources.SetSource(src);
   650     SetSource(src);
   651     NewDir(0);
   652     }
   653   return osContinue;
   654 }
   655 
   656 eOSState cMenuMPlayBrowse::Summary(void)
   657 {
   658   cFileObj *item=CurrentItem();
   659   if(item && item->Type()==otFile) {
   660     static const char *exts[] = { ".summary",".txt",".nfo",0 };
   661     for(int i=0; exts[i]; i++) {
   662       char buff[4096];
   663       strn0cpy(buff,item->FullPath(),sizeof(buff)-20);
   664       char *e=&buff[strlen(buff)];
   665       strn0cpy(e,exts[i],20);
   666       int fd=open(buff,O_RDONLY);
   667       *e=0;
   668       if(fd<0 && (e=rindex(buff,'.'))) {
   669         strn0cpy(e,exts[i],20);
   670         fd=open(buff,O_RDONLY);
   671         }
   672       if(fd>=0) {
   673         int r=read(fd,buff,sizeof(buff)-1);
   674         close(fd);
   675         if(r>0) {
   676           buff[r]=0;
   677           return AddSubMenu(new cMenuText(tr("Summary"),buff));
   678           }
   679         }
   680       }
   681     }
   682   return osContinue;
   683 }
   684 
   685 eOSState cMenuMPlayBrowse::ProcessKey(eKeys Key)
   686 {
   687   eOSState state=cOsdMenu::ProcessKey(Key);
   688   if(state==osContinue && !HasSubMenu()) {
   689     if(sourcing) return Source(true);
   690     if(aidedit) { aidedit=false; SetButtons(); }
   691     }
   692   bool rew=false;
   693   if(state==osUnknown) {
   694     switch(Key) {
   695       case kGreen:
   696         {
   697         cFileObj *item=CurrentItem();
   698         if(item && item->Type()==otFile) {
   699           lastselect=new cFileObj(item);
   700           state=osBack;
   701           rew=true;
   702           } 
   703         else state=osContinue;
   704         break;
   705         }
   706       case kYellow:
   707         state=Source(false);
   708         break;
   709       case kBlue:
   710         aidedit=true;
   711         state=AddSubMenu(new cMenuMPlayAid);
   712         break;
   713       case k0:
   714         state=Summary();
   715         break;
   716       default:
   717         break;
   718       }
   719     }
   720   if(state==osUnknown) state=cMenuBrowse::ProcessStdKey(Key,state);
   721   if(state==osBack && lastselect) {
   722     cMPlayerControl::SetFile(lastselect,rew);
   723     cControl::Launch(new cMPlayerControl);
   724     return osEnd;
   725     }
   726   return state;
   727 }
   728 
   729 // --- cPluginMPlayer ----------------------------------------------------------
   730 
   731 static const char *VERSION        = PLUGIN_VERSION;
   732 static const char *DESCRIPTION    = trNOOP("Media replay via MPlayer");
   733 static const char *MAINMENUENTRY  = "MPlayer";
   734 
   735 class cPluginMPlayer : public cPlugin {
   736 private:
   737 #if APIVERSNUM >= 10330
   738   bool ExternalPlay(const char *path, bool test);
   739 #endif
   740 public:
   741   cPluginMPlayer(void);
   742   virtual ~cPluginMPlayer();
   743   virtual const char *Version(void) { return VERSION; }
   744   virtual const char *Description(void) { return tr(DESCRIPTION); }
   745   virtual const char *CommandLineHelp(void);
   746   virtual bool ProcessArgs(int argc, char *argv[]);
   747 #if APIVERSNUM >= 10131
   748   virtual bool Initialize(void);
   749 #else
   750   virtual bool Start(void);
   751 #endif
   752   virtual const char *MainMenuEntry(void);
   753   virtual cOsdMenu *MainMenuAction(void);
   754   virtual cMenuSetupPage *SetupMenu(void);
   755   virtual bool SetupParse(const char *Name, const char *Value);
   756 #if APIVERSNUM >= 10330
   757   virtual bool Service(const char *Id, void *Data);
   758 #if APIVERSNUM >= 10331
   759   virtual const char **SVDRPHelpPages(void);
   760   virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
   761 #endif
   762 #endif
   763   };
   764 
   765 cPluginMPlayer::cPluginMPlayer(void)
   766 {
   767   // Initialize any member variables here.
   768   // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
   769   // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
   770   status=0;
   771 }
   772 
   773 cPluginMPlayer::~cPluginMPlayer()
   774 {
   775   delete status;
   776 }
   777 
   778 const char *cPluginMPlayer::CommandLineHelp(void)
   779 {
   780   static char *help_str=0;
   781   
   782   free(help_str);    //                                     for easier orientation, this is column 80|
   783   asprintf(&help_str,"  -m CMD,   --mount=CMD    use CMD to mount/unmount/eject mp3 sources\n"
   784                      "                           (default: %s)\n"
   785                      "  -M CMD,   --mplayer=CMD  use CMD when calling MPlayer\n"
   786                      "                           (default: %s)\n"
   787                      "  -S SUB,   --sources=SUB  search sources config in SUB subdirectory\n"
   788                      "                           (default: %s)\n"
   789                      "  -R DIR,   --resume=DIR   store global resume file in DIR\n"
   790                      "                           (default: %s)\n",
   791                      mountscript,
   792                      MPlayerCmd,
   793                      sourcesSub ? sourcesSub:"none",
   794                      globalResumeDir ? globalResumeDir:"video dir"
   795                      );
   796   return help_str;
   797 }
   798 
   799 bool cPluginMPlayer::ProcessArgs(int argc, char *argv[])
   800 {
   801   static struct option long_options[] = {
   802       { "mount",    required_argument, NULL, 'm' },
   803       { "mplayer",  required_argument, NULL, 'M' },
   804       { "sources",  required_argument, NULL, 'S' },
   805       { "resume",   required_argument, NULL, 'R' },
   806       { NULL }
   807     };
   808 
   809   int c, option_index = 0;
   810   while((c=getopt_long(argc,argv,"m:M:S:R:",long_options,&option_index))!=-1) {
   811     switch (c) {
   812       case 'm': mountscript=optarg; break;
   813       case 'M': MPlayerCmd=optarg; break;
   814       case 'S': sourcesSub=optarg; break;
   815       case 'R': globalResumeDir=optarg; break;
   816       default:  return false;
   817       }
   818     }
   819   return true;
   820 }
   821 
   822 #if APIVERSNUM >= 10131
   823 bool cPluginMPlayer::Initialize(void)
   824 #else
   825 bool cPluginMPlayer::Start(void)
   826 #endif
   827 {
   828   if(!CheckVDRVersion(1,1,16,"mplayer")) return false;
   829   plugin_name="mplayer";
   830 #if APIVERSNUM < 10507
   831   i18n_name="mplayer";
   832 #else
   833   i18n_name="vdr-mplayer";
   834 #endif
   835   MPlaySources.Load(AddDirectory(ConfigDirectory(sourcesSub),"mplayersources.conf"));
   836   if(MPlaySources.Count()<1) {
   837     esyslog("ERROR: you must have defined at least one source in mplayersources.conf");
   838     fprintf(stderr,"No source(s) defined in mplayersources.conf\n");
   839     return false;
   840     }
   841 #if APIVERSNUM < 10507
   842   RegisterI18n(Phrases);
   843 #endif
   844   if(!(status=new cMPlayerStatus)) return false;
   845   return true;
   846 }
   847 
   848 const char *cPluginMPlayer::MainMenuEntry(void)
   849 {
   850   return MPlayerSetup.HideMainMenu ? 0 : tr(MAINMENUENTRY);
   851 }
   852 
   853 cOsdMenu *cPluginMPlayer::MainMenuAction(void)
   854 {
   855   return new cMenuMPlayBrowse;
   856 }
   857 
   858 cMenuSetupPage *cPluginMPlayer::SetupMenu(void)
   859 {
   860   return new cMenuSetupMPlayer;
   861 }
   862 
   863 bool cPluginMPlayer::SetupParse(const char *Name, const char *Value)
   864 {
   865   if(      !strcasecmp(Name, "ControlMode"))  MPlayerSetup.SlaveMode    = atoi(Value);
   866   else if (!strcasecmp(Name, "HideMainMenu")) MPlayerSetup.HideMainMenu = atoi(Value);
   867   else if (!strcasecmp(Name, "ResumeMode"))   MPlayerSetup.ResumeMode   = atoi(Value);
   868   else if (!strcasecmp(Name, "OsdPos"))       MPlayerSetup.OsdPos       = atoi(Value);
   869   else if (!strncasecmp(Name,"KeyCmd", 6) && strlen(Name)==7 && isdigit(Name[6]))
   870     strn0cpy(MPlayerSetup.KeyCmd[Name[6]-'0'],Value,sizeof(MPlayerSetup.KeyCmd[0]));
   871   else return false;
   872   return true;
   873 }
   874 
   875 #if APIVERSNUM >= 10330
   876 
   877 bool cPluginMPlayer::ExternalPlay(const char *path, bool test)
   878 {
   879   char real[PATH_MAX+1];
   880   if(realpath(path,real)) {
   881     cFileSource *src=MPlaySources.FindSource(real);
   882     if(src) {
   883       cFileObj *item=new cFileObj(src,0,0,otFile);
   884       if(item) {
   885         item->SplitAndSet(real);
   886         if(item->GuessType()) {
   887           if(item->Exists()) {
   888             if(!test) {
   889               cMPlayerControl::SetFile(item,true);
   890               cControl::Launch(new cMPlayerControl);
   891               cControl::Attach();
   892               }
   893             delete item;
   894             return true;
   895             }
   896           else dsyslog("MPlayer service: cannot play '%s'",path);
   897           }
   898         else dsyslog("MPlayer service: GuessType() failed for '%s'",path);
   899         delete item;
   900         }
   901       }
   902     else dsyslog("MPlayer service: cannot find source for '%s', real '%s'",path,real);
   903     }
   904   else if(errno!=ENOENT && errno!=ENOTDIR)
   905     esyslog("ERROR: realpath: %s: %s",path,strerror(errno));
   906   return false;
   907 }
   908 
   909 bool cPluginMPlayer::Service(const char *Id, void *Data)
   910 {
   911   if(!strcasecmp(Id,"MPlayer-Play-v1")) {
   912     if(Data) {
   913       struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
   914       msd->result=ExternalPlay(msd->data.filename,false);
   915       }
   916     return true;
   917     }
   918   else if(!strcasecmp(Id,"MPlayer-Test-v1")) {
   919     if(Data) {
   920       struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
   921       msd->result=ExternalPlay(msd->data.filename,true);
   922       }
   923     return true;
   924     }
   925   return false;
   926 }
   927 
   928 #if APIVERSNUM >= 10331
   929 
   930 const char **cPluginMPlayer::SVDRPHelpPages(void)
   931 {
   932   static const char *HelpPages[] = {
   933     "PLAY <filename>\n"
   934     "    Triggers playback of file 'filename'.",
   935     "TEST <filename>\n"
   936     "    Tests is playback of file 'filename' is possible.",
   937     NULL
   938     };
   939   return HelpPages;
   940 }
   941 
   942 cString cPluginMPlayer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
   943 {
   944   if(!strcasecmp(Command,"PLAY")) {
   945     if(*Option) {
   946       if(ExternalPlay(Option,false)) return "Playback triggered";
   947       else { ReplyCode=550; return "Playback failed"; }
   948       }
   949     else { ReplyCode=501; return "Missing filename"; }
   950     }
   951   else if(!strcasecmp(Command,"TEST")) {
   952     if(*Option) {
   953       if(ExternalPlay(Option,true)) return "Playback possible";
   954       else { ReplyCode=550; return "Playback not possible"; }
   955       }
   956     else { ReplyCode=501; return "Missing filename"; }
   957     }
   958   return NULL;
   959 }
   960 
   961 #endif // 1.3.31
   962 #endif // 1.3.30
   963 
   964 VDRPLUGINCREATOR(cPluginMPlayer); // Don't touch this!