stream.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
nathan@0
     1
/*
nathan@0
     2
 * MP3/MPlayer plugin to VDR (C++)
nathan@0
     3
 *
nathan@0
     4
 * (C) 2001-2006 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 <stdlib.h>
nathan@0
    23
#include <stdio.h>
nathan@0
    24
#include <unistd.h>
nathan@0
    25
#include <errno.h>
nathan@0
    26
nathan@0
    27
#include "common.h"
nathan@0
    28
#include "setup-mp3.h"
nathan@0
    29
#include "stream.h"
nathan@0
    30
#include "network.h"
nathan@0
    31
#include "menu-async.h"
nathan@0
    32
#include "i18n.h"
nathan@0
    33
#include "version.h"
nathan@0
    34
nathan@0
    35
#define USE_MMAP
nathan@0
    36
#define MAX_MMAP_SIZE   (32*1024*1024)
nathan@0
    37
#define MP3FILE_BUFSIZE (32*1024)
nathan@0
    38
nathan@0
    39
#ifdef USE_MMAP
nathan@0
    40
#include <sys/mman.h>
nathan@0
    41
#endif
nathan@0
    42
nathan@0
    43
#define DEFAULT_PORT 80 // default port for streaming (HTTP)
nathan@0
    44
nathan@0
    45
//#define DUMP_HEAD "/var/tmp/headers"
nathan@0
    46
nathan@0
    47
// --- cIO ---------------------------------------------------------------------
nathan@0
    48
nathan@0
    49
#if 0
nathan@0
    50
cIO::cIO(const char *Filename, bool Log)
nathan@0
    51
:cFileInfo(Filename)
nathan@0
    52
{
nathan@0
    53
  log=Log;
nathan@0
    54
  readpos=0;
nathan@0
    55
}
nathan@0
    56
nathan@0
    57
cIO::~cIO()
nathan@0
    58
{
nathan@0
    59
}
nathan@0
    60
nathan@0
    61
// --- cStreamIO ---------------------------------------------------------------
nathan@0
    62
nathan@0
    63
cStreamIO::cStreamIO(void)
nathan@0
    64
{
nathan@0
    65
  data=0;
nathan@0
    66
}
nathan@0
    67
nathan@0
    68
cStreamIO::~cStreamIO()
nathan@0
    69
{
nathan@0
    70
  StreamClear(true);
nathan@0
    71
}
nathan@0
    72
nathan@0
    73
void cStreamIO::StreamClear(bool all)
nathan@0
    74
{
nathan@0
    75
  if(all) { free(data); data=0; }
nathan@0
    76
  fill=0;
nathan@0
    77
}
nathan@0
    78
nathan@0
    79
unsigned char *cStreamIO::StreamInit(int Size)
nathan@0
    80
{
nathan@0
    81
  StreamClear(true);
nathan@0
    82
  size=Size; data=(unsigned char *)malloc(size);
nathan@0
    83
  return data;
nathan@0
    84
}
nathan@0
    85
nathan@0
    86
int cStreamIO::Stream(const unsigned char *rest)
nathan@0
    87
{
nathan@0
    88
  if(rest && fill) { // copy remaining data to start of buffer
nathan@0
    89
    fill-=(rest-data);
nathan@0
    90
    memmove(data,rest,fill);
nathan@0
    91
    }
nathan@0
    92
  else fill=0;
nathan@0
    93
nathan@0
    94
  unsigned long r=Read(data+fill,size-fill);
nathan@0
    95
  if(r>=0) { 
nathan@0
    96
    fill+=r;
nathan@0
    97
    return fill;
nathan@0
    98
    }
nathan@0
    99
  return -1;    
nathan@0
   100
}
nathan@0
   101
nathan@0
   102
// --- cFileIO -----------------------------------------------------------------
nathan@0
   103
nathan@0
   104
cFileIO::cFileIO(const char *Filename, bool Log)
nathan@0
   105
:cIO(Filename,Log)
nathan@0
   106
{
nathan@0
   107
  fd=-1;
nathan@0
   108
}
nathan@0
   109
nathan@0
   110
cFileIO::~cFileIO()
nathan@0
   111
{
nathan@0
   112
  Close();
nathan@0
   113
}
nathan@0
   114
nathan@0
   115
bool cFileIO::Open(void)
nathan@0
   116
{
nathan@0
   117
  if(fd>=0) return Seek(0,SEEK_SET);
nathan@0
   118
  if(FileInfo(log)) {
nathan@0
   119
    if((fd=open(Filename,O_RDONLY))>=0) {
nathan@0
   120
      StreamClear(false);
nathan@0
   121
      readpos=0;
nathan@0
   122
      return true;
nathan@0
   123
      }
nathan@0
   124
    else if(log) { esyslog("ERROR: open failed on %s: %s",Filename,strerror(errno)); }
nathan@0
   125
    }
nathan@0
   126
  Close();
nathan@0
   127
  return false;
nathan@0
   128
}
nathan@0
   129
nathan@0
   130
void cFileIO::Close(void)
nathan@0
   131
{
nathan@0
   132
  if(fd>=0) { close(fd); fd=-1; }
nathan@0
   133
}
nathan@0
   134
nathan@0
   135
int cFileIO::Read(unsigned char *Data, int Size)
nathan@0
   136
{
nathan@0
   137
  unsigned long r=read(fd,Data,Size);
nathan@0
   138
  if(r>=0) readpos+=r;
nathan@0
   139
  else if(log) { esyslog("ERROR: read failed in %s: %s",Filename,strerror(errno)); }
nathan@0
   140
  return r;
nathan@0
   141
}
nathan@0
   142
nathan@0
   143
bool cFileIO::Seek(unsigned long long pos, int whence)
nathan@0
   144
{
nathan@0
   145
  if(pos>=0 && pos<=Filesize) {
nathan@0
   146
    StreamClear(false);
nathan@0
   147
    if((readpos=lseek64(fd,pos,whence))>=0) {
nathan@0
   148
      if(readpos!=pos) { dsyslog("seek mismatch in %s, wanted %lld, got %lld",Filename,pos,readpos); }
nathan@0
   149
      return true;
nathan@0
   150
      }
nathan@0
   151
    else if(log) { esyslog("ERROR: seek failed in %s: %s",Filename,strerror(errno)); }
nathan@0
   152
    }
nathan@0
   153
  else d(printf("mp3: bad seek call fd=%d pos=%lld name=%s\n",fd,pos,Filename))
nathan@0
   154
  return false;
nathan@0
   155
}
nathan@0
   156
#endif
nathan@0
   157
nathan@0
   158
// --- cStream -----------------------------------------------------------------
nathan@0
   159
nathan@0
   160
cStream::cStream(const char *Filename)
nathan@0
   161
:cFileInfo(Filename)
nathan@0
   162
{
nathan@0
   163
  fd=-1; ismmap=false; buffer=0;
nathan@0
   164
}
nathan@0
   165
nathan@0
   166
cStream::~cStream()
nathan@0
   167
{
nathan@0
   168
  Close();
nathan@0
   169
}
nathan@0
   170
nathan@0
   171
bool cStream::Open(bool log)
nathan@0
   172
{
nathan@0
   173
  if(fd>=0) return Seek();
nathan@0
   174
nathan@0
   175
  if(FileInfo(log)) {
nathan@0
   176
    if((fd=open(Filename,O_RDONLY))>=0) {
nathan@0
   177
      buffpos=readpos=0; fill=0;
nathan@0
   178
nathan@0
   179
#ifdef USE_MMAP
nathan@0
   180
      if(Filesize<=MAX_MMAP_SIZE) {
nathan@0
   181
        buffer=(unsigned char *)mmap(0,Filesize,PROT_READ,MAP_SHARED,fd,0);
nathan@0
   182
        if(buffer!=MAP_FAILED) {
nathan@0
   183
          ismmap=true;
nathan@0
   184
          return true;
nathan@0
   185
          }
nathan@0
   186
        else dsyslog("mmap() failed for %s: %s",Filename,strerror(errno));
nathan@0
   187
        }
nathan@0
   188
#endif
nathan@0
   189
nathan@0
   190
        buffer = new unsigned char[MP3FILE_BUFSIZE];
nathan@0
   191
        if(buffer) return true;
nathan@0
   192
        else { esyslog("ERROR: not enough memory for buffer: %s",Filename); }
nathan@0
   193
      }
nathan@0
   194
    else if(log) { esyslog("ERROR: failed to open file %s: %s",Filename,strerror(errno)); }
nathan@0
   195
    }
nathan@0
   196
nathan@0
   197
  Close();
nathan@0
   198
  return false;
nathan@0
   199
}
nathan@0
   200
nathan@0
   201
void cStream::Close(void)
nathan@0
   202
{
nathan@0
   203
#ifdef USE_MMAP
nathan@0
   204
  if(ismmap) { 
nathan@0
   205
    munmap(buffer,Filesize); buffer=0; ismmap=false;
nathan@0
   206
    }
nathan@0
   207
  else {
nathan@0
   208
#endif
nathan@0
   209
    delete buffer; buffer=0;
nathan@0
   210
#ifdef USE_MMAP
nathan@0
   211
    }
nathan@0
   212
#endif
nathan@0
   213
  if(fd>=0) { close(fd); fd=-1; }
nathan@0
   214
}
nathan@0
   215
nathan@0
   216
bool cStream::Seek(unsigned long long pos)
nathan@0
   217
{
nathan@0
   218
  if(fd>=0 && pos>=0 && pos<=Filesize) {
nathan@0
   219
    buffpos=0; fill=0;
nathan@0
   220
    if(ismmap) {
nathan@0
   221
      readpos=pos;
nathan@0
   222
      return true;
nathan@0
   223
      }
nathan@0
   224
    else {
nathan@0
   225
      if((readpos=lseek64(fd,pos,SEEK_SET))>=0) {
nathan@0
   226
        if(readpos!=pos) { dsyslog("seek mismatch in %s, wanted %lld, got %lld",Filename,pos,readpos); }
nathan@0
   227
        return true;
nathan@0
   228
        }
nathan@0
   229
      else { esyslog("ERROR: seeking failed in %s: %d,%s",Filename,errno,strerror(errno)); }
nathan@0
   230
      }
nathan@0
   231
    }
nathan@0
   232
  else d(printf("mp3: bad seek call fd=%d pos=%lld name=%s\n",fd,pos,Filename))
nathan@0
   233
  return false;
nathan@0
   234
}
nathan@0
   235
nathan@0
   236
bool cStream::Stream(unsigned char * &data, unsigned long &len, const unsigned char *rest)
nathan@0
   237
{
nathan@0
   238
  if(fd>=0) {
nathan@0
   239
    if(readpos<Filesize) {
nathan@0
   240
      if(ismmap) {
nathan@0
   241
        if(rest && fill) readpos=(rest-buffer);   // take care of remaining data
nathan@0
   242
        fill=Filesize-readpos;
nathan@0
   243
        data=buffer+readpos; len=fill;
nathan@0
   244
        buffpos=readpos; readpos+=fill;
nathan@0
   245
        return true;
nathan@0
   246
        }
nathan@0
   247
      else {
nathan@0
   248
        if(rest && fill) {       // copy remaining data to start of buffer
nathan@0
   249
          fill-=(rest-buffer);   // remaing bytes
nathan@0
   250
          memmove(buffer,rest,fill);
nathan@0
   251
          }
nathan@0
   252
        else fill=0;
nathan@0
   253
nathan@0
   254
        int r;
nathan@0
   255
        do { 
nathan@0
   256
          r=read(fd,buffer+fill,MP3FILE_BUFSIZE-fill);
nathan@0
   257
          } while(r==-1 && errno==EINTR);
nathan@0
   258
nathan@0
   259
        if(r>=0) {
nathan@0
   260
          buffpos=readpos-fill; readpos+=r; fill+=r;
nathan@0
   261
          data=buffer; len=fill;
nathan@0
   262
          return true;
nathan@0
   263
          }
nathan@0
   264
        else { esyslog("ERROR: read failed in %s: %d,%s",Filename,errno,strerror(errno)); }
nathan@0
   265
        }
nathan@0
   266
      }
nathan@0
   267
    else {
nathan@0
   268
      len=0;
nathan@0
   269
      return true;
nathan@0
   270
      }
nathan@0
   271
    }
nathan@0
   272
  return false;
nathan@0
   273
}
nathan@0
   274
nathan@0
   275
// -----------------------------------------------------------------------------
nathan@0
   276
nathan@0
   277
#ifdef DUMP_HEAD
nathan@0
   278
void Dump(const char *name, char *buffer)
nathan@0
   279
{
nathan@0
   280
  FILE *f=fopen(name,"a");
nathan@0
   281
  if(f) {
nathan@0
   282
    fprintf(f,"<<<< %s\n",buffer);
nathan@0
   283
/*
nathan@0
   284
    int n=strlen(buffer);
nathan@0
   285
    for(int i=0 ; i<n ; i+=8) {
nathan@0
   286
      fprintf(f,"%04x: ",i);
nathan@0
   287
      for(int l=0 ; l<8 && i+l<n ; l++) fprintf(f,"%02x ",buffer[i+l]);
nathan@0
   288
      fprintf(f,"\n");
nathan@0
   289
      }
nathan@0
   290
*/
nathan@0
   291
    fclose(f);
