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