player-mplayer.c
branchtrunk
changeset 0 474a1293c3c0
child 22 93aaf15c145a
equal deleted inserted replaced
-1:000000000000 0:474a1293c3c0
       
     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 <ctype.h>
       
    23 #include <stdlib.h>
       
    24 #include <stdio.h>
       
    25 #include <stdarg.h>
       
    26 #include <string.h>
       
    27 #include <fcntl.h>
       
    28 #include <errno.h>
       
    29 #include <sys/ioctl.h>
       
    30 #include <sys/types.h>
       
    31 #include <sys/poll.h>
       
    32 #include <unistd.h>
       
    33 #include <signal.h>
       
    34 #include <wait.h>
       
    35 #include <math.h>
       
    36 #include <locale.h>
       
    37 
       
    38 #include <vdr/device.h>
       
    39 #include <vdr/videodir.h>
       
    40 
       
    41 #include "common.h"
       
    42 #include "data.h"
       
    43 #include "player-mplayer.h"
       
    44 #include "setup-mplayer.h"
       
    45 
       
    46 //#define DEBUG_SLAVE
       
    47 
       
    48 #define MPLAYER_VOL_STEP 3.0
       
    49 
       
    50 const char *MPlayerCmd = "mplayer.sh";
       
    51 int MPlayerAid=-1;
       
    52 const char *globalResumeDir = 0;
       
    53 
       
    54 // -- cMPlayerStatus -----------------------------------------------------------
       
    55 
       
    56 cMPlayerStatus *status;
       
    57 
       
    58 cMPlayerStatus::cMPlayerStatus(void)
       
    59 {
       
    60   mute=changed=false;
       
    61   volume=0;
       
    62 }
       
    63 
       
    64 bool cMPlayerStatus::GetVolume(int &Volume, bool &Mute)
       
    65 {
       
    66   Lock();
       
    67   bool r=changed;
       
    68   // convert according cDvbDevice::SetVolumeDevice();
       
    69   // take into account that VDR does non-linear changes, while
       
    70   // MPlayer does linear volume changes.
       
    71   Volume=((2*volume-volume*volume/255)*100+128)/256;
       
    72   Mute=mute;
       
    73   changed=false;
       
    74   Unlock();
       
    75   return r;
       
    76 }
       
    77 
       
    78 void cMPlayerStatus::SetVolume(int Volume, bool Absolute)
       
    79 {
       
    80   Lock();
       
    81   if(Absolute && Volume==0) mute=true;
       
    82   else {
       
    83 #if APIVERSNUM>=10401
       
    84 #if APIVERSNUM==10401
       
    85 #warning Caution! This code does not work with VDR 1.4.1 and 1.4.1-1. You can ignore this warning if you are using VDR 1.4.1-2 or later.
       
    86 #endif
       
    87     if(!Absolute)
       
    88       volume+=Volume;
       
    89     else
       
    90 #endif
       
    91       volume=Volume;
       
    92     if(volume>0) mute=false;
       
    93     }
       
    94   d(printf("status: volume=%d mute=%d\n",volume,mute))
       
    95   changed=true;
       
    96   Unlock();
       
    97 }
       
    98 
       
    99 // --- cResumeEntry ------------------------------------------------------------
       
   100 
       
   101 class cResumeEntry : public cListObject {
       
   102 public:
       
   103   char *name;
       
   104   float pos;
       
   105   //
       
   106   cResumeEntry(void);
       
   107   ~cResumeEntry();
       
   108   };
       
   109 
       
   110 cResumeEntry::cResumeEntry(void)
       
   111 {
       
   112   name=0;
       
   113 }
       
   114 
       
   115 cResumeEntry::~cResumeEntry()
       
   116 {
       
   117   free(name);
       
   118 }
       
   119 
       
   120 // --- cMPlayerResume ----------------------------------------------------------
       
   121 
       
   122 #define RESUME_FILE ".mplayer.resume"
       
   123 #define GLOBAL_RESUME_FILE "global.mplayer.resume"
       
   124 
       
   125 class cMPlayerResume : public cList<cResumeEntry> {
       
   126 private:
       
   127   char *resfile;
       
   128   bool modified, global;
       
   129   cFileObj *resobj;
       
   130   //
       
   131   bool OpenResume(const cFileObj *file);
       
   132   bool SaveResume(void);
       
   133   void Purge(void);
       
   134   cResumeEntry *FindResume(const cFileObj *file);
       
   135 public:
       
   136   cMPlayerResume(void);
       
   137   ~cMPlayerResume();
       
   138   void SetResume(const cFileObj *file, float pos);
       
   139   bool GetResume(const cFileObj *file, float &pos);
       
   140   };
       
   141 
       
   142 cMPlayerResume::cMPlayerResume(void)
       
   143 {
       
   144   resfile=0; resobj=0;
       
   145 }
       
   146 
       
   147 cMPlayerResume::~cMPlayerResume()
       
   148 {
       
   149   SaveResume();
       
   150   free(resfile);
       
   151   delete resobj;
       
   152 }
       
   153 
       
   154 void cMPlayerResume::SetResume(const cFileObj *file, float pos)
       
   155 {
       
   156   if(pos<0.001) pos=0.0;
       
   157   else if(pos>99.0) pos=99.0;
       
   158   cResumeEntry *re;
       
   159   if(OpenResume(file) && (re=FindResume(file))) {
       
   160     d(printf("resume: setting resume %f (update)\n",pos))
       
   161     }
       
   162   else {
       
   163     re=new cResumeEntry;
       
   164     re->name=strdup(global ? file->FullPath() : file->Name());
       
   165     Add(re);
       
   166     d(printf("resume: setting resume %f (new)\n",pos))
       
   167     }
       
   168   re->pos=pos;
       
   169   modified=true;
       
   170 }
       
   171 
       
   172 bool cMPlayerResume::GetResume(const cFileObj *file, float &pos)
       
   173 {
       
   174   cResumeEntry *re;
       
   175   if(OpenResume(file) && (re=FindResume(file))) {
       
   176     pos=re->pos;
       
   177     return true;
       
   178     }
       
   179   return false;
       
   180 }
       
   181 
       
   182 bool cMPlayerResume::OpenResume(const cFileObj *file)
       
   183 {
       
   184   if(!resfile) {
       
   185     Clear();
       
   186     modified=global=false;
       
   187     free(resfile); resfile=0;
       
   188     delete resobj; resobj=new cFileObj(file);
       
   189     char *s;
       
   190     asprintf(&s,file->Subdir() ? "%s/%s":"%s",file->Source()->BaseDir(),file->Subdir());
       
   191     if(MPlayerSetup.ResumeMode==1 || 
       
   192        (access(s,W_OK) && (errno==EACCES || errno==EROFS))) {
       
   193       global=true;
       
   194       resfile=AddPath(globalResumeDir?globalResumeDir:VideoDirectory,GLOBAL_RESUME_FILE);
       
   195       d(printf("resume: using global file\n"))
       
   196       }
       
   197     else {
       
   198       resfile=AddPath(s,RESUME_FILE);
       
   199       }
       
   200     free(s);
       
   201     d(printf("resume: resume file is '%s'\n",resfile))
       
   202     FILE *f=fopen(resfile,"r");
       
   203     if(f) {
       
   204       d(printf("resume: successfully opened resume file\n"))
       
   205       char line[768];
       
   206       while(fgets(line,sizeof(line),f)) {
       
   207         char name[512];
       
   208         float p;
       
   209         if(sscanf(line,"%f:%511[^\n]",&p,name)==2) {
       
   210           cResumeEntry *re=new cResumeEntry;
       
   211           re->name=strdup(name);
       
   212           re->pos=p;
       
   213           Add(re);
       
   214           }
       
   215         }
       
   216       fclose(f);
       
   217       return true;
       
   218       }
       
   219     else {
       
   220       d(printf("resume: assuming empty resume file\n"))
       
   221       return false;
       
   222       }
       
   223     }
       
   224   return true;
       
   225 }
       
   226 
       
   227 bool cMPlayerResume::SaveResume(void)
       
   228 {
       
   229   if(resfile && modified) {
       
   230     Purge();
       
   231     d(printf("resume: saving resume file\n"))
       
   232     cSafeFile f(resfile);
       
   233     if(f.Open()) {
       
   234       for(cResumeEntry *re=First(); re; re=Next(re))
       
   235         fprintf(f,"%06.2f:%s\n",re->pos,re->name);
       
   236       f.Close();
       
   237       return true;
       
   238       }
       
   239     else
       
   240       d(printf("resume: failed to save resume file\n"))
       
   241     }
       
   242   return false;
       
   243 }
       
   244 
       
   245 void cMPlayerResume::Purge(void)
       
   246 {
       
   247   d(printf("resume: purging from resume file\n"))
       
   248   for(cResumeEntry *re=First(); re;) {
       
   249     bool del=false;
       
   250     if(re->pos<1.0 || re->pos>99.0) {
       
   251       del=true;
       
   252       d(printf("resume: purging due to position: %s\n",re->name))
       
   253       }
       
   254     else if(!global) {
       
   255       resobj->SetName(re->name);
       
   256       if(access(resobj->FullPath(),F_OK)<0) {
       
   257         del=true;
       
   258         d(printf("resume: purging due to access: %s\n",re->name))
       
   259         }
       
   260       }
       
   261     if(del) {
       
   262       cResumeEntry *n=Next(re);
       
   263       Del(re);
       
   264       modified=true;
       
   265       re=n;
       
   266       }
       
   267     else
       
   268       re=Next(re);
       
   269     }
       
   270 }
       
   271 
       
   272 cResumeEntry *cMPlayerResume::FindResume(const cFileObj *file)
       
   273 {
       
   274  if(resfile) {
       
   275    d(printf("resume: searching resume  position for '%s'\n",file->Name()))
       
   276    const char *s=global ? file->FullPath() : file->Name();
       
   277    for(cResumeEntry *re=First(); re; re=Next(re))
       
   278      if(!strcasecmp(re->name,s)) {
       
   279        d(printf("resume: found resume position %.1f%%\n",re->pos))
       
   280        return re;
       
   281        }
       
   282    }
       
   283  d(printf("resume: no resume position found\n"))
       
   284  return 0;
       
   285 }
       
   286 
       
   287 // --- cMPlayerPlayer ----------------------------------------------------------
       
   288 
       
   289 cMPlayerPlayer::cMPlayerPlayer(const cFileObj *File, bool Rewind)
       
   290 :cPlayer(pmExtern_THIS_SHOULD_BE_AVOIDED)
       
   291 {
       
   292   started=slave=brokenPipe=false; run=true; pid=-1; pipefl=0;
       
   293   playMode=pmPlay; index=saveIndex=total=-1; nextTime=nextPos=0;
       
   294   currentName=0;
       
   295   file=new cFileObj(File);
       
   296   rewind=Rewind;
       
   297   resume=MPlayerSetup.ResumeMode ? new cMPlayerResume : 0;
       
   298 }
       
   299 
       
   300 cMPlayerPlayer::~cMPlayerPlayer()
       
   301 {
       
   302   Detach();
       
   303   ClosePipe();
       
   304   delete file;
       
   305   delete resume;
       
   306   free(currentName);
       
   307 }
       
   308 
       
   309 void cMPlayerPlayer::ClosePipe(void)
       
   310 {
       
   311   if(pipefl&1) close(inpipe[0]);
       
   312   if(pipefl&2) close(inpipe[1]);
       
   313   if(pipefl&4) close(outpipe[0]);
       
   314   if(pipefl&8) close(outpipe[1]);
       
   315   pipefl=0;
       
   316 }
       
   317 
       
   318 void cMPlayerPlayer::Activate(bool On)
       
   319 {
       
   320   if(On) {
       
   321     if(file && !started) {
       
   322       if(Fork()) started=true;
       
   323       }
       
   324     }
       
   325   else if(started) {
       
   326     run=false;
       
   327     if(Active()) {
       
   328       if(slave) {
       
   329         Play(); // MPlayer ignores "quit" while paused
       
   330         MPlayerControl("quit");
       
   331         int until=time_ms()+3000; // wait some time until MPlayer is gone
       
   332         d(printf("mplayer: waiting for child exit"))
       
   333         while(Active()) {
       
   334           if(time_ms()>until) {
       
   335             kill(pid,SIGKILL); // kill it anyways
       
   336             d(printf(" SIGKILL"))
       
   337             break;
       
   338             }
       
   339           SLEEP(250);
       
   340           d(printf(".")) d(fflush(stdout))
       
   341           }
       
   342         d(printf("\n"))
       
   343         }
       
   344       else {
       
   345         kill(pid,SIGTERM);
       
   346         d(printf("mplayer: waiting for child exit (non-slave)\n"))
       
   347         }
       
   348       waitpid(pid,0,0);
       
   349       }
       
   350     ClosePipe();
       
   351     Cancel(2);
       
   352     started=slave=false;
       
   353     }
       
   354 }
       
   355 
       
   356 bool cMPlayerPlayer::Active(void)
       
   357 {
       
   358   return waitpid(pid,0,WNOHANG)==0;
       
   359 }
       
   360 
       
   361 bool cMPlayerPlayer::Fork(void)
       
   362 {
       
   363   if(MPlayerSetup.SlaveMode) {
       
   364     if(pipe(inpipe)==-1) {
       
   365       esyslog("ERROR: pipe failed for inpipe: (%d) %s",errno,strerror(errno));
       
   366       return false;
       
   367       }
       
   368     pipefl|=1+2;
       
   369     if(pipe(outpipe)==-1) {
       
   370       esyslog("ERROR: pipe failed for outpipe: (%d) %s",errno,strerror(errno));
       
   371       return false;
       
   372       }
       
   373     pipefl|=4+8;
       
   374     brokenPipe=false;
       
   375     }
       
   376 
       
   377   pid=fork();
       
   378   if(pid==-1) {
       
   379     esyslog("ERROR: fork failed: (%d) %s",errno,strerror(errno));
       
   380     return false;
       
   381     }
       
   382   if(pid==0) { // child
       
   383     dsyslog("mplayer: mplayer child started (pid=%d)", getpid());
       
   384 
       
   385     if(MPlayerSetup.SlaveMode) {
       
   386       if(dup2(inpipe[0],STDIN_FILENO)<0 ||
       
   387          dup2(outpipe[1],STDOUT_FILENO)<0 ||
       
   388          dup2(outpipe[1],STDERR_FILENO)<0) {
       
   389         esyslog("ERROR: dup2() failed in MPlayer child: (%d) %s",errno,strerror(errno));
       
   390         exit(127);
       
   391         }
       
   392       }
       
   393     else {
       
   394       int nfd=open("/dev/null",O_RDONLY);
       
   395       if(nfd<0 || dup2(nfd,STDIN_FILENO)<0)
       
   396         esyslog("ERROR: redirect of STDIN failed in MPlayer child: (%d) %s",errno,strerror(errno));
       
   397       }
       
   398     for(int i=getdtablesize()-1; i>STDERR_FILENO; i--) close(i);
       
   399 
       
   400     char cmd[64+PATH_MAX*2], aid[20];
       
   401     char *fname=Quote(file->FullPath());
       
   402     if(MPlayerAid>=0) snprintf(aid,sizeof(aid)," AID %d",MPlayerAid);
       
   403     else aid[0]=0;
       
   404     snprintf(cmd,sizeof(cmd),"%s \"%s\" %s%s",MPlayerCmd,fname,MPlayerSetup.SlaveMode?"SLAVE":"",aid);
       
   405     free(fname);
       
   406     execle("/bin/sh","sh","-c",cmd,(char *)0,environ);
       
   407     esyslog("ERROR: exec failed for %s: (%d) %s",cmd,errno,strerror(errno));
       
   408     exit(127);
       
   409     }
       
   410 
       
   411   if(MPlayerSetup.SlaveMode) {
       
   412     close(inpipe[0]); pipefl&=~1;
       
   413     close(outpipe[1]); pipefl&=~8;
       
   414     fcntl(outpipe[0],F_SETFL,O_NONBLOCK);
       
   415     run=slave=true;
       
   416     mpVolume=100; // MPlayer startup defaults
       
   417     mpMute=false;
       
   418     Start();
       
   419     }
       
   420   return true;
       
   421 }
       
   422 
       
   423 #define BSIZE    1024
       
   424 #define TIME_INT 20
       
   425 #define POS_INT  1
       
   426 
       
   427 void cMPlayerPlayer::Action(void)
       
   428 {
       
   429   dsyslog("mplayer: player thread started (pid=%d)", getpid());
       
   430 
       
   431   // set locale for correct parsing of MPlayer output.
       
   432   // I don't know if this affects other parts of VDR.
       
   433   const char * const oldLocale=setlocale(LC_NUMERIC,"C");
       
   434 
       
   435   pollfd pfd[1];
       
   436   pfd[0].fd=outpipe[0];
       
   437   pfd[0].events=POLLIN;
       
   438 
       
   439   float curPos=-1.0, resPos=-1.0;
       
   440   if(resume && !rewind) resume->GetResume(file,resPos);
       
   441 
       
   442   char buff[BSIZE+2]; // additional space for fake newline
       
   443   int c=0;
       
   444   bool force=true, slavePatch=false, trustedTotal=false, playBack=false;
       
   445   while(run) {
       
   446     if(playMode==pmPlay && playBack) {
       
   447       int t=time(0);
       
   448       if(t>=nextTime) {
       
   449         MPlayerControl("get_time_length");
       
   450         nextTime=t+(total>0 ? TIME_INT : POS_INT);
       
   451         }
       
   452       if(t>=nextPos) {
       
   453         if(!slavePatch) MPlayerControl("get_percent_pos");
       
   454         nextPos=t+POS_INT;
       
   455         }
       
   456       }
       
   457 
       
   458     poll(pfd,1,300);
       
   459     int r=read(outpipe[0],buff+c,BSIZE-c);
       
   460     if(r>0) c+=r;
       
   461     if(c>0) {
       
   462       buff[c]=0; // make sure buffer is NULL terminated
       
   463       char *p;
       
   464       do {
       
   465         p=strpbrk(buff,"\n\r");
       
   466         if(!p && c==BSIZE) { // Full buffer, but no newline found.
       
   467           p=&buff[c];        // Have to fake one.
       
   468           buff[c]='\n'; c++; buff[c]=0;
       
   469           }
       
   470         if(p) {
       
   471 #ifdef DEBUG
       
   472           char cc=*p;
       
   473 #endif
       
   474           *p++=0;
       
   475           float ftime=-1.0, fpos=-1.0;
       
   476           int itime;
       
   477           if(strncmp(buff,"Starting playback",17)==0 ||
       
   478              strncmp(buff,"Starte Wiedergabe",17)==0) {
       
   479             if(!playBack) {
       
   480               playBack=true;
       
   481               nextTime=nextPos=0;
       
   482               d(printf("PLAYBACK STARTED\n"))
       
   483               if(resPos>=0.0) {
       
   484                 if(!currentName ||
       
   485                    !strcmp(currentName,file->FullPath()) ||
       
   486                    !strcmp(currentName,file->Path()))
       
   487                   MPlayerControl("seek %.1f 1",resPos);
       
   488                 else
       
   489                   d(printf("mplayer: no resume, seems to be playlist\n"))
       
   490                 }
       
   491               }
       
   492             }
       
   493           else if(strncmp(buff,"Playing ",8)==0 ||
       
   494                   strncmp(buff,"Spiele ",7)==0) {
       
   495             nextTime=nextPos=0;
       
   496             index=saveIndex=total=-1;
       
   497             trustedTotal=false;
       
   498             LOCK_THREAD;
       
   499             free(currentName);
       
   500             currentName=strdup(::index(buff,' ')+1);
       
   501             if(currentName[0]) {
       
   502               int l=strlen(currentName);
       
   503               if(currentName[l-1]=='.') currentName[l-1]=0; // skip trailing dot
       
   504               }
       
   505             d(printf("PLAYING %s\n",currentName))
       
   506             }
       
   507           else if(sscanf(buff,"ANS_LENGTH=%d",&itime)==1) {
       
   508             if(itime>0) {
       
   509               total=SecondsToFrames(itime);
       
   510               trustedTotal=true;
       
   511 #ifdef DEBUG_SLAVE
       
   512               printf("sl: ANS_LENGTH=%s (%s)\n",IndexToHMSF(total),buff);
       
   513 #endif
       
   514               }
       
   515             }
       
   516           else if(sscanf(buff,"ANS_PERCENT_POSITION=%d",&itime)==1) {
       
   517             if(itime>0) {
       
   518               curPos=itime;
       
   519               if(total>=0) {
       
   520                 index=total*itime/100;
       
   521 #ifdef DEBUG_SLAVE
       
   522                 printf("sl: ANS_PERCENT_POS=%s (%s)\n",IndexToHMSF(index),buff);
       
   523 #endif
       
   524                 }
       
   525               }
       
   526             }
       
   527           else if(sscanf(buff,"SLAVE: time=%f position=%f",&ftime,&fpos)==2) {
       
   528             curPos=fpos;
       
   529             const float fr=(float)SecondsToFrames(1);
       
   530             itime=(int)(ftime*fr);
       
   531             if(saveIndex<0 || itime>saveIndex) { // prevent index jump-back
       
   532               saveIndex=index=itime;
       
   533               if(!trustedTotal) total=(int)(ftime*fr*100.0/fpos);
       
   534 #ifdef DEBUG_SLAVE
       
   535               printf("sl: SLAVE=%s/%s [%d] (%s)\n",IndexToHMSF(index),IndexToHMSF(total),trustedTotal,buff);
       
   536 #endif
       
   537               }
       
   538             slavePatch=playBack=true;
       
   539             }
       
   540 #ifdef DEBUG
       
   541           else printf("%s%c",buff,cc);
       
   542 #endif
       
   543           c-=(p-buff);
       
   544           memmove(buff,p,c+1);
       
   545           }
       
   546         } while(c>0 && p);
       
   547       }
       
   548     if(playBack) {
       
   549       SetMPlayerVolume(force);
       
   550       force=false;
       
   551       }
       
   552     }
       
   553 
       
   554   if(resume && curPos>=0.0) resume->SetResume(file,curPos);
       
   555 
       
   556   // restore old locale
       
   557   if(oldLocale) setlocale(LC_NUMERIC,oldLocale);
       
   558 
       
   559   dsyslog("mplayer: player thread ended (pid=%d)", getpid());
       
   560 }
       
   561 
       
   562 void cMPlayerPlayer::SetMPlayerVolume(bool force)
       
   563 {
       
   564   int volume;
       
   565   bool mute;
       
   566   Lock();
       
   567   if(status->GetVolume(volume,mute) || force) {
       
   568     if(mute) {
       
   569       if(!mpMute) { MPlayerControl("mute"); mpMute=true; }
       
   570       }
       
   571     else {
       
   572       if(mpMute) { MPlayerControl("mute"); mpMute=false; }
       
   573       if(volume!=mpVolume) {
       
   574         MPlayerControl("volume %d 1",volume);
       
   575         mpVolume=volume;
       
   576         }
       
   577       }
       
   578     d(printf("mplayer: volume=%d mpVolume=%d mpMute=%d\n",volume,mpVolume,mpMute))
       
   579     }
       
   580   Unlock();
       
   581 }
       
   582 
       
   583 void cMPlayerPlayer::MPlayerControl(const char *format, ...)
       
   584 {
       
   585   if(slave) {
       
   586     va_list ap;
       
   587     va_start(ap,format);
       
   588     char *buff=0;
       
   589     vasprintf(&buff,format,ap);
       
   590     Lock();
       
   591     // check for writeable pipe i.e. prevent broken pipe signal
       
   592     if(!brokenPipe) {
       
   593       struct pollfd pfd;
       
   594       pfd.fd=inpipe[1]; pfd.events=POLLOUT; pfd.revents=0;
       
   595       int r=poll(&pfd,1,50);
       
   596       if(r>0) {
       
   597         if(pfd.revents & ~POLLOUT) {
       
   598           d(printf("mplayer: %s%s%s%sin MPlayerControl\n",pfd.revents&POLLOUT?"POLLOUT ":"",pfd.revents&POLLERR?"POLLERR ":"",pfd.revents&POLLHUP?"POLLHUP ":"",pfd.revents&POLLNVAL?"POLLNVAL ":""))
       
   599           brokenPipe=true;
       
   600           }
       
   601         else if(pfd.revents & POLLOUT) {
       
   602           r=write(inpipe[1],buff,strlen(buff));
       
   603           if(r<0) {
       
   604             d(printf("mplayer: pipe write(1) failed: %s\n",strerror(errno)))
       
   605             brokenPipe=true;
       
   606             }
       
   607           else {
       
   608             r=write(inpipe[1],"\n",1);
       
   609             if(r<0) {
       
   610               d(printf("mplayer: pipe write(2) failed: %s\n",strerror(errno)))
       
   611               brokenPipe=true;
       
   612               }
       
   613             }
       
   614           }
       
   615         }
       
   616       else if(r==0) d(printf("mplayer: poll timed out in MPlayerControl (hugh?)\n"))
       
   617       else d(printf("mplayer: poll failed in MPlayerControl: %s\n",strerror(errno)))
       
   618       }
       
   619     else d(printf("mplayer: cmd pipe is broken\n"))
       
   620     Unlock();
       
   621     d(printf("mplayer: slave cmd: %s\n",buff))
       
   622     free(buff);
       
   623     va_end(ap);
       
   624     }
       
   625 }
       
   626 
       
   627 void cMPlayerPlayer::Pause(void)
       
   628 {
       
   629   if(slave) {
       
   630     if(playMode==pmPaused) Play();
       
   631     else if(playMode==pmPlay) {
       
   632       playMode=pmPaused;
       
   633       MPlayerControl("pause");
       
   634       }
       
   635     }
       
   636 }
       
   637 
       
   638 void cMPlayerPlayer::Play(void)
       
   639 {
       
   640   if(slave) {
       
   641     if(playMode==pmPaused) {
       
   642       playMode=pmPlay;
       
   643       MPlayerControl("pause");
       
   644       }
       
   645     }
       
   646 }
       
   647 
       
   648 void cMPlayerPlayer::Goto(int Index, bool percent, bool still)
       
   649 {
       
   650   if(slave) {
       
   651     if(playMode==pmPaused) Play();
       
   652     if(percent) MPlayerControl("seek %d 1",Index);
       
   653     else        MPlayerControl("seek %+d 0",Index-(index/SecondsToFrames(1)));
       
   654     if(still) Pause();
       
   655     saveIndex=-1;
       
   656     }
       
   657 }
       
   658 
       
   659 void cMPlayerPlayer::SkipSeconds(int secs)
       
   660 {
       
   661   if(slave) {
       
   662     bool p=false;
       
   663     if(playMode==pmPaused) { Play(); p=true; }
       
   664     MPlayerControl("seek %+d 0",secs);
       
   665     if(p) Pause();
       
   666     saveIndex=-1;
       
   667     }
       
   668 }
       
   669 
       
   670 void cMPlayerPlayer::KeyCmd(const char *cmd)
       
   671 {
       
   672   if(slave) MPlayerControl(cmd);
       
   673 }
       
   674 
       
   675 bool cMPlayerPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
       
   676 {
       
   677   Current=index; Total=total;
       
   678   return true;
       
   679 }
       
   680 
       
   681 bool cMPlayerPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
       
   682 {
       
   683   Play=(playMode==pmPlay);
       
   684   Forward=true;
       
   685   Speed=-1;
       
   686   return true;
       
   687 }
       
   688 
       
   689 char *cMPlayerPlayer::GetCurrentName(void)
       
   690 {
       
   691   LOCK_THREAD;
       
   692   return currentName ? strdup(currentName) : 0;
       
   693 }
       
   694