nathan@0
   292
    }
nathan@0
   293
}
nathan@0
   294
#endif
nathan@0
   295
nathan@0
   296
// --- cNetStream -----------------------------------------------------------------
nathan@0
   297
nathan@0
   298
cNetStream::cNetStream(const char *Filename)
nathan@0
   299
:cStream(Filename)
nathan@0
   300
{
nathan@0
   301
  net=0; host=path=auth=0; cc=0;
nathan@0
   302
  icyName=icyUrl=icyTitle=0; icyChanged=false;
nathan@0
   303
  metaInt=0;
nathan@0
   304
  InfoDone();
nathan@0
   305
}
nathan@0
   306
nathan@0
   307
cNetStream::~cNetStream()
nathan@0
   308
{
nathan@0
   309
  free(host); free(path); free(auth);
nathan@0
   310
  free(icyName); free(icyUrl); free(icyTitle);
nathan@0
   311
}
nathan@0
   312
nathan@0
   313
bool cNetStream::ParseURL(const char *line, bool log)
nathan@0
   314
{
nathan@0
   315
  char pr[32], h[512], p[512], a[512];
nathan@0
   316
  int r=sscanf(line," %31[^:]://%511[^/]%511[^\r\n]",pr,h,p);
nathan@0
   317
  if(r==2) {
nathan@0
   318
    d(printf("netstream: adding default path '/'\n"))
nathan@0
   319
    strcpy(p,"/");
nathan@0
   320
    r++;
nathan@0
   321
    }
nathan@0
   322
  if(r==3) {
nathan@0
   323
    a[0]=0;
nathan@0
   324
    char *s=index(h,'@');
nathan@0
   325
    if(s) {
nathan@0
   326
      *s=0;
nathan@0
   327
      strcpy(a,h);
nathan@0
   328
      strcpy(h,s+1);
nathan@0
   329
      }
nathan@0
   330
    d(printf("netstream: parsed proto='%s' host='%s' path='%s' auth='%s'\n",pr,h,p,a))
nathan@0
   331
    if(!strcasecmp(pr,"http")) {
nathan@0
   332
      int pp=DEFAULT_PORT;
nathan@0
   333
      s=rindex(h,':');
nathan@0
   334
      if(s) { *s++=0; pp=atoi(s); }
nathan@0
   335
nathan@0
   336
      free(host); host=strdup(h);
nathan@0
   337
      free(path); path=strdup(p);
nathan@0
   338
      free(auth); auth=a[0] ? strdup(a) : 0;
nathan@0
   339
      port=pp;
nathan@0
   340
      return true;
nathan@0
   341
      }
nathan@0
   342
    else if(log) esyslog("Unsupported protocol %s in: %s",pr,line);
nathan@0
   343
    }
nathan@0
   344
  else if(log) esyslog("Bad URL line: %s",line);
nathan@0
   345
  return false;    
nathan@0
   346
}
nathan@0
   347
nathan@0
   348
bool cNetStream::ParseURLFile(const char *name, bool log)
nathan@0
   349
{
nathan@0
   350
  bool res=false;
nathan@0
   351
  FILE *f=fopen(name,"r");
nathan@0
   352
  if(f) {
nathan@0
   353
    char line[2048];
nathan@0
   354
    if(fgets(line,sizeof(line),f)) {
nathan@0
   355
      res=ParseURL(line,log);
nathan@0
   356
      }
nathan@0
   357
    else if(log) esyslog("Nothing to read from URL file %s. File empty?",name);
nathan@0
   358
    fclose(f);
nathan@0
   359
    }
nathan@0
   360
  else if(log) esyslog("fopen() failed on URL file %s: %s",name,strerror(errno));
nathan@0
   361
  return res;
nathan@0
   362
}
nathan@0
   363
nathan@0
   364
bool cNetStream::SendRequest(void)
nathan@0
   365
{
nathan@0
   366
  bool res=false;
nathan@0
   367
  char buff[2048];
nathan@0
   368
nathan@0
   369
  char *h, *p;
nathan@0
   370
  asprintf(&h,port!=DEFAULT_PORT ? "%s:%d":"%s",host,port);
nathan@0
   371
  if(MP3Setup.UseProxy) asprintf(&p,"http://%s%s",h,path);
nathan@0
   372
  else asprintf(&p,"%s",path);
nathan@0
   373
nathan@0
   374
  char a[1024];
nathan@0
   375
  a[0]=0;
nathan@0
   376
  if(auth) {
nathan@0
   377
    cBase64Encoder b64((uchar *)auth,strlen(auth),76);
nathan@0
   378
    int q=0;
nathan@0
   379
    const char *l;
nathan@0
   380
    while((l=b64.NextLine())) {
nathan@0
   381
      q+=snprintf(&a[q],sizeof(a)-q,"%s%s\r\n",q==0?"Authorization: Basic ":" ",l);
nathan@0
   382
      }
nathan@0
   383
    }
nathan@0
   384
nathan@0
   385
  snprintf(buff,sizeof(buff),
nathan@0
   386
           "GET %s HTTP/1.0\r\n"
nathan@0
   387
           "User-Agent: %s/%s\r\n"
nathan@0
   388
           "Host: %s\r\n"
nathan@0
   389
           "Accept: audio/mpeg\r\n"   //XXX audio/x-mpegurl, */*
nathan@0
   390
           "Icy-MetaData: 1\r\n"
nathan@0
   391
           "%s\r\n",
nathan@0
   392
           p,PLUGIN_NAME,PLUGIN_VERSION,h,a);
nathan@0
   393
  free(p); free(h);  
nathan@0
   394
nathan@0
   395
  if(++cc==1) asyncStatus.Set(tr("Connecting to stream server ..."));
nathan@0
   396
nathan@0
   397
  if(net->Connect(MP3Setup.UseProxy ? MP3Setup.ProxyHost:host , MP3Setup.UseProxy ? MP3Setup.ProxyPort:port)) {
nathan@0
   398
    d(printf("netstream: -> %s",buff))
nathan@0
   399
    if(net->Puts(buff)>0) res=GetHTTPResponse();
nathan@0
   400
    }
nathan@0
   401
nathan@0
   402
  if(cc--==1) asyncStatus.Set(0);
nathan@0
   403
  return res;
nathan@0
   404
}
nathan@0
   405
nathan@0
   406
bool cNetStream::ParseHeader(const char *buff, const char *name, char **value)
nathan@0
   407
{
nathan@0
   408
  char *s=index(buff,':');
nathan@0
   409
  if(s && !strncasecmp(buff,name,s-buff)) {
nathan@0
   410
    s=skipspace(s+1);
nathan@0
   411
    d(printf("netstream: found header '%s' contents '%s'\n",name,s))
nathan@0
   412
    free(*value); *value=strdup(s);
nathan@0
   413
    return true;
nathan@0
   414
    }
nathan@0
   415
  return false;
nathan@0
   416
}
nathan@0
   417
nathan@0
   418
bool cNetStream::GetHTTPResponse(void)
nathan@0
   419
{
nathan@0
   420
  bool res=false;
nathan@0
   421
  char buff[1024], text[128], *newurl=0;
nathan@0
   422
  int code=-1, hcount=0;
nathan@0
   423
  while(net->Gets(buff,sizeof(buff))>0) {
nathan@0
   424
    stripspace(buff);
nathan@0
   425
#ifdef DUMP_HEAD
nathan@0
   426
    Dump(DUMP_HEAD,buff);
nathan@0
   427
#endif
nathan@0
   428
    d(printf("netstream: <- %s\n",buff))
nathan@0
   429
    hcount++;
nathan@0
   430
    if(hcount==1) {   // parse status line
nathan@0
   431
      if(sscanf(buff,"%*[^ ] %d %128s",&code,text)!=2) {
nathan@0
   432
        esyslog("Bad HTTP response '%s' from %s:%d",buff,host,port);
nathan@0
   433
        goto out;
nathan@0
   434
        }
nathan@0
   435
      }
nathan@0
   436
    else {            // parse header lines
nathan@0
   437
      if(buff[0]==0) { // headers finish if we receive a empty line
nathan@0
   438
        switch(code) {
nathan@0
   439
          case 200: // OK
nathan@0
   440
             res=true;
nathan@0
   441
             goto out;
nathan@0
   442
          case 300: // MULTIPLE_CHOICES
nathan@0
   443
          case 301: // MOVED_PERMANENTLY
nathan@0
   444
          case 302: // MOVED_TEMPORARILY
nathan@0
   445
             if(newurl) {
nathan@0
   446
               if(ParseURL(newurl,true)) res=SendRequest();
nathan@0
   447
               }
nathan@0
   448
             else esyslog("No location header for redirection from %s:%d",host,port);
nathan@0
   449
             goto out;
nathan@0
   450
          default:
nathan@0
   451
             esyslog("Unhandled HTTP response '%d %s' from %s:%d",code,text,host,port);
nathan@0
   452
             goto out;
nathan@0
   453
          }
nathan@0
   454
        }
nathan@0
   455
nathan@0
   456
      ParseHeader(buff,"Location",&newurl);
nathan@0
   457
      ParseHeader(buff,"icy-name",&icyName);
nathan@0
   458
      ParseHeader(buff,"icy-url",&icyUrl);
nathan@0
   459
      char *meta=0;
nathan@0
   460
      if(ParseHeader(buff,"icy-metaint",&meta)) {
nathan@0
   461
        metaInt=metaCnt=atol(meta);
nathan@0
   462
        d(printf("netstream: meta interval set to %d\n",metaInt));
nathan@0
   463
        }
nathan@0
   464
      free(meta);
nathan@0
   465
      }
nathan@0
   466
    }
nathan@0
   467
out:
nathan@0
   468
  free(newurl);
nathan@0
   469
  return res;
nathan@0
   470
}
nathan@0
   471
