decoder-mp3.c
author nathan
Sat, 29 Dec 2007 14:47:40 +0100
branchtrunk
changeset 0 474a1293c3c0
child 25 887faebaba0a
permissions -rw-r--r--
release 0.10.0
nathan@0
     1
/*
nathan@0
     2
 * MP3/MPlayer plugin to VDR (C++)
nathan@0
     3
 *
nathan@0
     4
 * (C) 2001-2005 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
nathan@0
    25
#include "common.h"
nathan@0
    26
#include "data-mp3.h"
nathan@0
    27
#include "decoder-mp3.h"
nathan@0
    28
#include "stream.h"
nathan@0
    29
nathan@0
    30
#define MAX_FRAME_ERR 10
nathan@0
    31
nathan@0
    32
// ----------------------------------------------------------------
nathan@0
    33
nathan@0
    34
int MadStream(struct mad_stream *stream, cStream *str)
nathan@0
    35
{
nathan@0
    36
  unsigned char *data;
nathan@0
    37
  unsigned long len;
nathan@0
    38
  if(str->Stream(data,len,stream->next_frame)) {
nathan@0
    39
    if(len>0) mad_stream_buffer(stream, data, len);
nathan@0
    40
    return len;
nathan@0
    41
    }
nathan@0
    42
  return -1;
nathan@0
    43
}
nathan@0
    44
nathan@0
    45
// --- cMP3Decoder -------------------------------------------------------------
nathan@0
    46
nathan@0
    47
cMP3Decoder::cMP3Decoder(const char *Filename, bool preinit)
nathan@0
    48
:cDecoder(Filename)
nathan@0
    49
{
nathan@0
    50
  str=0; scan=0; isStream=false;
nathan@0
    51
  if(preinit) {
nathan@0
    52
    //d(printf("mp3: preinit\n"))
nathan@0
    53
    str=new cStream(filename);
nathan@0
    54
    scan=new cScanID3(str,&urgentLock);
nathan@0
    55
    }
nathan@0
    56
  fi=0; stream=0; frame=0; synth=0;
nathan@0
    57
}
nathan@0
    58
nathan@0
    59
cMP3Decoder::~cMP3Decoder()
nathan@0
    60
{
nathan@0
    61
  Clean();
nathan@0
    62
  delete scan;
nathan@0
    63
  delete str;
nathan@0
    64
}
nathan@0
    65
nathan@0
    66
bool cMP3Decoder::Valid(void)
nathan@0
    67
{
nathan@0
    68
  bool res=false;
nathan@0
    69
  if(TryLock()) {
nathan@0
    70
    struct mad_stream stream;
nathan@0
    71
    struct mad_header header;
nathan@0
    72
    mad_stream_init(&stream);
nathan@0
    73
    mad_stream_options(&stream,MAD_OPTION_IGNORECRC);
nathan@0
    74
    mad_header_init(&header);
nathan@0
    75
    if(str->Open() && str->Seek()) {
nathan@0
    76
      int count=10;
nathan@0
    77
      do {
nathan@0
    78
        if(mad_header_decode(&header,&stream)<0) {
nathan@0
    79
          if(stream.error==MAD_ERROR_BUFLEN || stream.error==MAD_ERROR_BUFPTR) {
nathan@0
    80
            if(MadStream(&stream,str)<=0) break;
nathan@0
    81
            }
nathan@0
    82
          else if(!MAD_RECOVERABLE(stream.error)) break;
nathan@0
    83
          count++;
nathan@0
    84
          }
nathan@0
    85
        } while(--count);
nathan@0
    86
      if(!count) res=true;
nathan@0
    87
      }
nathan@0
    88
    mad_header_finish(&header);
nathan@0
    89
    mad_stream_finish(&stream);
nathan@0
    90
    str->Close();
nathan@0
    91
    Unlock();
nathan@0
    92
    }
nathan@0
    93
  return res;
nathan@0
    94
}
nathan@0
    95
nathan@0
    96
cFileInfo *cMP3Decoder::FileInfo(void)
nathan@0
    97
{
nathan@0
    98
  cFileInfo *fi=0;
nathan@0
    99
  if(str->HasInfo()) fi=str;
nathan@0
   100
  else if(TryLock()){
nathan@0
   101
    if(str->Open()) { fi=str; str->Close(); }
nathan@0
   102
    Unlock();
nathan@0
   103
    }
nathan@0
   104
  return fi;
nathan@0
   105
}
nathan@0
   106
nathan@0
   107
cSongInfo *cMP3Decoder::SongInfo(bool get)
nathan@0
   108
{
nathan@0
   109
  cSongInfo *si=0;
nathan@0
   110
  if(scan->HasInfo()) si=scan;
nathan@0
   111
  else if(get && TryLock()) {
nathan@0
   112
    if(scan->DoScan()) si=scan;
nathan@0
   113
    Unlock();
nathan@0
   114
    }
nathan@0
   115
  return si;
nathan@0
   116
}
nathan@0
   117
nathan@0
   118
cPlayInfo *cMP3Decoder::PlayInfo(void)
nathan@0
   119
{
nathan@0
   120
  if(playing) {
nathan@0
   121
    pi.Index=mad_timer_count(playtime,MAD_UNITS_SECONDS);
nathan@0
   122
    pi.Total=scan->Total;
nathan@0
   123
    return &pi;
nathan@0
   124
    }
nathan@0
   125
  return 0;
nathan@0
   126
}
nathan@0
   127
nathan@0
   128
void cMP3Decoder::Init(void)
nathan@0
   129
{
nathan@0
   130
  Clean();
nathan@0
   131
  stream=new struct mad_stream;
nathan@0
   132
  mad_stream_init(stream);
nathan@0
   133
  mad_stream_options(stream,MAD_OPTION_IGNORECRC);
nathan@0
   134
  frame=new struct mad_frame;
nathan@0
   135
  mad_frame_init(frame);
nathan@0
   136
  synth=new struct mad_synth;
nathan@0
   137
  mad_synth_init(synth);
nathan@0
   138
  playtime=mad_timer_zero; skiptime=mad_timer_zero;
nathan@0
   139
  framenum=framemax=0; mute=errcount=0;
nathan@0
   140
}
nathan@0
   141
nathan@0
   142
void cMP3Decoder::Clean(void)
nathan@0
   143
{
nathan@0
   144
  playing=false;
nathan@0
   145
  if(synth) { mad_synth_finish(synth); delete synth; synth=0; }
nathan@0
   146
  if(frame) { mad_frame_finish(frame); delete frame; frame=0; }
nathan@0
   147
  if(stream) { mad_stream_finish(stream); delete stream; stream=0; }
nathan@0
   148
  delete[] fi; fi=0;
nathan@0
   149
}
nathan@0
   150
nathan@0
   151
bool cMP3Decoder::Start(void)
nathan@0
   152
{
nathan@0
   153
  Lock(true);
nathan@0
   154
  Init(); playing=true;
nathan@0
   155
  if(str->Open() && scan->DoScan(true)) {
nathan@0
   156
    if(!isStream) {
nathan@0
   157
      str->Seek();
nathan@0
   158
      framemax=scan->Frames+20;
nathan@0
   159
      fi=new struct FrameInfo[framemax];
nathan@0
   160
      if(!fi) esyslog("ERROR: no memory for frame index, rewinding disabled");
nathan@0
   161
      }
nathan@0
   162
    Unlock();
nathan@0
   163
    return true;
nathan@0
   164
    }
nathan@0
   165
  str->Close();
nathan@0
   166
  Clean();
nathan@0
   167
  Unlock();
nathan@0
   168
  return false;
nathan@0
   169
}
nathan@0
   170
nathan@0
   171
bool cMP3Decoder::Stop(void)
nathan@0
   172
{
nathan@0
   173
  Lock();
nathan@0
   174
  if(playing) {
nathan@0
   175
    str->Close();
nathan@0
   176
    Clean();
nathan@0
   177
    }
nathan@0
   178
  Unlock();
nathan@0
   179
  return true;
nathan@0
   180
}
nathan@0
   181
nathan@0
   182
struct Decode *cMP3Decoder::Done(eDecodeStatus status)
nathan@0
   183
{
nathan@0
   184
  ds.status=status;
nathan@0
   185
  ds.index=mad_timer_count(playtime,MAD_UNITS_MILLISECONDS);
nathan@0
   186
  ds.pcm=&synth->pcm;
nathan@0
   187
  Unlock(); // release the lock from Decode()
nathan@0
   188
  return &ds;
nathan@0
   189
}
nathan@0
   190
nathan@0
   191
eDecodeStatus cMP3Decoder::DecodeError(bool hdr)
nathan@0
   192
{
nathan@0
   193
  if(stream->error==MAD_ERROR_BUFLEN || stream->error==MAD_ERROR_BUFPTR) {
nathan@0
   194
    int s=MadStream(stream,str);
nathan@0
   195
    if(s<0) return dsError;
nathan@0
   196
    if(s==0) return dsEof;
nathan@0
   197
    }
nathan@0
   198
  else if(!MAD_RECOVERABLE(stream->error)) {
nathan@0
   199
    d(printf("mad: decode %sfailed, frame=%d: %s\n",hdr?"hdr ":"",framenum,mad_stream_errorstr(stream)))
nathan@0
   200
    return dsError;
nathan@0
   201
    }
nathan@0
   202
  else { 
nathan@0
   203
    if(stream->error==MAD_ERROR_LOSTSYNC) { // check for ID3 tags
nathan@0
   204
#ifdef DEBUG
nathan@0
   205
      char buf[10];
nathan@0
   206
      int buf2[3];
nathan@0
   207
      memcpy(buf,stream->this_frame,8); buf[8]=0;
nathan@0
   208
      memcpy(buf2,stream->this_frame,8);
nathan@0
   209
      printf("mad: lost sync %08x %08x %s\n",buf2[0],buf2[1],buf);
nathan@0
   210
#endif
nathan@0
   211
      id3_length_t count=stream->bufend-stream->this_frame;
nathan@0
   212
      id3_length_t tagsize=id3_tag_query(stream->this_frame,count);
nathan@0
   213
      if(tagsize>0) {
nathan@0
   214
        d(printf("mad: skipping over ID3 tag\n"))
nathan@0
   215
        if(count>tagsize) count=tagsize;
nathan@0
   216
        mad_stream_skip(stream,count);
nathan@0
   217
        while(count<tagsize) {
nathan@0
   218
          unsigned char *sdata;
nathan@0
   219
          unsigned long slen;
nathan@0
   220
          if(!str->Stream(sdata,slen)) return dsError;
nathan@0
   221
          if(slen<=0) return dsEof;
nathan@0
   222
          unsigned long len=min(tagsize-count,slen);
nathan@0
   223
          count+=len;
nathan@0
   224
          sdata+=len; slen-=len;
nathan@0
   225
          if(slen>0) mad_stream_buffer(stream,sdata,slen);
nathan@0
   226
          }
nathan@0
   227
        return dsOK;
nathan@0
   228
        }
nathan@0
   229
      }
nathan@0
   230
    errcount+=hdr?1:100;
nathan@0
   231
    d(printf("mad: decode %serror, frame=%d count=%d: %s\n",hdr?"hdr ":"",framenum,errcount,mad_stream_errorstr(stream)))
nathan@0
   232
    }
nathan@0
   233
  return dsOK;
nathan@0
   234
}
nathan@0
   235
nathan@0
   236
struct Decode *cMP3Decoder::Decode(void)
nathan@0
   237
{
nathan@0
   238
  Lock(); // this is released in Done()
nathan@0
   239
  eDecodeStatus r;
nathan@0
   240
  while(playing) {
nathan@0
   241
    if(errcount>=MAX_FRAME_ERR*100) {
nathan@0
   242
      esyslog("ERROR: excessive decoding errors, aborting file %s",filename);
nathan@0
   243
      return Done(dsError);
nathan@0
   244
      }
nathan@0
   245
nathan@0
   246
    if(mad_header_decode(&frame->header,stream)<0) {
nathan@0
   247
      if((r=DecodeError(true))) return Done(r);
nathan@0
   248
      }
nathan@0
   249
    else {
nathan@0
   250
      if(!isStream) {
nathan@0
   251
#ifdef DEBUG
nathan@0
   252
        if(framenum>=framemax) printf("mp3: framenum >= framemax!!!!\n");
nathan@0
   253
#endif
nathan@0
   254
        if(fi && framenum<framemax) {
nathan@0
   255
          fi[framenum].Pos=str->BufferPos() + (stream->this_frame-stream->buffer);
nathan@0
   256
          fi[framenum].Time=playtime;
nathan@0
   257
          }
nathan@0
   258
        }
nathan@0
   259
nathan@0
   260
      mad_timer_add(&playtime,frame->header.duration); framenum++;
nathan@0
   261
nathan@0
   262
      if(mad_timer_compare(playtime,skiptime)>=0) skiptime=mad_timer_zero;
nathan@0
   263
      else return Done(dsSkip);  // skipping, decode next header
nathan@0
   264
nathan@0
   265
      if(mad_frame_decode(frame,stream)<0) {
nathan@0
   266
        if((r=DecodeError(false))) return Done(r);
nathan@0
   267
        }
nathan@0
   268
      else {
nathan@0
   269
        errcount=0;
nathan@0
   270
        scan->InfoHook(&frame->header);
nathan@0
   271
        mad_synth_frame(synth,frame);
nathan@0
   272
        if(mute) { mute--; return Done(dsSkip); }
nathan@0
   273
        return Done(dsPlay);
nathan@0
   274
        }
nathan@0
   275
      }
nathan@0
   276
    }
nathan@0
   277
  return Done(dsError);
nathan@0
   278
}
nathan@0
   279
nathan@0
   280
void cMP3Decoder::MakeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime, int secs, float bsecs)
nathan@0
   281
{
nathan@0
   282
  mad_timer_t time;
nathan@0
   283
  *skiptime=playtime;
nathan@0
   284
  mad_timer_set(&time,abs(secs),0,0);
nathan@0
   285
  if(secs<0) mad_timer_negate(&time);
nathan@0
   286
  mad_timer_add(skiptime,time);
nathan@0
   287
  int full=(int)bsecs; bsecs-=(float)full;
nathan@0
   288
  mad_timer_set(&time,full,(int)(bsecs*1000.0),1000);
nathan@0
   289
  mad_timer_negate(&time);
nathan@0
   290
  mad_timer_add(skiptime,time);
nathan@0
   291
  d(printf("mp3: skip: playtime=%ld secs=%d full=%d bsecs=%f skiptime=%ld\n",
nathan@0
   292
           mad_timer_count(playtime,MAD_UNITS_MILLISECONDS),secs,full,bsecs,mad_timer_count(*skiptime,MAD_UNITS_MILLISECONDS)))
nathan@0
   293
}
nathan@0
   294
nathan@0
   295
bool cMP3Decoder::Skip(int Seconds, float bsecs)
nathan@0
   296
{
nathan@0
   297
  Lock();
nathan@0
   298
  bool res=false;
nathan@0
   299
  if(playing && !isStream) {
nathan@0
   300
    if(!mad_timer_compare(skiptime,mad_timer_zero)) { // allow only one skip at any time
nathan@0
   301
      mad_timer_t time;
nathan@0
   302
      MakeSkipTime(&time,playtime,Seconds,bsecs);
nathan@0
   303
nathan@0
   304
      if(mad_timer_compare(playtime,time)<=0) { // forward skip
nathan@0
   305
#ifdef DEBUG
nathan@0
   306
        int i=mad_timer_count(time,MAD_UNITS_SECONDS);
nathan@0
   307
        printf("mp3: forward skipping to %02d:%02d\n",i/60,i%60);
nathan@0
   308
#endif
nathan@0
   309
        skiptime=time; mute=1;
nathan@0
   310
        res=true;
nathan@0
   311
        }
nathan@0
   312
      else {                                    // backward skip
nathan@0
   313
        if(fi) {
nathan@0
   314
#ifdef DEBUG
nathan@0
   315
          int i=mad_timer_count(time,MAD_UNITS_SECONDS);
nathan@0
   316
          printf("mp3: rewinding to %02d:%02d\n",i/60,i%60);
nathan@0
   317
#endif
nathan@0
   318
          while(framenum && mad_timer_compare(time,fi[--framenum].Time)<0) ;
nathan@0
   319
          mute=2; if(framenum>=2) framenum-=2;
nathan@0
   320
          playtime=fi[framenum].Time;
nathan@0
   321
          str->Seek(fi[framenum].Pos);
nathan@0
   322
          mad_stream_finish(stream); // reset stream buffer
nathan@0
   323
          mad_stream_init(stream);
nathan@0
   324
#ifdef DEBUG
nathan@0
   325
          i=mad_timer_count(playtime,MAD_UNITS_MILLISECONDS);
nathan@0
   326
          printf("mp3: new playtime=%d framenum=%d filepos=%lld\n",i,framenum,fi[framenum].Pos);
nathan@0
   327
#endif
nathan@0
   328
          res=true;
nathan@0
   329
          }
nathan@0
   330
        }
nathan@0
   331
      }
nathan@0
   332
    }
nathan@0
   333
  Unlock();
nathan@0
   334
  return res;
nathan@0
   335
}
nathan@0
   336
nathan@0
   337
// --- cScanID3 ----------------------------------------------------------------
nathan@0
   338
nathan@0
   339
// This function was adapted from mad_timer, from the 
nathan@0
   340
// libmad distribution
nathan@0
   341
nathan@0
   342
#define MIN_SCAN_FRAMES 200 // min. number of frames to scan
nathan@0
   343
nathan@0
   344
cScanID3::cScanID3(cStream *Str, bool *Urgent)
nathan@0
   345
{
nathan@0
   346
  str=Str;
nathan@0
   347
  urgent=Urgent;
nathan@0
   348
}
nathan@0
   349
nathan@0
   350
bool cScanID3::Abort(bool result)
nathan@0
   351
{
nathan@0
   352
  if(!keepOpen) str->Close();
nathan@0
   353
  return result;
nathan@0
   354
}
nathan@0
   355
nathan@0
   356
bool cScanID3::DoScan(bool KeepOpen)
nathan@0
   357
{
nathan@0
   358
  mad_timer_t duration=mad_timer_zero;
nathan@0
   359
  unsigned int bitrate=0, minrate=~0, maxrate=0;
nathan@0
   360
  int xframes=0;
nathan@0
   361
  unsigned int id3_vers=0;
nathan@0
   362
  bool is_vbr=false, has_id3=false;
nathan@0
   363
nathan@0
   364
  keepOpen=KeepOpen;
nathan@0
   365
  if(!str->Open()) return Abort(false);
nathan@0
   366
  if(HasInfo()) return Abort(true);
nathan@0
   367
nathan@0
   368
  // check the infocache
nathan@0
   369
  cCacheData *dat=InfoCache.Search(str);
nathan@0
   370
  if(dat) {
nathan@0
   371
    Set(dat); dat->Unlock();
nathan@0
   372
    if(!DecoderID) {
nathan@0
   373
      DecoderID=DEC_MP3;
nathan@0
   374
      InfoCache.Cache(this,str);
nathan@0
   375
      }
nathan@0
   376
    return Abort(true);
nathan@0
   377
    }
nathan@0
   378
nathan@0
   379
  Clear();
nathan@0
   380
nathan@0
   381
  // do a initial check for a ID3v1 tag at the end of the file
nathan@0
   382
  // to speed up the following scan
nathan@0
   383
  if(str->Filesize>=128 && str->Seek(str->Filesize-128)) {
nathan@0
   384
    unsigned char *data;
nathan@0
   385
    unsigned long len;
nathan@0
   386
    if(str->Stream(data,len)) {
nathan@0
   387
      struct id3_tag *tag=id3_tag_parse(data,len);
nathan@0
   388
      if(tag) {
nathan@0
   389
        d(printf("id3-scan: initialy found ID3 V1 tag at EOF\n"))
nathan@0
   390
        ParseID3(tag);
nathan@0
   391
        has_id3=true; id3_vers=tag->version;
nathan@0
   392
        id3_tag_delete(tag);
nathan@0
   393
        }
nathan@0
   394
      }
nathan@0
   395
    }
nathan@0
   396
  if(!str->Seek()) return Abort(false);
nathan@0
   397
nathan@0
   398
  // There are three ways of calculating the length of an mp3:
nathan@0
   399
  // 1) Constant bitrate: One frame can provide the information
nathan@0
   400
  //    needed: # of frames and duration. Just see how long it
nathan@0
   401
  //    is and do the division.
nathan@0
   402
  // 2) Variable bitrate: Xing tag. It provides the number of 
nathan@0
   403
  //    frames. Each frame has the same number of samples, so
nathan@0
   404
  //    just use that.
nathan@0
   405
  // 3) All: Count up the frames and duration of each frames
nathan@0
   406
  //    by decoding each one. We do this if we've no other
nathan@0
   407
  //    choice, i.e. if it's a VBR file with no Xing tag.
nathan@0
   408
nathan@0
   409
  struct mad_stream stream;
nathan@0
   410
  struct mad_header header;
nathan@0
   411
  mad_stream_init(&stream);
nathan@0
   412
  mad_stream_options(&stream,MAD_OPTION_IGNORECRC);
nathan@0
   413
  mad_header_init(&header);
nathan@0
   414
  bool res=true;
nathan@0
   415
  int errcount=0;
nathan@0
   416
  while(1) {
nathan@0
   417
    if(*urgent) {
nathan@0
   418
      d(printf("id3-scan: urgent request, aborting!\n"))
nathan@0
   419
      res=false; break; // abort scan if there is an urgent request for the decoder lock
nathan@0
   420
      }
nathan@0
   421
nathan@0
   422
    if(mad_header_decode(&header,&stream)<0) {
nathan@0
   423
      if(stream.error==MAD_ERROR_BUFLEN || stream.error==MAD_ERROR_BUFPTR) {
nathan@0
   424
        int s=MadStream(&stream,str);
nathan@0
   425
        if(s>0) continue;
nathan@0
   426
        if(s<0) res=false;
nathan@0
   427
        break;
nathan@0
   428
        }
nathan@0
   429
      else if(stream.error==MAD_ERROR_LOSTSYNC) { // check for ID3 tags
nathan@0
   430
#ifdef DEBUG
nathan@0
   431
        char buf[10];
nathan@0
   432
        int buf2[3];
nathan@0
   433
        memcpy(buf,stream.this_frame,8); buf[8]=0;
nathan@0
   434
        memcpy(buf2,stream.this_frame,8);
nathan@0
   435
        printf("id3-scan: lost sync %08x %08x %s\n",buf2[0],buf2[1],buf);
nathan@0
   436
#endif
nathan@0
   437
        id3_length_t tagsize=id3_tag_query(stream.this_frame,stream.bufend-stream.this_frame);
nathan@0
   438
        if(tagsize>0) {
nathan@0
   439
	  struct id3_tag *tag=GetID3(&stream,tagsize);
nathan@0
   440
	  if(tag) {
nathan@0
   441
            unsigned int vers=id3_tag_version(tag);
nathan@0
   442
            d(printf("id3-scan: found ID3 %s tag (%d.%d)\n",vers==0x100?"V1":"V2",ID3_TAG_VERSION_MAJOR(vers),ID3_TAG_VERSION_MINOR(vers)))
nathan@0
   443
            if(!has_id3 || vers>id3_vers) {
nathan@0
   444
              ParseID3(tag);
nathan@0
   445
              has_id3=true; id3_vers=vers;
nathan@0
   446
              }
nathan@0
   447
	    id3_tag_delete(tag);
nathan@0
   448
	    }
nathan@0
   449
          }
nathan@0
   450
        continue;
nathan@0
   451
        }
nathan@0
   452
      else {
nathan@0
   453
        d(printf("id3-scan: decode header error (frame %d): %s\n",Frames,mad_stream_errorstr(&stream)))
nathan@0
   454
        errcount++;
nathan@0
   455
        if(errcount<MAX_FRAME_ERR*100 && MAD_RECOVERABLE(stream.error)) continue;
nathan@0
   456
        res=false; break;
nathan@0
   457
        }
nathan@0
   458
      }
nathan@0
   459
    errcount=0;
nathan@0
   460
    if(header.bitrate>maxrate) maxrate=header.bitrate;
nathan@0
   461
    if(header.bitrate<minrate) minrate=header.bitrate;
nathan@0
   462
nathan@0
   463
    // Limit xing testing to the first frame header
nathan@0
   464
    if(!Frames) {
nathan@0
   465
      if((xframes=ParseXing(&stream.anc_ptr, stream.anc_bitlen))>=0) {
nathan@0
   466
        is_vbr=true;
nathan@0
   467
        }
nathan@0
   468
      }                
nathan@0
   469
    // Test the first n frames to see if this is a VBR file
nathan@0
   470
    if(!is_vbr && Frames<MIN_SCAN_FRAMES) {
nathan@0
   471
      if(bitrate && header.bitrate!=bitrate) is_vbr=true;
nathan@0
   472
      else bitrate=header.bitrate;
nathan@0
   473
      }
nathan@0
   474
    // We have to assume it's not a VBR file if it hasn't already been
nathan@0
   475
    // marked as one and we've checked n frames for different bitrates
nathan@0
   476
    else if(!is_vbr && has_id3)
nathan@0
   477
      {
nathan@0
   478
      break;
nathan@0
   479
      }
nathan@0
   480
nathan@0
   481
    Frames++;
nathan@0
   482
    mad_timer_add(&duration,header.duration);
nathan@0
   483
    }
nathan@0
   484
  mad_header_finish(&header);
nathan@0
   485
  mad_stream_finish(&stream);
nathan@0
   486
nathan@0
   487
  if(res) {
nathan@0
   488
    d(printf("mad: scanned %d frames%s\n",Frames,Frames?"":"(is this really a mp3?)"))
nathan@0
   489
    if(Frames) {
nathan@0
   490
      SampleFreq=header.samplerate;
nathan@0
   491
      Channels=MAD_NCHANNELS(&header);
nathan@0
   492
      ChMode=header.mode;
nathan@0
   493
      DecoderID=DEC_MP3;
nathan@0
   494
      InfoDone();
nathan@0
   495
nathan@0
   496
      if(!is_vbr) {
nathan@0
   497
        d(printf("mad: constant birate\n"))
nathan@0
   498
        double time = (str->Filesize * 8.0) / (header.bitrate);  // time in seconds
nathan@0
   499
        long nsamples = 32 * MAD_NSBSAMPLES(&header);   // samples per frame
nathan@0
   500
nathan@0
   501
        Frames = (long)(time * header.samplerate / nsamples);
nathan@0
   502
        Total  = (long)time;
nathan@0
   503
        Bitrate= (int)bitrate;
nathan@0
   504
        }
nathan@0
   505
      else if(xframes>0) {
nathan@0
   506
        d(printf("mad: vbr, but has Xing frame\n"))
nathan@0
   507
        mad_timer_multiply(&header.duration, xframes);
nathan@0
   508
        Frames = xframes;
nathan@0
   509
        Total  = mad_timer_count(header.duration,MAD_UNITS_SECONDS);
nathan@0
   510
        }
nathan@0
   511
      else {
nathan@0
   512
        // the durations have been added up, and the number of frames counted. We do nothing here.
nathan@0
   513
        d(printf("mad: vbr detected\n"))
nathan@0
   514
        Total      = mad_timer_count(duration,MAD_UNITS_SECONDS);
nathan@0
   515
        Bitrate    = (int)minrate;
nathan@0
   516
        MaxBitrate = (int)maxrate;
nathan@0
   517
        }
nathan@0
   518
nathan@0
   519
      if(!has_id3 || !Title) FakeTitle(str->Filename,".mp3");
nathan@0
   520
      InfoCache.Cache(this,str);
nathan@0
   521
      }
nathan@0
   522
    }
nathan@0
   523
  return Abort(res);
nathan@0
   524
}
nathan@0
   525
nathan@0
   526
// This function was adapted from player.c, from the 
nathan@0
   527
// libmad distribution
nathan@0
   528
nathan@0
   529
struct id3_tag *cScanID3::GetID3(struct mad_stream *stream, id3_length_t tagsize) const
nathan@0
   530
{
nathan@0
   531
  struct id3_tag *tag=0;
nathan@0
   532
  const id3_byte_t *data;
nathan@0
   533
  id3_byte_t *allocated=0;
nathan@0
   534
nathan@0
   535
  id3_length_t count=stream->bufend-stream->this_frame;
nathan@0
   536
nathan@0
   537
  if(count>=tagsize) {
nathan@0
   538
    data=stream->this_frame;
nathan@0
   539
    mad_stream_skip(stream,tagsize);
nathan@0
   540
    }
nathan@0
   541
  else {
nathan@0
   542
    if(!(allocated=(id3_byte_t *)malloc(tagsize))) {
nathan@0
   543
      esyslog("ERROR: not enough memory for id3 tag buffer");
nathan@0
   544
      return 0;
nathan@0
   545
      }
nathan@0
   546
    memcpy(allocated,stream->this_frame,count);
nathan@0
   547
    mad_stream_skip(stream,count);
nathan@0
   548
nathan@0
   549
    while(count<tagsize) {
nathan@0
   550
      unsigned char *sdata;
nathan@0
   551
      unsigned long len, slen;
nathan@0
   552
nathan@0
   553
      if(!str->Stream(sdata,slen) || !slen) {
nathan@0
   554
         d(printf("mad: error or eof on ID3 tag parse\n"))
nathan@0
   555
         free(allocated);
nathan@0
   556
         return 0;
nathan@0
   557
         }
nathan@0
   558
      len=tagsize-count; if(len>slen) len=slen;
nathan@0
   559
      memcpy(allocated+count,sdata,len);
nathan@0
   560
      count+=len;
nathan@0
   561
      sdata+=len; slen-=len;
nathan@0
   562
      if(slen) mad_stream_buffer(stream,sdata,slen);
nathan@0
   563
      }
nathan@0
   564
    data=allocated;
nathan@0
   565
    }
nathan@0
   566
nathan@0
   567
  tag=id3_tag_parse(data,tagsize);
nathan@0
   568
  if(allocated) free(allocated);
nathan@0
   569
  return tag;
nathan@0
   570
}
nathan@0
   571
nathan@0
   572
void cScanID3::ParseID3(const struct id3_tag *tag)
nathan@0
   573
{
nathan@0
   574
  d(printf("id3-scan: parsing ID3 tag\n"))
nathan@0
   575
  ParseStr(tag,ID3_FRAME_TITLE,Title);
nathan@0
   576
  ParseStr(tag,ID3_FRAME_ARTIST,Artist);
nathan@0
   577
  ParseStr(tag,ID3_FRAME_ALBUM,Album);
nathan@0
   578
  char *data=0;
nathan@0
   579
  ParseStr(tag,ID3_FRAME_YEAR,data);
nathan@0
   580
  if(data) Year=atol(data);
nathan@0
   581
  free(data);
nathan@0
   582
  //ParseStr(tag,ID3_FRAME_TRACK,Track);
nathan@0
   583
  //ParseStr(tag,ID3_FRAME_GENRE,Genre);
nathan@0
   584
}
nathan@0
   585
nathan@0
   586
/*
nathan@0
   587
void cScanID3::ParsePic(const struct id3_tag *tag, const char *id, char * &name)
nathan@0
   588
{
nathan@0
   589
  const struct id3_frame *frame=id3_tag_findframe(tag,id,0);
nathan@0
   590
  if(frame) {
nathan@0
   591
    id3_length_t len;
nathan@0
   592
    const id3_byte_t *data=id3_field_getbinarydata(&frame->fields[1],&len);
nathan@0
   593
    if(data && len>0) {
nathan@0
   594
      static const char salt[] = { "$1$id3__pic$" };
nathan@0
   595
      
nathan@0
   596
      }
nathan@0
   597
    }
nathan@0
   598
}
nathan@0
   599
*/
nathan@0
   600
