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