nathan@0
   472
bool cNetStream::Open(bool log)
nathan@0
   473
{
nathan@0
   474
  if(net && net->Connected()) return true;
nathan@0
   475
nathan@0
   476
  if(!net) net=new cNet(0,0,0);
nathan@0
   477
  net->Disconnect();
nathan@0
   478
  
nathan@0
   479
  if(ParseURLFile(Filename,log)) {
nathan@0
   480
    buffpos=readpos=0; fill=0;
nathan@0
   481
    buffer = new unsigned char[MP3FILE_BUFSIZE];
nathan@0
   482
    if(buffer) {
nathan@0
   483
      if(SendRequest()) {
nathan@0
   484
        return true;
nathan@0
   485
        }
nathan@0
   486
      }
nathan@0
   487
    else esyslog("Not enough memory for buffer");
nathan@0
   488
    }
nathan@0
   489
nathan@0
   490
  Close();
nathan@0
   491
  return false;
nathan@0
   492
}
nathan@0
   493
nathan@0
   494
void cNetStream::Close(void)
nathan@0
   495
{
nathan@0
   496
  delete buffer; buffer=0;
nathan@0
   497
  delete net; net=0;
nathan@0
   498
}
nathan@0
   499
nathan@0
   500
bool cNetStream::Seek(unsigned long long pos)
nathan@0
   501
{
nathan@0
   502
  return false;
nathan@0
   503
}
nathan@0
   504
