nathan@0: /* nathan@0: * MP3/MPlayer plugin to VDR (C++) nathan@0: * nathan@0: * (C) 2001-2007 Stefan Huelswitt nathan@0: * nathan@0: * This code is free software; you can redistribute it and/or nathan@0: * modify it under the terms of the GNU General Public License nathan@0: * as published by the Free Software Foundation; either version 2 nathan@0: * of the License, or (at your option) any later version. nathan@0: * nathan@0: * This code is distributed in the hope that it will be useful, nathan@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of nathan@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nathan@0: * GNU General Public License for more details. nathan@0: * nathan@0: * You should have received a copy of the GNU General Public License nathan@0: * along with this program; if not, write to the Free Software nathan@0: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. nathan@0: * Or, point your browser to http://www.gnu.org/copyleft/gpl.html nathan@0: */ nathan@0: nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: nathan@0: #include nathan@0: #include nathan@0: nathan@0: #include "common.h" nathan@0: #include "data.h" nathan@0: #include "player-mplayer.h" nathan@0: #include "setup-mplayer.h" nathan@0: nathan@0: //#define DEBUG_SLAVE nathan@0: nathan@0: #define MPLAYER_VOL_STEP 3.0 nathan@0: nathan@0: const char *MPlayerCmd = "mplayer.sh"; nathan@0: int MPlayerAid=-1; nathan@0: const char *globalResumeDir = 0; nathan@0: nathan@0: // -- cMPlayerStatus ----------------------------------------------------------- nathan@0: nathan@0: cMPlayerStatus *status; nathan@0: nathan@0: cMPlayerStatus::cMPlayerStatus(void) nathan@0: { nathan@0: mute=changed=false; nathan@0: volume=0; nathan@0: } nathan@0: nathan@0: bool cMPlayerStatus::GetVolume(int &Volume, bool &Mute) nathan@0: { nathan@0: Lock(); nathan@0: bool r=changed; nathan@0: // convert according cDvbDevice::SetVolumeDevice(); nathan@0: // take into account that VDR does non-linear changes, while nathan@0: // MPlayer does linear volume changes. nathan@0: Volume=((2*volume-volume*volume/255)*100+128)/256; nathan@0: Mute=mute; nathan@0: changed=false; nathan@0: Unlock(); nathan@0: return r; nathan@0: } nathan@0: nathan@0: void cMPlayerStatus::SetVolume(int Volume, bool Absolute) nathan@0: { nathan@0: Lock(); nathan@0: if(Absolute && Volume==0) mute=true; nathan@0: else { nathan@0: #if APIVERSNUM>=10401 nathan@0: #if APIVERSNUM==10401 nathan@0: #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: #endif nathan@0: if(!Absolute) nathan@0: volume+=Volume; nathan@0: else nathan@0: #endif nathan@0: volume=Volume; nathan@0: if(volume>0) mute=false; nathan@0: } nathan@0: d(printf("status: volume=%d mute=%d\n",volume,mute)) nathan@0: changed=true; nathan@0: Unlock(); nathan@0: } nathan@0: nathan@0: // --- cResumeEntry ------------------------------------------------------------ nathan@0: nathan@0: class cResumeEntry : public cListObject { nathan@0: public: nathan@0: char *name; nathan@0: float pos; nathan@0: // nathan@0: cResumeEntry(void); nathan@0: ~cResumeEntry(); nathan@0: }; nathan@0: nathan@0: cResumeEntry::cResumeEntry(void) nathan@0: { nathan@0: name=0; nathan@0: } nathan@0: nathan@0: cResumeEntry::~cResumeEntry() nathan@0: { nathan@0: free(name); nathan@0: } nathan@0: nathan@0: // --- cMPlayerResume ---------------------------------------------------------- nathan@0: nathan@0: #define RESUME_FILE ".mplayer.resume" nathan@0: #define GLOBAL_RESUME_FILE "global.mplayer.resume" nathan@0: nathan@0: class cMPlayerResume : public cList { nathan@0: private: nathan@0: char *resfile; nathan@0: bool modified, global; nathan@0: cFileObj *resobj; nathan@0: // nathan@0: bool OpenResume(const cFileObj *file); nathan@0: bool SaveResume(void); nathan@0: void Purge(void); nathan@0: cResumeEntry *FindResume(const cFileObj *file); nathan@0: public: nathan@0: cMPlayerResume(void); nathan@0: ~cMPlayerResume(); nathan@0: void SetResume(const cFileObj *file, float pos); nathan@0: bool GetResume(const cFileObj *file, float &pos); nathan@0: }; nathan@0: nathan@0: cMPlayerResume::cMPlayerResume(void) nathan@0: { nathan@0: resfile=0; resobj=0; nathan@0: } nathan@0: nathan@0: cMPlayerResume::~cMPlayerResume() nathan@0: { nathan@0: SaveResume(); nathan@0: free(resfile); nathan@0: delete resobj; nathan@0: } nathan@0: nathan@0: void cMPlayerResume::SetResume(const cFileObj *file, float pos) nathan@0: { nathan@0: if(pos<0.001) pos=0.0; nathan@0: else if(pos>99.0) pos=99.0; nathan@0: cResumeEntry *re; nathan@0: if(OpenResume(file) && (re=FindResume(file))) { nathan@0: d(printf("resume: setting resume %f (update)\n",pos)) nathan@0: } nathan@0: else { nathan@0: re=new cResumeEntry; nathan@0: re->name=strdup(global ? file->FullPath() : file->Name()); nathan@0: Add(re); nathan@0: d(printf("resume: setting resume %f (new)\n",pos)) nathan@0: } nathan@0: re->pos=pos; nathan@0: modified=true; nathan@0: } nathan@0: nathan@0: bool cMPlayerResume::GetResume(const cFileObj *file, float &pos) nathan@0: { nathan@0: cResumeEntry *re; nathan@0: if(OpenResume(file) && (re=FindResume(file))) { nathan@0: pos=re->pos; nathan@0: return true; nathan@0: } nathan@0: return false; nathan@0: } nathan@0: nathan@0: bool cMPlayerResume::OpenResume(const cFileObj *file) nathan@0: { nathan@0: if(!resfile) { nathan@0: Clear(); nathan@0: modified=global=false; nathan@0: free(resfile); resfile=0; nathan@0: delete resobj; resobj=new cFileObj(file); nathan@0: char *s; nathan@0: asprintf(&s,file->Subdir() ? "%s/%s":"%s",file->Source()->BaseDir(),file->Subdir()); nathan@0: if(MPlayerSetup.ResumeMode==1 || nathan@0: (access(s,W_OK) && (errno==EACCES || errno==EROFS))) { nathan@0: global=true; nathan@0: resfile=AddPath(globalResumeDir?globalResumeDir:VideoDirectory,GLOBAL_RESUME_FILE); nathan@0: d(printf("resume: using global file\n")) nathan@0: } nathan@0: else { nathan@0: resfile=AddPath(s,RESUME_FILE); nathan@0: } nathan@0: free(s); nathan@0: d(printf("resume: resume file is '%s'\n",resfile)) nathan@0: FILE *f=fopen(resfile,"r"); nathan@0: if(f) { nathan@0: d(printf("resume: successfully opened resume file\n")) nathan@0: char line[768]; nathan@0: while(fgets(line,sizeof(line),f)) { nathan@0: char name[512]; nathan@0: float p; nathan@0: if(sscanf(line,"%f:%511[^\n]",&p,name)==2) { nathan@0: cResumeEntry *re=new cResumeEntry; nathan@0: re->name=strdup(name); nathan@0: re->pos=p; nathan@0: Add(re); nathan@0: } nathan@0: } nathan@0: fclose(f); nathan@0: return true; nathan@0: } nathan@0: else { nathan@0: d(printf("resume: assuming empty resume file\n")) nathan@0: return false; nathan@0: } nathan@0: } nathan@0: return true; nathan@0: } nathan@0: nathan@0: bool cMPlayerResume::SaveResume(void) nathan@0: { nathan@0: if(resfile && modified) { nathan@0: Purge(); nathan@0: d(printf("resume: saving resume file\n")) nathan@0: cSafeFile f(resfile); nathan@0: if(f.Open()) { nathan@0: for(cResumeEntry *re=First(); re; re=Next(re)) nathan@0: fprintf(f,"%06.2f:%s\n",re->pos,re->name); nathan@0: f.Close(); nathan@0: return true; nathan@0: } nathan@0: else nathan@0: d(printf("resume: failed to save resume file\n")) nathan@0: } nathan@0: return false; nathan@0: } nathan@0: nathan@0: void cMPlayerResume::Purge(void) nathan@0: { nathan@0: d(printf("resume: purging from resume file\n")) nathan@0: for(cResumeEntry *re=First(); re;) { nathan@0: bool del=false; nathan@0: if(re->pos<1.0 || re->pos>99.0) { nathan@0: del=true; nathan@0: d(printf("resume: purging due to position: %s\n",re->name)) nathan@0: } nathan@0: else if(!global) { nathan@0: resobj->SetName(re->name); nathan@0: if(access(resobj->FullPath(),F_OK)<0) { nathan@0: del=true; nathan@0: d(printf("resume: purging due to access: %s\n",re->name)) nathan@0: } nathan@0: } nathan@0: if(del) { nathan@0: cResumeEntry *n=Next(re); nathan@0: Del(re); nathan@0: modified=true; nathan@0: re=n; nathan@0: } nathan@0: else nathan@0: re=Next(re); nathan@0: } nathan@0: } nathan@0: nathan@0: cResumeEntry *cMPlayerResume::FindResume(const cFileObj *file) nathan@0: { nathan@0: if(resfile) { nathan@0: d(printf("resume: searching resume position for '%s'\n",file->Name())) nathan@0: const char *s=global ? file->FullPath() : file->Name(); nathan@0: for(cResumeEntry *re=First(); re; re=Next(re)) nathan@0: if(!strcasecmp(re->name,s)) { nathan@0: d(printf("resume: found resume position %.1f%%\n",re->pos)) nathan@0: return re; nathan@0: } nathan@0: } nathan@0: d(printf("resume: no resume position found\n")) nathan@0: return 0; nathan@0: } nathan@0: nathan@0: // --- cMPlayerPlayer ---------------------------------------------------------- nathan@0: nathan@0: cMPlayerPlayer::cMPlayerPlayer(const cFileObj *File, bool Rewind) nathan@0: :cPlayer(pmExtern_THIS_SHOULD_BE_AVOIDED) nathan@0: { nathan@0: started=slave=brokenPipe=false; run=true; pid=-1; pipefl=0; nathan@0: playMode=pmPlay; index=saveIndex=total=-1; nextTime=nextPos=0; nathan@0: currentName=0; nathan@0: file=new cFileObj(File); nathan@0: rewind=Rewind; nathan@0: resume=MPlayerSetup.ResumeMode ? new cMPlayerResume : 0; nathan@0: } nathan@0: nathan@0: cMPlayerPlayer::~cMPlayerPlayer() nathan@0: { nathan@0: Detach(); nathan@0: ClosePipe(); nathan@0: delete file; nathan@0: delete resume; nathan@0: free(currentName); nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::ClosePipe(void) nathan@0: { nathan@0: if(pipefl&1) close(inpipe[0]); nathan@0: if(pipefl&2) close(inpipe[1]); nathan@0: if(pipefl&4) close(outpipe[0]); nathan@0: if(pipefl&8) close(outpipe[1]); nathan@0: pipefl=0; nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::Activate(bool On) nathan@0: { nathan@0: if(On) { nathan@0: if(file && !started) { nathan@0: if(Fork()) started=true; nathan@0: } nathan@0: } nathan@0: else if(started) { nathan@0: run=false; nathan@0: if(Active()) { nathan@0: if(slave) { nathan@0: Play(); // MPlayer ignores "quit" while paused nathan@0: MPlayerControl("quit"); nathan@0: int until=time_ms()+3000; // wait some time until MPlayer is gone nathan@0: d(printf("mplayer: waiting for child exit")) nathan@0: while(Active()) { nathan@0: if(time_ms()>until) { nathan@0: kill(pid,SIGKILL); // kill it anyways nathan@0: d(printf(" SIGKILL")) nathan@0: break; nathan@0: } nathan@0: SLEEP(250); nathan@0: d(printf(".")) d(fflush(stdout)) nathan@0: } nathan@0: d(printf("\n")) nathan@0: } nathan@0: else { nathan@0: kill(pid,SIGTERM); nathan@0: d(printf("mplayer: waiting for child exit (non-slave)\n")) nathan@0: } nathan@0: waitpid(pid,0,0); nathan@0: } nathan@0: ClosePipe(); nathan@0: Cancel(2); nathan@0: started=slave=false; nathan@0: } nathan@0: } nathan@0: nathan@0: bool cMPlayerPlayer::Active(void) nathan@0: { nathan@0: return waitpid(pid,0,WNOHANG)==0; nathan@0: } nathan@0: nathan@0: bool cMPlayerPlayer::Fork(void) nathan@0: { nathan@0: if(MPlayerSetup.SlaveMode) { nathan@0: if(pipe(inpipe)==-1) { nathan@0: esyslog("ERROR: pipe failed for inpipe: (%d) %s",errno,strerror(errno)); nathan@0: return false; nathan@0: } nathan@0: pipefl|=1+2; nathan@0: if(pipe(outpipe)==-1) { nathan@0: esyslog("ERROR: pipe failed for outpipe: (%d) %s",errno,strerror(errno)); nathan@0: return false; nathan@0: } nathan@0: pipefl|=4+8; nathan@0: brokenPipe=false; nathan@0: } nathan@0: nathan@0: pid=fork(); nathan@0: if(pid==-1) { nathan@0: esyslog("ERROR: fork failed: (%d) %s",errno,strerror(errno)); nathan@0: return false; nathan@0: } nathan@0: if(pid==0) { // child nathan@0: dsyslog("mplayer: mplayer child started (pid=%d)", getpid()); nathan@0: nathan@0: if(MPlayerSetup.SlaveMode) { nathan@0: if(dup2(inpipe[0],STDIN_FILENO)<0 || nathan@0: dup2(outpipe[1],STDOUT_FILENO)<0 || nathan@0: dup2(outpipe[1],STDERR_FILENO)<0) { nathan@0: esyslog("ERROR: dup2() failed in MPlayer child: (%d) %s",errno,strerror(errno)); nathan@0: exit(127); nathan@0: } nathan@0: } nathan@0: else { nathan@0: int nfd=open("/dev/null",O_RDONLY); nathan@0: if(nfd<0 || dup2(nfd,STDIN_FILENO)<0) nathan@0: esyslog("ERROR: redirect of STDIN failed in MPlayer child: (%d) %s",errno,strerror(errno)); nathan@0: } nathan@0: for(int i=getdtablesize()-1; i>STDERR_FILENO; i--) close(i); nathan@0: nathan@0: char cmd[64+PATH_MAX*2], aid[20]; nathan@0: char *fname=Quote(file->FullPath()); nathan@0: if(MPlayerAid>=0) snprintf(aid,sizeof(aid)," AID %d",MPlayerAid); nathan@0: else aid[0]=0; nathan@0: snprintf(cmd,sizeof(cmd),"%s \"%s\" %s%s",MPlayerCmd,fname,MPlayerSetup.SlaveMode?"SLAVE":"",aid); nathan@0: free(fname); nathan@0: execle("/bin/sh","sh","-c",cmd,(char *)0,environ); nathan@0: esyslog("ERROR: exec failed for %s: (%d) %s",cmd,errno,strerror(errno)); nathan@0: exit(127); nathan@0: } nathan@0: nathan@0: if(MPlayerSetup.SlaveMode) { nathan@0: close(inpipe[0]); pipefl&=~1; nathan@0: close(outpipe[1]); pipefl&=~8; nathan@0: fcntl(outpipe[0],F_SETFL,O_NONBLOCK); nathan@0: run=slave=true; nathan@0: mpVolume=100; // MPlayer startup defaults nathan@0: mpMute=false; nathan@0: Start(); nathan@0: } nathan@0: return true; nathan@0: } nathan@0: nathan@0: #define BSIZE 1024 nathan@0: #define TIME_INT 20 nathan@0: #define POS_INT 1 nathan@0: nathan@0: void cMPlayerPlayer::Action(void) nathan@0: { nathan@0: dsyslog("mplayer: player thread started (pid=%d)", getpid()); nathan@0: nathan@0: // set locale for correct parsing of MPlayer output. nathan@0: // I don't know if this affects other parts of VDR. nathan@0: const char * const oldLocale=setlocale(LC_NUMERIC,"C"); nathan@0: nathan@0: pollfd pfd[1]; nathan@0: pfd[0].fd=outpipe[0]; nathan@0: pfd[0].events=POLLIN; nathan@0: nathan@0: float curPos=-1.0, resPos=-1.0; nathan@0: if(resume && !rewind) resume->GetResume(file,resPos); nathan@0: nathan@0: char buff[BSIZE+2]; // additional space for fake newline nathan@0: int c=0; nathan@0: bool force=true, slavePatch=false, trustedTotal=false, playBack=false; nathan@0: while(run) { nathan@0: if(playMode==pmPlay && playBack) { nathan@0: int t=time(0); nathan@0: if(t>=nextTime) { nathan@0: MPlayerControl("get_time_length"); nathan@0: nextTime=t+(total>0 ? TIME_INT : POS_INT); nathan@0: } nathan@0: if(t>=nextPos) { nathan@0: if(!slavePatch) MPlayerControl("get_percent_pos"); nathan@0: nextPos=t+POS_INT; nathan@0: } nathan@0: } nathan@0: nathan@0: poll(pfd,1,300); nathan@0: int r=read(outpipe[0],buff+c,BSIZE-c); nathan@0: if(r>0) c+=r; nathan@0: if(c>0) { nathan@0: buff[c]=0; // make sure buffer is NULL terminated nathan@0: char *p; nathan@0: do { nathan@0: p=strpbrk(buff,"\n\r"); nathan@0: if(!p && c==BSIZE) { // Full buffer, but no newline found. nathan@0: p=&buff[c]; // Have to fake one. nathan@0: buff[c]='\n'; c++; buff[c]=0; nathan@0: } nathan@0: if(p) { nathan@0: #ifdef DEBUG nathan@0: char cc=*p; nathan@0: #endif nathan@0: *p++=0; nathan@0: float ftime=-1.0, fpos=-1.0; nathan@0: int itime; nathan@0: if(strncmp(buff,"Starting playback",17)==0 || nathan@0: strncmp(buff,"Starte Wiedergabe",17)==0) { nathan@0: if(!playBack) { nathan@0: playBack=true; nathan@0: nextTime=nextPos=0; nathan@0: d(printf("PLAYBACK STARTED\n")) nathan@0: if(resPos>=0.0) { nathan@0: if(!currentName || nathan@0: !strcmp(currentName,file->FullPath()) || nathan@0: !strcmp(currentName,file->Path())) nathan@0: MPlayerControl("seek %.1f 1",resPos); nathan@0: else nathan@0: d(printf("mplayer: no resume, seems to be playlist\n")) nathan@0: } nathan@0: } nathan@0: } nathan@0: else if(strncmp(buff,"Playing ",8)==0 || nathan@0: strncmp(buff,"Spiele ",7)==0) { nathan@0: nextTime=nextPos=0; nathan@0: index=saveIndex=total=-1; nathan@0: trustedTotal=false; nathan@0: LOCK_THREAD; nathan@0: free(currentName); nathan@0: currentName=strdup(::index(buff,' ')+1); nathan@0: if(currentName[0]) { nathan@0: int l=strlen(currentName); nathan@0: if(currentName[l-1]=='.') currentName[l-1]=0; // skip trailing dot nathan@0: } nathan@0: d(printf("PLAYING %s\n",currentName)) nathan@0: } nathan@0: else if(sscanf(buff,"ANS_LENGTH=%d",&itime)==1) { nathan@0: if(itime>0) { nathan@0: total=SecondsToFrames(itime); nathan@0: trustedTotal=true; nathan@0: #ifdef DEBUG_SLAVE nathan@0: printf("sl: ANS_LENGTH=%s (%s)\n",IndexToHMSF(total),buff); nathan@0: #endif nathan@0: } nathan@0: } nathan@0: else if(sscanf(buff,"ANS_PERCENT_POSITION=%d",&itime)==1) { nathan@0: if(itime>0) { nathan@0: curPos=itime; nathan@0: if(total>=0) { nathan@0: index=total*itime/100; nathan@0: #ifdef DEBUG_SLAVE nathan@0: printf("sl: ANS_PERCENT_POS=%s (%s)\n",IndexToHMSF(index),buff); nathan@0: #endif nathan@0: } nathan@0: } nathan@0: } nathan@0: else if(sscanf(buff,"SLAVE: time=%f position=%f",&ftime,&fpos)==2) { nathan@0: curPos=fpos; nathan@0: const float fr=(float)SecondsToFrames(1); nathan@0: itime=(int)(ftime*fr); nathan@0: if(saveIndex<0 || itime>saveIndex) { // prevent index jump-back nathan@0: saveIndex=index=itime; nathan@0: if(!trustedTotal) total=(int)(ftime*fr*100.0/fpos); nathan@0: #ifdef DEBUG_SLAVE nathan@0: printf("sl: SLAVE=%s/%s [%d] (%s)\n",IndexToHMSF(index),IndexToHMSF(total),trustedTotal,buff); nathan@0: #endif nathan@0: } nathan@0: slavePatch=playBack=true; nathan@0: } nathan@0: #ifdef DEBUG nathan@0: else printf("%s%c",buff,cc); nathan@0: #endif nathan@0: c-=(p-buff); nathan@0: memmove(buff,p,c+1); nathan@0: } nathan@0: } while(c>0 && p); nathan@0: } nathan@0: if(playBack) { nathan@0: SetMPlayerVolume(force); nathan@0: force=false; nathan@0: } nathan@0: } nathan@0: nathan@0: if(resume && curPos>=0.0) resume->SetResume(file,curPos); nathan@0: nathan@0: // restore old locale nathan@0: if(oldLocale) setlocale(LC_NUMERIC,oldLocale); nathan@0: nathan@0: dsyslog("mplayer: player thread ended (pid=%d)", getpid()); nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::SetMPlayerVolume(bool force) nathan@0: { nathan@0: int volume; nathan@0: bool mute; nathan@0: Lock(); nathan@0: if(status->GetVolume(volume,mute) || force) { nathan@0: if(mute) { nathan@0: if(!mpMute) { MPlayerControl("mute"); mpMute=true; } nathan@0: } nathan@0: else { nathan@0: if(mpMute) { MPlayerControl("mute"); mpMute=false; } nathan@0: if(volume!=mpVolume) { nathan@0: MPlayerControl("volume %d 1",volume); nathan@0: mpVolume=volume; nathan@0: } nathan@0: } nathan@0: d(printf("mplayer: volume=%d mpVolume=%d mpMute=%d\n",volume,mpVolume,mpMute)) nathan@0: } nathan@0: Unlock(); nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::MPlayerControl(const char *format, ...) nathan@0: { nathan@0: if(slave) { nathan@0: va_list ap; nathan@0: va_start(ap,format); nathan@0: char *buff=0; nathan@0: vasprintf(&buff,format,ap); nathan@0: Lock(); nathan@0: // check for writeable pipe i.e. prevent broken pipe signal nathan@0: if(!brokenPipe) { nathan@0: struct pollfd pfd; nathan@0: pfd.fd=inpipe[1]; pfd.events=POLLOUT; pfd.revents=0; nathan@0: int r=poll(&pfd,1,50); nathan@0: if(r>0) { nathan@0: if(pfd.revents & ~POLLOUT) { nathan@0: 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: brokenPipe=true; nathan@0: } nathan@0: else if(pfd.revents & POLLOUT) { nathan@0: r=write(inpipe[1],buff,strlen(buff)); nathan@0: if(r<0) { nathan@0: d(printf("mplayer: pipe write(1) failed: %s\n",strerror(errno))) nathan@0: brokenPipe=true; nathan@0: } nathan@0: else { nathan@0: r=write(inpipe[1],"\n",1); nathan@0: if(r<0) { nathan@0: d(printf("mplayer: pipe write(2) failed: %s\n",strerror(errno))) nathan@0: brokenPipe=true; nathan@0: } nathan@0: } nathan@0: } nathan@0: } nathan@0: else if(r==0) d(printf("mplayer: poll timed out in MPlayerControl (hugh?)\n")) nathan@0: else d(printf("mplayer: poll failed in MPlayerControl: %s\n",strerror(errno))) nathan@0: } nathan@0: else d(printf("mplayer: cmd pipe is broken\n")) nathan@0: Unlock(); nathan@0: d(printf("mplayer: slave cmd: %s\n",buff)) nathan@0: free(buff); nathan@0: va_end(ap); nathan@0: } nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::Pause(void) nathan@0: { nathan@0: if(slave) { nathan@0: if(playMode==pmPaused) Play(); nathan@0: else if(playMode==pmPlay) { nathan@0: playMode=pmPaused; nathan@0: MPlayerControl("pause"); nathan@0: } nathan@0: } nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::Play(void) nathan@0: { nathan@0: if(slave) { nathan@0: if(playMode==pmPaused) { nathan@0: playMode=pmPlay; nathan@0: MPlayerControl("pause"); nathan@0: } nathan@0: } nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::Goto(int Index, bool percent, bool still) nathan@0: { nathan@0: if(slave) { nathan@0: if(playMode==pmPaused) Play(); nathan@0: if(percent) MPlayerControl("seek %d 1",Index); nathan@0: else MPlayerControl("seek %+d 0",Index-(index/SecondsToFrames(1))); nathan@0: if(still) Pause(); nathan@0: saveIndex=-1; nathan@0: } nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::SkipSeconds(int secs) nathan@0: { nathan@0: if(slave) { nathan@0: bool p=false; nathan@0: if(playMode==pmPaused) { Play(); p=true; } nathan@0: MPlayerControl("seek %+d 0",secs); nathan@0: if(p) Pause(); nathan@0: saveIndex=-1; nathan@0: } nathan@0: } nathan@0: nathan@0: void cMPlayerPlayer::KeyCmd(const char *cmd) nathan@0: { nathan@0: if(slave) MPlayerControl(cmd); nathan@0: } nathan@0: nathan@0: bool cMPlayerPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame) nathan@0: { nathan@0: Current=index; Total=total; nathan@0: return true; nathan@0: } nathan@0: nathan@0: bool cMPlayerPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed) nathan@0: { nathan@0: Play=(playMode==pmPlay); nathan@0: Forward=true; nathan@0: Speed=-1; nathan@0: return true; nathan@0: } nathan@0: nathan@0: char *cMPlayerPlayer::GetCurrentName(void) nathan@0: { nathan@0: LOCK_THREAD; nathan@0: return currentName ? strdup(currentName) : 0; nathan@0: } nathan@0: