nathan@0: /* nathan@0: * MP3/MPlayer plugin to VDR (C++) nathan@0: * nathan@0: * (C) 2001-2006 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: nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: nathan@0: #include nathan@0: nathan@0: #include "common.h" nathan@0: #include "setup-mp3.h" nathan@0: #include "network.h" nathan@0: nathan@0: #define CON_TIMEOUT 30*1000 // default timeout (ms) for connect operation nathan@0: #define RW_TIMEOUT 30*1000 // default timeout (ms) for read/write operations nathan@0: #define BUFFERSIZE 128*1024 // default ringbuffer size (bytes) for async read nathan@0: nathan@0: #define NETDOWN_TIMEOUT 30 // timeout (s) for shutting down network nathan@0: nathan@0: const char *netscript=0; nathan@0: nathan@0: #if APIVERSNUM == 10131 nathan@0: #error Using this plugin with vdr 1.1.31 is not recommended (may cause high cpu load during streaming) nathan@0: #endif nathan@0: nathan@0: // ----------------------------------------------------------------------------- nathan@0: nathan@0: int RunCommand(const char *cmd, const char *State, const char *Name=0) nathan@0: { nathan@0: int res=-1; nathan@0: if(cmd) { nathan@0: char *tmp=0; nathan@0: if(Name) nathan@0: #if APIVERSNUM < 10318 nathan@0: asprintf(&tmp,"%s %s \"%s\"",cmd,State,strescape(Name,"\"$")); nathan@0: #else nathan@0: asprintf(&tmp,"%s %s \"%s\"",cmd,State,*strescape(Name,"\"$")); nathan@0: #endif nathan@0: else asprintf(&tmp,"%s %s",cmd,State); nathan@0: nathan@0: d(printf("run: executing '%s'\n",tmp)) nathan@0: res=SystemExec(tmp); nathan@0: free(tmp); nathan@0: } nathan@0: return res; nathan@0: } nathan@0: nathan@0: // -- cNetScript --------------------------------------------------------------- nathan@0: nathan@0: class cNetScript : public cThread { nathan@0: private: nathan@0: int count; nathan@0: bool pending; nathan@0: protected: nathan@0: virtual void Action(void); nathan@0: public: nathan@0: cNetScript(void); nathan@0: ~cNetScript(); nathan@0: void Up(void); nathan@0: void Down(void); nathan@0: }; nathan@0: nathan@0: cNetScript ns; nathan@0: nathan@0: cNetScript::cNetScript(void) nathan@0: { nathan@0: count=0; pending=false; nathan@0: } nathan@0: nathan@0: cNetScript::~cNetScript() nathan@0: { nathan@0: if(pending) Cancel(0); nathan@0: } nathan@0: nathan@0: void cNetScript::Up(void) nathan@0: { nathan@0: Lock(); nathan@0: if(netscript) { nathan@0: if(pending) { Cancel(0); pending=false; } nathan@0: RunCommand(netscript,"up"); nathan@0: count++; nathan@0: } nathan@0: Unlock(); nathan@0: } nathan@0: nathan@0: void cNetScript::Down(void) nathan@0: { nathan@0: Lock(); nathan@0: if(netscript) { nathan@0: if(--count==0) { Start(); pending=true; } nathan@0: } nathan@0: Unlock(); nathan@0: } nathan@0: nathan@0: void cNetScript::Action(void) nathan@0: { nathan@0: d(printf("net: netscript down delay\n")) nathan@0: sleep(NETDOWN_TIMEOUT); nathan@0: Lock(); nathan@0: RunCommand(netscript,"down"); nathan@0: Unlock(); nathan@0: } nathan@0: nathan@0: // -- cNetConnect -------------------------------------------------------------- nathan@0: nathan@0: class cNetConnect : public cThread { nathan@0: private: nathan@0: int fd; nathan@0: const char *hostname; nathan@0: int port; nathan@0: cMutex conMutex; nathan@0: cCondVar conCond; nathan@0: int result; nathan@0: protected: nathan@0: virtual void Action(void); nathan@0: void Done(int res); nathan@0: public: nathan@0: cNetConnect(int Fd, const char *Hostname, int Port); nathan@0: ~cNetConnect(); nathan@0: int Wait(int timeoutMs); nathan@0: }; nathan@0: nathan@0: cNetConnect::cNetConnect(int Fd, const char *Hostname, int Port) nathan@0: { nathan@0: fd=Fd; nathan@0: hostname=Hostname; nathan@0: port=Port; nathan@0: result=0; nathan@0: Start(); nathan@0: } nathan@0: nathan@0: cNetConnect::~cNetConnect() nathan@0: { nathan@0: Cancel(1); nathan@0: } nathan@0: nathan@0: int cNetConnect::Wait(int timeoutMs) nathan@0: { nathan@0: conMutex.Lock(); nathan@0: if(!result) conCond.TimedWait(conMutex,timeoutMs); nathan@0: conMutex.Unlock(); nathan@0: return result; nathan@0: } nathan@0: nathan@0: void cNetConnect::Done(int res) nathan@0: { nathan@0: conMutex.Lock(); nathan@0: result=res; nathan@0: conCond.Broadcast(); nathan@0: conMutex.Unlock(); nathan@0: } nathan@0: nathan@0: void cNetConnect::Action(void) nathan@0: { nathan@0: d(printf("net: name lookup %s\n",hostname)) nathan@0: struct hostent *hp=gethostbyname(hostname); nathan@0: if(hp) { nathan@0: struct sockaddr_in sin; nathan@0: sin.sin_port=htons(port); nathan@0: sin.sin_family=AF_INET; nathan@0: memcpy((char *)&sin.sin_addr,hp->h_addr,hp->h_length); nathan@0: d(printf("net: connecting to %s:%d\n",hostname,port)) nathan@0: if(connect(fd,(struct sockaddr *)&sin,sizeof(sin))==0) { nathan@0: d(printf("net: connected\n")) nathan@0: Done(1); nathan@0: } nathan@0: else { esyslog("connect() failed: %s",strerror(errno)); Done(-1); } nathan@0: } nathan@0: else { esyslog("Unknown host '%s'",hostname); Done(-1); } nathan@0: } nathan@0: nathan@0: // -- cNet --------------------------------------------------------------------- nathan@0: nathan@0: cNet::cNet(int size, int ConTimeoutMs, int RwTimeoutMs) nathan@0: :cRingBufferLinear(size>0?size:BUFFERSIZE,1,false) nathan@0: { nathan@0: fd=-1; deferedErrno=0; count=0; nathan@0: connected=netup=false; nathan@0: rwTimeout =RwTimeoutMs ? RwTimeoutMs :RW_TIMEOUT; nathan@0: conTimeout=ConTimeoutMs ? ConTimeoutMs:CON_TIMEOUT; nathan@0: #if APIVERSNUM >= 10132 nathan@0: SetTimeouts(50,50); nathan@0: #endif nathan@0: } nathan@0: nathan@0: cNet::~cNet() nathan@0: { nathan@0: Disconnect(); nathan@0: } nathan@0: nathan@0: void cNet::Close(void) nathan@0: { nathan@0: if(connected) { nathan@0: connected=false; nathan@0: Cancel(2); nathan@0: deferedErrno=0; nathan@0: } nathan@0: if(fd>=0) { close(fd); fd=-1; } nathan@0: Clear(); count=0; nathan@0: } nathan@0: nathan@0: void cNet::Disconnect(void) nathan@0: { nathan@0: Close(); nathan@0: if(netup) { ns.Down(); netup=false; } nathan@0: } nathan@0: nathan@0: bool cNet::Connect(const char *hostname, const int port) nathan@0: { nathan@0: Close(); nathan@0: fd=socket(AF_INET,SOCK_STREAM,0); nathan@0: if(fd>=0) { nathan@0: ns.Up(); netup=true; nathan@0: cNetConnect *con=new cNetConnect(fd,hostname,port); nathan@0: int res=con->Wait(conTimeout); nathan@0: delete con; nathan@0: if(res>0) { nathan@0: if(fcntl(fd,F_SETFL,O_NONBLOCK)>=0) { nathan@0: deferedErrno=0; connected=true; nathan@0: Start(); nathan@0: return(true); nathan@0: } nathan@0: else esyslog("fnctl() failed: %s",strerror(errno)); nathan@0: } nathan@0: else if(res==0) esyslog("Connection timed out"); nathan@0: } nathan@0: else esyslog("socket() failed: %s",strerror(errno)); nathan@0: Disconnect(); nathan@0: return false; nathan@0: } nathan@0: nathan@0: void cNet::CopyFromBuff(unsigned char *dest, int n) nathan@0: { nathan@0: memcpy(dest,lineBuff,n); nathan@0: count-=n; nathan@0: if(count>0) memmove(lineBuff,lineBuff+n,count); nathan@0: } nathan@0: nathan@0: int cNet::Gets(char *dest, int len) nathan@0: { nathan@0: len--; // let room for trailing zero nathan@0: int c=0; nathan@0: while(c0) { nathan@0: c=count; if(c>len) c=len; nathan@0: CopyFromBuff(dest,c); nathan@0: } nathan@0: else { nathan@0: c=RingRead(dest,len); nathan@0: } nathan@0: return c; nathan@0: } nathan@0: nathan@0: int cNet::Write(unsigned char *dest, int len) nathan@0: { nathan@0: int t=0, r; nathan@0: cPoller poll(fd,true); nathan@0: do { nathan@0: if(poll.Poll(rwTimeout)) { nathan@0: r=write(fd,dest,len); nathan@0: if(r<0 && errno!=EAGAIN) { nathan@0: esyslog("write() failed: %s",strerror(errno)); nathan@0: break; nathan@0: } nathan@0: dest+=r; len-=r; t+=r; nathan@0: } nathan@0: else { esyslog("Write timed out"); break; } nathan@0: } while(len>0); nathan@0: return t; nathan@0: } nathan@0: nathan@0: int cNet::Puts(char *dest) nathan@0: { nathan@0: return Write((unsigned char *)dest,strlen(dest)); nathan@0: } nathan@0: nathan@0: int cNet::RingRead(unsigned char *dest, int len) nathan@0: { nathan@0: int r=0; nathan@0: const uchar *rd; nathan@0: for(;;) { nathan@0: if(!Available() && deferedErrno) { nathan@0: d(printf("net: ringbuffer empty, async read bailed out\n")) nathan@0: return -1; nathan@0: } nathan@0: rd=Get(r); nathan@0: if(rd && r>0) { nathan@0: if(r>len) r=len; nathan@0: memcpy(dest,rd,r); nathan@0: Del(r); nathan@0: return r; nathan@0: } nathan@0: } nathan@0: } nathan@0: nathan@0: void cNet::Action(void) nathan@0: { nathan@0: d(printf("net: async read started\n")) nathan@0: nathan@0: cPoller poll(fd,false); nathan@0: while(connected) { nathan@0: if(poll.Poll(rwTimeout)) { nathan@0: unsigned char buff[8192]; nathan@0: int r=read(fd,buff,sizeof(buff)); nathan@0: if(r>0) { nathan@0: int d=0; nathan@0: do { d+=Put(buff+d,r-d); } while(d