nathan@0
   505
bool cNetStream::Stream(unsigned char * &data, unsigned long &len, const unsigned char *rest)
nathan@0
   506
{
nathan@0
   507
  if(net && net->Connected()) {
nathan@0
   508
    if(rest && fill) {       // copy remaining data to start of buffer
nathan@0
   509
      fill-=(rest-buffer);   // remaing bytes
nathan@0
   510
      memmove(buffer,rest,fill);
nathan@0
   511
      }
nathan@0
   512
    else fill=0;
nathan@0
   513
nathan@0
   514
    int r=MP3FILE_BUFSIZE-fill;
nathan@0
   515
    if(metaInt && r>metaCnt) r=metaCnt;
nathan@0
   516
    r=net->Read(buffer+fill,r);
nathan@0
   517
    if(r>=0) {
nathan@0
   518
      fill+=r; data=buffer; len=fill;
nathan@0
   519
      metaCnt-=r;
nathan@0
   520
      if(metaInt && metaCnt<=0) {
nathan@0
   521
        ParseMetaData();
nathan@0
   522
        metaCnt=metaInt;
nathan@0
   523
        }
nathan@0
   524
      return true;
nathan@0
   525
      }
nathan@0
   526
    }
nathan@0
   527
  return false;
nathan@0
   528
}
nathan@0
   529