nathan@0
   601
// This function was adapted from player.c, from the 
nathan@0
   602
// libmad distribution 
nathan@0
   603
nathan@0
   604
void cScanID3::ParseStr(const struct id3_tag *tag, const char *id, char * &data)
nathan@0
   605
{
nathan@0
   606
  const struct id3_frame *frame=id3_tag_findframe(tag,id,0);
nathan@0
   607
  if(!frame) return;
nathan@0
   608
nathan@0
   609
  free(data); data=0;
nathan@0
   610
  const union id3_field *field=&frame->fields[1];
nathan@0
   611
  if(id3_field_getnstrings(field)>0) {
nathan@0
   612
    const id3_ucs4_t *ucs4=id3_field_getstrings(field,0);
nathan@0
   613
    if(!ucs4) return;
nathan@0
   614
    if(!strcmp(id,ID3_FRAME_GENRE)) ucs4=id3_genre_name(ucs4);
nathan@0
   615
nathan@0
   616
    id3_latin1_t *latin1=id3_ucs4_latin1duplicate(ucs4);
nathan@0
   617
    if(!latin1) return;
nathan@0
   618
nathan@0
   619
    data=strdup((char *)latin1);
nathan@0
   620
    free(latin1);
nathan@0
   621
    }
nathan@0
   622
}
nathan@0
   623
nathan@0
   624
// XING parsing was adapted from the MAD winamp input plugin,
nathan@0
   625
// from the libmad distribution
nathan@0
   626
nathan@0
   627
#define XING_MAGIC (('X'<<24) | ('i'<<16) | ('n'<<8) | 'g')
nathan@0
   628
#define XING_FRAMES 0x0001
nathan@0
   629
// #define XING_BYTES  0x0002
nathan@0
   630
// #define XING_TOC    0x0004
nathan@0
   631
// #define XING_SCALE  0x0008
nathan@0
   632
nathan@0
   633
int cScanID3::ParseXing(struct mad_bitptr *ptr, unsigned int bitlen) const
nathan@0
   634
{
nathan@0
   635
  if(bitlen>=64 && mad_bit_read(ptr,32)==XING_MAGIC) {
nathan@0
   636
    int flags=mad_bit_read(ptr, 32);
nathan@0
   637
    bitlen-=64;
nathan@0
   638
    return (bitlen>=32 && (flags & XING_FRAMES)) ? mad_bit_read(ptr,32) : 0;
nathan@0
   639
    }
nathan@0
   640
  return -1;
nathan@0
   641
}