nathan@0
   530
char *cNetStream::ParseMetaString(const char *buff, const char *name, char **value)
nathan@0
   531
{
nathan@0
   532
  char *s=index(buff,'=');
nathan@0
   533
  if(s && !strncasecmp(buff,name,s-buff)) {
nathan@0
   534
    char *end=index(s+2,'\'');
nathan@0
   535
    if(s[1]=='\'' && end) {
nathan@0
   536
      *end=0;
nathan@0
   537
      s=stripspace(skipspace(s+2));
nathan@0
   538
      if(strlen(s)>0) {
nathan@0
   539
        d(printf("netstream: found metadata '%s' contents '%s'\n",name,s))
nathan@0
   540
        free(*value); *value=strdup(s);
nathan@0
   541
        }
nathan@0
   542
      //else d(printf("netstream: found empty metadata '%s'\n",name))
nathan@0
   543
      return end+1;
nathan@0
   544
      }
nathan@0
   545
    else d(printf("netstream: bad metadata format\n"))
nathan@0
   546
    }
nathan@0
   547
  return 0;
nathan@0
   548
}
nathan@0
   549
nathan@0
   550
bool cNetStream::ParseMetaData(void)
nathan@0
   551
{
nathan@0
   552
  unsigned char byte;
nathan@0
   553
  int r=net->Read(&byte,1);
nathan@0
   554
  if(r<=0) return false;
nathan@0
   555
  int metalen=byte*16;
nathan@0
   556
  if(metalen>0) {
nathan@0
   557
    char data[metalen+1];
nathan@0
   558
    data[metalen]=0;
nathan@0
   559
    int cnt=0;
nathan@0
   560
    do {
nathan@0
   561
      r=net->Read((unsigned char *)data+cnt,metalen-cnt);
nathan@0
   562
      if(r<=0) return false;
nathan@0
   563
      cnt+=r;
nathan@0
   564
      } while(cnt<metalen);
nathan@0
   565
nathan@0
   566
#ifdef DUMP_HEAD
nathan@0
   567
    Dump(DUMP_HEAD,data);
nathan@0
   568
#endif
nathan@0
   569
nathan@0
   570
    char *p=data;
nathan@0
   571
    while(*p && p-data<metalen) {
nathan@0
   572
      char *n;
nathan@0
   573
      if((n=ParseMetaString(p,"StreamTitle",&icyTitle)) ||
nathan@0
   574
         (n=ParseMetaString(p,"StreamUrl",&icyUrl))) {
nathan@0
   575
        p=n;
nathan@0
   576
        icyChanged=true;
nathan@0
   577
        }
nathan@0
   578
      else  p++;
nathan@0
   579
      }
nathan@0
   580
    }
nathan@0
   581
  return true;
nathan@0
   582
}
nathan@0
   583
nathan@0
   584
bool cNetStream::IcyChanged(void)
nathan@0
   585
{
nathan@0
   586
  bool c=icyChanged;
nathan@0
   587
  icyChanged=false;
nathan@0
   588
  return c;
nathan@0
   589
}