]> git.vomp.tv Git - vompclient.git/blob - demuxeraudio.cc
Preparations for dynamic mode switching
[vompclient.git] / demuxeraudio.cc
1 /*\r
2     Copyright 2006 Mark Calderbank, Andreas Vogel\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
19 */\r
20 \r
21 #include "demuxeraudio.h"\r
22 #include "audio.h"\r
23 #include "i18n.h"\r
24 #include "log.h"\r
25 \r
26 #define HDRBYTE1 0xff\r
27 #define HDRBYTE2 0xe0\r
28 #define HDRBYTE2MASK 0xe0\r
29 \r
30 \r
31 \r
32 class PacketBuffer {\r
33   \r
34   public:\r
35     PacketBuffer(Stream *as,UCHAR strtype) {\r
36       log=Log::getInstance();\r
37       audio=as;\r
38       streamtype=strtype;\r
39       newStream();\r
40     }\r
41     //just handle the data (do not deal with headers)\r
42     int putInternal(UCHAR* buf,int len,unsigned int &packetnum);\r
43     void reset(){\r
44       partPacket=0;\r
45       bytesWritten=0;\r
46       framelen=DemuxerAudio::PACKET_SIZE;\r
47     }\r
48     void newStream() {\r
49       reset();\r
50       numpackets=0;\r
51       numbytes=0;\r
52                         skipfactor=0;\r
53                         numskip=0;\r
54     }\r
55     bool bufferFull() {\r
56       return (partPacket>=framelen);\r
57     }\r
58     //can we write a new packet?\r
59     bool bufferEmpty() {\r
60       return partPacket==0;\r
61     }\r
62     //only set this, if buffer empty\r
63     //otherwise ignored!\r
64     bool setFramelen(int len) {\r
65       if (! bufferEmpty() ) return false;\r
66       if (len > (int)DemuxerAudio::PACKET_SIZE) return false;\r
67       framelen=len;\r
68       return true;\r
69     }\r
70     //how much bytes do we need to fill the packet?\r
71     int bytesMissing() {\r
72       return framelen-partPacket;\r
73     }\r
74     int getFramelen() {\r
75       return framelen;\r
76     }\r
77                 void setSkipFactor(int factor) {\r
78                         skipfactor=factor;\r
79                         numskip=0;\r
80                 }\r
81   private:\r
82     void packetWritten() {\r
83       numbytes+=framelen;\r
84       //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written packet %ld l=%d, bytes %ld",numpackets,framelen,numbytes);\r
85       numpackets++;\r
86       reset();\r
87     }\r
88                 bool doSkip();\r
89     UCHAR store[DemuxerAudio::PACKET_SIZE]; // Storage for partial packets\r
90     int partPacket;    // Length of partial packet stored from previous put()\r
91     int bytesWritten;  //if they are !=0 and != framelength the stream is full...\r
92     int framelen;\r
93     Log * log;\r
94     Stream * audio;\r
95     UCHAR streamtype;\r
96     //global counters\r
97     ULONG numpackets;\r
98     ULONG numbytes;\r
99                 int skipfactor;\r
100                 int numskip;\r
101 };\r
102 \r
103 \r
104 DemuxerAudio::DemuxerAudio(int p_vID, int p_aID)\r
105 {\r
106   inSync=false;\r
107   isStarting=true;\r
108   log=Log::getInstance();\r
109   readHeaders=0;\r
110   streamtype=Audio::MP3;\r
111   buffer=new PacketBuffer(&audiostream,streamtype);\r
112 //  buffer=new PacketBuffer(&teststream,streamtype);\r
113   globalBytesWritten=0;\r
114   packetnum=0;\r
115   id3=NULL;\r
116   info=NULL;\r
117         vbr=NULL;\r
118   reset();\r
119 }\r
120 \r
121 DemuxerAudio::~DemuxerAudio() {\r
122   delete buffer;\r
123   if(info) delete info;\r
124   if(id3) delete id3;\r
125         if (vbr) delete vbr;\r
126 }\r
127 \r
128 void DemuxerAudio::flush()\r
129 {\r
130   Demuxer::flushAudio();\r
131   buffer->newStream();\r
132   tmpFill=0;\r
133 }\r
134 \r
135 void DemuxerAudio::reset() {\r
136   buffer->newStream();\r
137   tmpFill=0;\r
138   readHeaders=0;\r
139   packetnum=0;\r
140   outOfSync=0;\r
141   globalBytesWritten=0;\r
142   if (id3) delete id3;\r
143   id3=NULL;\r
144   if (info) delete info;\r
145   info=NULL;\r
146         if (vbr) delete vbr;\r
147         vbr=NULL;\r
148   inSync=false;\r
149   hasHdrInfo=false;\r
150   hasVBRInfo=false;\r
151   isStarting=true;\r
152   hdrBitrate=128000;\r
153   hdrSamplingRate=44100;\r
154   avrBitrate=0;\r
155   hdrFramelen=0;\r
156   isStarting=true;\r
157 }\r
158 \r
159 int DemuxerAudio::scan(UCHAR *buf, int len)\r
160 {\r
161   //no differend pids here\r
162   return 0;\r
163 }\r
164 \r
165 void DemuxerAudio::setVID(int p_vID)\r
166 {\r
167 }\r
168 \r
169 void DemuxerAudio::setAID(int p_aID,int type)\r
170 {\r
171 }\r
172 \r
173 static const char * id3_1_genre[] = {\r
174   "Blueshhh",\r
175   "Classic Rock",\r
176   "Country",\r
177   "Dance",\r
178   "Disco",\r
179   "Funk",\r
180   "Grunge",\r
181   "Hip-Hop",\r
182   "Jazz",\r
183   "Metal",\r
184   "New Age",\r
185   "Oldies",\r
186   "Other",\r
187   "Pop",\r
188   "R&B",\r
189   "Rap",\r
190   "Reggae",\r
191   "Rock",\r
192   "Techno",\r
193   "Industrial",\r
194   "Alternative",\r
195   "Ska",\r
196   "Death Metal",\r
197   "Pranks",\r
198   "Soundtrack",\r
199   "Euro-Techno",\r
200   "Ambient",\r
201   "Trip-Hop",\r
202   "Vocal",\r
203   "Jazz+Funk",\r
204   "Fusion",\r
205   "Trance",\r
206   "Classical",\r
207   "Instrumental",\r
208   "Acid",\r
209   "House",\r
210   "Game",\r
211   "Sound Clip",\r
212   "Gospel",\r
213   "Noise",\r
214   "AlternRock",\r
215   "Bass",\r
216   "Soul",\r
217   "Punk",\r
218   "Space",\r
219   "Meditative",\r
220   "Instrumental Pop",\r
221   "Instrumental Rock",\r
222   "Ethnic",\r
223   "Gothic",\r
224   "Darkwave",\r
225   "Techno-Industrial",\r
226   "Electronic",\r
227   "Pop-Folk",\r
228   "Eurodance",\r
229   "Dream",\r
230   "Southern Rock",\r
231   "Comedy",\r
232   "Cult",\r
233   "Gangsta",\r
234   "Top 40",\r
235   "Christian Rap",\r
236   "Pop/Funk",\r
237   "Jungle",\r
238   "Native American",\r
239   "Cabaret",\r
240   "New Wave",\r
241   "Psychadelic",\r
242   "Rave",\r
243   "Showtunes",\r
244   "Trailer",\r
245   "Lo-Fi",\r
246   "Tribal",\r
247   "Acid Punk",\r
248   "Acid Jazz",\r
249   "Polka",\r
250   "Retro",\r
251   "Musical",\r
252   "Rock & Roll",\r
253   "Hard Rock"\r
254 };\r
255 \r
256 \r
257 \r
258 static int bitrateTable[16][5]={\r
259 /*        L1,L2,L3,2L1,2L2 */\r
260 /*0000*/ {-1,-1,-1,-1,-1},\r
261 /*0001*/ {32,32,32,32,8},\r
262 /*0010*/ {64,48,40,48,16},\r
263 /*0011*/ {96,56,48,56,24},\r
264 /*0100*/ {128,64,56,64,32},\r
265 /*0101*/ {160,80,64,80,40},\r
266 /*0110*/ {192,96,80,96,48},\r
267 /*0111*/ {224,112,96,112,56},\r
268 /*1000*/ {256,128,112,128,64},\r
269 /*1001*/ {288,160,128,144,80},\r
270 /*1010*/ {320,192,160,160,96},\r
271 /*1011*/ {352,224,192,176,112},\r
272 /*1100*/ {384,256,224,192,128},\r
273 /*1101*/ {416,320,256,224,144},\r
274 /*1110*/ {448,384,320,256,160},\r
275 /*1111*/ {-1,-1,-1,-1,-1} };\r
276 \r
277 static int  samplingRateTable[4][3]={\r
278 /*00*/ {44100,22050,11025},\r
279 /*01*/ {48000,24000,12000},\r
280 /*10*/ {32000,16000,8000},\r
281 /*11*/ {-1,-1,-1}};\r
282 \r
283 //max 7 char!\r
284 static const char * mpegString(UCHAR code) {\r
285   switch(code) {\r
286     case 0:\r
287       return "MPEG2.5";\r
288     case 1:\r
289       return "RESERV";\r
290     case 2:\r
291       return "MPEG 2";\r
292     case 3:\r
293       return "MPEG 1";\r
294   }\r
295   return "UNKNOWN";\r
296 }\r
297 \r
298 static const char * layerString(UCHAR code) {\r
299   switch(code) {\r
300     case 0:\r
301       return "Layer reserved";\r
302     case 1:\r
303       return "Layer III";\r
304     case 2:\r
305       return "Layer II";\r
306     case 3:\r
307       return "Layer I";\r
308   }\r
309   return "Layer UNKNOWN";\r
310 }\r
311 /**\r
312   * parse an id3 Header\r
313   * provided by Brian Walton\r
314   * @returns -1 of nothing found\r
315   */\r
316   \r
317 int DemuxerAudio::id3_2_3_FrameParse(unsigned char buf[], id3_frame *frame)\r
318 {\r
319   if (buf[0] < 0x20 || buf[1] < 0x20 || buf [2] < 0x20 ) return -1;\r
320   frame->size = (buf[4] & 0x7F) << 21 | (buf[5] & 0x7F) << 14 |  (buf[6] & 0x7F) << 7 | (buf[7] & 0x7F);\r
321   if (frame->size == 0) return -1;\r
322   //TODO. clearify flags against:\r
323   //http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b\r
324   frame->flags.tagAlterPreserv = (buf[8] & 0x80) >> 7;\r
325   frame->flags.filelterPreserv = (buf[8] & 0x40) >> 6;\r
326   frame->flags.readOnly = (buf[8] & 0x20) >> 5;\r
327   frame->flags.groupId = (buf[9] & 0x20) >> 5;\r
328   frame->flags.compression = (buf[9] & 0x80) >> 7;\r
329   frame->flags.encryption = (buf[9] & 0x40) >> 6;\r
330   frame->flags.unsync = 0;\r
331   frame->flags.dataLen = 0;\r
332   return 0;\r
333 }\r
334 \r
335  /**\r
336   * parse an id3 Header\r
337   * provided by Brian Walton\r
338   * @returns -1 of nothing found\r
339   */\r
340   \r
341 int DemuxerAudio::id3_2_2_FrameParse(unsigned char buf[], id3_frame *frame)\r
342 {\r
343   if (buf[0] < 0x20 || buf[1] < 0x20 || buf[2] < 0x20) return -1;\r
344   frame->size = (buf[3] & 0x7F) << 14 |  (buf[4] & 0x7F) << 7 | (buf[5] & 0x7F);\r
345   if (frame->size == 0) return -1;\r
346   return 0;\r
347 }\r
348 \r
349 \r
350 //fill an id3tag from a frame payload\r
351 //http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b\r
352 //http://id3.org/id3v2-00\r
353 static struct tagid {\r
354   const char * bytes;\r
355   int index;\r
356 } knownFrames[]= {\r
357   //ID3V2.3\r
358   {"TIT2",1}, //title\r
359   {"TPE1",2}, //artist\r
360   {"TCON",3}, //genre\r
361   {"TRCK",6}, //track\r
362   {"TYER",4}, //year\r
363   {"TALB",5}, //album\r
364   {"TCOM",7}, //composer\r
365   {"COMM",8}, //comment\r
366               //Text encoding           $xx\r
367               //Language                $xx xx xx\r
368               //Short content descrip.  <text string according to encoding> $00 (00)\r
369               //The actual text         <full text string according to encoding>\r
370   //ID3V2.0\r
371   {"TT2",1 },\r
372   {"TP1",2 },\r
373   {"TCM",7 },\r
374   {"TCO",3 }, //(genreNumber)\r
375   {"TAL",5 },\r
376   {"TRK",6 },\r
377   {"TYE",4 },\r
378   {"COM",8 }\r
379 };\r
380 #define NUMKNOWN (sizeof(knownFrames)/sizeof(knownFrames[0]))\r
381 \r
382 /*fill in infos\r
383   from an ID3 V2.x, V2.3 frame into the tags structure\r
384   frameData must point to the header\r
385   framelen is the len without header (10 Bytes for V23, 6 Bytes for v2x)\r
386   */\r
387 \r
388 #define MAXLEN(tagtype) ((UINT)frameLen<sizeof(tag->tagtype)-1?(UINT)frameLen:sizeof(tag->tagtype)-1)\r
389 bool DemuxerAudio::fillId3Tag(id3_tag * tag,UCHAR * frameData, int frameLen, int dataOffset, bool v23) {\r
390   int tl=v23?4:3;\r
391   int tagIndex=-1;\r
392   if (tag == NULL) return false;\r
393   if (frameLen < 2) return false;\r
394   for (UINT i=0;i< NUMKNOWN;i++) {\r
395     if(strncmp((char *)frameData,knownFrames[i].bytes,tl) == 0) {\r
396       tagIndex=knownFrames[i].index;\r
397       break;\r
398     }\r
399   }\r
400   if (tagIndex < 0) return false;\r
401   UCHAR encoding=*(frameData+dataOffset);\r
402   dataOffset++;\r
403   frameLen--;\r
404   if (encoding != 0) {\r
405     log->log("DemuxerAudio",Log::DEBUG,"unknown encoding for tag %d, tagid %s",encoding,\r
406         knownFrames[tagIndex].bytes);\r
407     return false;\r
408   }\r
409   switch(tagIndex) {\r
410     case 1:  //title\r
411       strncpy(tag->title,(char*)(frameData+dataOffset),MAXLEN(title));\r
412       tag->title[MAXLEN(title)]=0;\r
413       break;\r
414     case 2:  //artist\r
415       strncpy(tag->artist,(char*)(frameData+dataOffset),MAXLEN(artist));\r
416       tag->artist[MAXLEN(artist)]=0;\r
417       break;\r
418     case 3:  //genre\r
419       {\r
420       UCHAR * st=frameData+dataOffset;\r
421       int genre=0;\r
422       if (*st=='(') {\r
423         genre=atoi((const char *)(st+1)) && 31;\r
424         st=(UCHAR *)id3_1_genre[genre];\r
425       }\r
426       strncpy(tag->genre,(char*)st,MAXLEN(genre));\r
427       tag->genre[MAXLEN(genre)]=0;\r
428       break;\r
429       }\r
430     case 4:  //year\r
431       strncpy(tag->year,(char *)(frameData+dataOffset),MAXLEN(year));\r
432       tag->year[MAXLEN(year)]=0;\r
433       break;\r
434     case 5:  //album\r
435       strncpy(tag->album,(char *)(frameData+dataOffset),MAXLEN(album));\r
436       tag->album[MAXLEN(album)]=0;\r
437       break;\r
438     case 6:  //track\r
439       strncpy(tag->track,(char *)(frameData+dataOffset),MAXLEN(track));\r
440       tag->track[MAXLEN(track)]=0;\r
441       break;\r
442     case 7:  //composer\r
443       strncpy(tag->composer,(char *)(frameData+dataOffset),MAXLEN(composer));\r
444       tag->composer[MAXLEN(composer)]=0;\r
445       break;\r
446     case 8:  //comment\r
447       strncpy(tag->comment,(char *)(frameData+dataOffset),MAXLEN(comment));\r
448       tag->comment[MAXLEN(comment)]=0;\r
449       break;\r
450     default:\r
451       return false;\r
452   }\r
453 \r
454   return true;\r
455 }\r
456 \r
457 /**\r
458   * parse an id3 Header\r
459   * based on code provided by Brian Walton\r
460   * @returns -1 of nothing found\r
461   *  otherwise the id3 info is filled\r
462   */\r
463 \r
464 int DemuxerAudio::parseID3V2(UCHAR *data, int len) {\r
465   int debug=0;\r
466   UCHAR * start=data;\r
467   id3_header id3header;\r
468   id3_frame id3frame;\r
469   id3_tag * id3tag=NULL;\r
470   //len = read(fd, data, 10);\r
471   if (len < 10) {\r
472     delete id3tag;\r
473     return -1;\r
474   }\r
475   len-=10;\r
476   if(data[0]=='I' && data[1]=='D' && data[2]=='3')\r
477   {\r
478     id3tag=new id3_tag();\r
479     id3header.major = data[3];\r
480     id3header.minor = data[4];\r
481     if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 V2.%d.%d found\n", id3header.major, id3header.minor);\r
482     id3header.flags.unsynchronisation = (data[5] & 0x80)>>7;\r
483     id3header.flags.extended_header = (data[5] & 0x40)>>6;\r
484     id3header.flags.experimental = (data[5] & 0x20)>>5;\r
485     id3header.flags.footer = (data[5] & 0x10)>>4;\r
486     if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Unsynchronisation flag: %d\n", id3header.flags.unsynchronisation);\r
487     if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header flag: %d\n", id3header.flags.extended_header);\r
488     if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Experimental indicator flag: %d\n", id3header.flags.experimental);\r
489     if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Footer present flag: %d\n", id3header.flags.footer);\r
490     id3header.size = (data[6] & 0x7F) << 21 | (data[7] & 0x7F) << 14 |  (data[8] & 0x7F) << 7 | (data[9] & 0x7F);\r
491     if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 Size: %d\n", id3header.size);\r
492     data=start+10;\r
493     if (len <= id3header.size) {\r
494       if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"header size to big %d, only %d bytes available\n",id3header.size,len);\r
495       delete id3tag;\r
496       return -1;\r
497     }\r
498     if (id3header.flags.extended_header)\r
499     {\r
500       int extended_hdr_hdr=4;  //still to be discussed (id3.org...)\r
501       //read extended header size\r
502       if (len < extended_hdr_hdr) {\r
503         if (debug) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended header found but cannot read\n");\r
504         delete id3tag;\r
505         return -1;\r
506       }\r
507       if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"remaining %d chars after extended hdr hdr\n", len);\r
508       id3header.extended_header.size = (data[0] & 0x7F) << 21 | (data[1] & 0x7F) << 14 |  (data[2] & 0x7F) << 7 | (data[3] & 0x7F);\r
509       if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header size: %d\n", id3header.extended_header.size);\r
510       if (len <= id3header.extended_header.size+extended_hdr_hdr) {\r
511       if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended Header to big, only %d bytes available\n",len);\r
512         delete id3tag;\r
513         return -1;\r
514       }\r
515       //lseek(fd, id3header.extended_header.size - 6, SEEK_CUR);\r
516       data+=id3header.extended_header.size+extended_hdr_hdr;\r
517       len-=id3header.extended_header.size+extended_hdr_hdr;\r
518     }\r
519     //set the end of the header\r
520     UCHAR * eob=start+id3header.size+10;\r
521     bool readNext=true;\r
522     while (data < eob && readNext)\r
523     {\r
524       //skip over some padding - found this in lame MCDI tag...\r
525       if (*data == 0) {\r
526         data++;\r
527         continue;\r
528       }\r
529       readNext=false;\r
530       switch(id3header.major)\r
531       {\r
532         case 2: //ID3 V2.2.x\r
533           if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.2 frame, %d : %c %c %c\n", data-start,*data,*(data+1),*(data+2));\r
534           if (data + 6 >= eob)\r
535           {\r
536             break;\r
537           }\r
538           if (id3_2_2_FrameParse(data, &id3frame) < 0)\r
539           {\r
540             break;\r
541           }\r
542           if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame size: %d\n", id3frame.size);\r
543           fillId3Tag(id3tag,data,id3frame.size,6,false);\r
544           if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + 6 +1);\r
545           data+=6+id3frame.size;\r
546           readNext=true;\r
547           break;\r
548         case 3: //ID3 V2.3.x\r
549           {\r
550           if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.3 frame, %d : %c %c %c %c\n", data-start,\r
551               *data,*(data+1),*(data+2),*(data+3));\r
552           if (data + 10 >= eob)\r
553           {\r
554             break;\r
555           }\r
556           if (id3_2_3_FrameParse(data, &id3frame) <0)\r
557           {\r
558             break;\r
559           }\r
560           if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame size: %d\n", id3frame.size);\r
561           int dataOffset=10;\r
562           if (id3frame.flags.groupId)\r
563           {\r
564             dataOffset++;\r
565             if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame group: %d\n", data[dataOffset]);\r
566           }\r
567           if (id3frame.flags.compression)\r
568           {\r
569             if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame compressed: %d\n", id3frame.flags.compression);\r
570           }\r
571           if (id3frame.flags.encryption)\r
572           {\r
573             dataOffset+=1;\r
574             if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame encryption method: %d\n", data[dataOffset]);\r
575           }\r
576           fillId3Tag(id3tag,data,id3frame.size-dataOffset+10,dataOffset,true);\r
577           if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + dataOffset +1);\r
578           data+=10+id3frame.size;\r
579           readNext=true;\r
580           break;\r
581           }\r
582         default:\r
583           //don't support this version\r
584           delete id3tag;\r
585           return -1;\r
586       }\r
587     }\r
588 \r
589     data=eob;\r
590     //store the found tag\r
591     if (id3) delete id3;\r
592     id3=id3tag;\r
593     return data-start;\r
594   }\r
595   return -1;\r
596 }\r
597 \r
598 /**\r
599   * parse an id3v1 Header\r
600   * based on code provided by Brian Walton\r
601   * @returns -1 of nothing found\r
602   *  otherwise the id3 info is filled\r
603   */\r
604 #define MEMCPY(type,len,offset) {int type##max=sizeof(tag->type)-1<len?sizeof(tag->type)-1:len;strncpy(tag->type,(char*)&data[offset],type##max);tag->type[type##max]=0;}\r
605 \r
606 int DemuxerAudio::parseID3V1(UCHAR *data, int len) {\r
607   int debug=1;\r
608   if (len < 128) return -1;\r
609   if(data[0]=='T' && data[1]=='A' && data[2]=='G')\r
610   {\r
611     id3_tag * tag=new id3_tag();\r
612     if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1 tag found\n");\r
613     MEMCPY(title,30,3);\r
614     MEMCPY(artist,30,33);\r
615     MEMCPY(album,30,63);\r
616     MEMCPY(year,4,93);\r
617     if (data[125]==0 && data[126]!=0)\r
618     { //ID3 V1.1\r
619       if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.1 tag\n");\r
620       MEMCPY(comment,29,97);\r
621       sprintf(tag->track, "%d", data[126]);\r
622     } else {\r
623       if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.0 tag\n");\r
624       MEMCPY(comment,30,97);\r
625     }\r
626     if (data[127] < sizeof(id3_1_genre)/sizeof(id3_1_genre[0]))\r
627     {\r
628       sprintf(tag->genre, id3_1_genre[data[127]]);\r
629     }\r
630     if (id3) delete id3;\r
631     id3=tag;\r
632     return 0;\r
633   }\r
634   return -1;\r
635 }\r
636 \r
637 //infos from http://www.multiweb.cz/twoinches/MP3inside.htm\r
638 int DemuxerAudio::parseVBR(UCHAR *data, int len) {\r
639         UCHAR *hdr=findHeader(data,len);\r
640         //we expect the header exactly here\r
641         if (hdr != data) return -1;\r
642         const static char * VBRHDR="Xing";\r
643         int vbridpos=36;\r
644   UCHAR mpgtype=(data[1] & 0x18)>>3;\r
645   UCHAR chmode=(data[2] & 0xc0) >> 6;\r
646   UCHAR layer=(data[2] & 0x06) >>1;\r
647         if ( mpgtype == 3 && chmode == 11) vbridpos=21;\r
648         if ( mpgtype != 3 && chmode != 11) vbridpos=21;\r
649         if ( mpgtype != 3 && chmode == 11) vbridpos=13;\r
650         //check for the header ID\r
651         if (vbridpos+(int)strlen(VBRHDR)+4 >= len) {\r
652                 Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header %d",len);\r
653                 return -1;\r
654         }\r
655         for (int i=4;i<vbridpos;i++) {\r
656                 if (data[i] != 0) {\r
657                   Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"garbage when searching VBR header at pos %d",i);\r
658                         return -1;\r
659                 }\r
660         }\r
661         if ( strncmp((char *)&data[vbridpos],VBRHDR,strlen(VBRHDR)) != 0) {\r
662                 Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"no VBR header at pos %d",vbridpos);\r
663                 return -1;\r
664         }\r
665         int framedata=vbridpos+strlen(VBRHDR);\r
666         int expectedLen=0;\r
667         //OK we should now have a valid vbr header\r
668         bool hasFramenum=data[framedata+3] & 1;\r
669         bool hasBytes=data[framedata+3] & 0x2;\r
670         bool hasTOC=data[framedata+3] & 0x4;\r
671         expectedLen+=8;\r
672         if (hasTOC) expectedLen+=100;\r
673         if (framedata+expectedLen > len) {\r
674                 Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header data %d, expected %d",\r
675                                 len,framedata+expectedLen);\r
676                 return -1;\r
677         }\r
678         if (!hasFramenum || ! hasBytes || ! hasTOC) {\r
679                 //not usefull for us..\r
680                 Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"not all parts in VBR header - ignore");\r
681                 return -1;\r
682         }\r
683         framedata+=4;\r
684         if (vbr) delete vbr;\r
685         vbr=new vbrInfo();\r
686         vbr->numFrames=data[framedata] << 24 | data[framedata+1]<<16|\r
687                         data[framedata+2]<<8 |data[framedata+3];\r
688         framedata+=4;\r
689         vbr->numBytes=data[framedata] << 24 | data[framedata+1]<<16|\r
690                         data[framedata+2]<<8 |data[framedata+3];\r
691         framedata+=4;\r
692         for (int ti=0;ti<100;ti++) {\r
693                 vbr->table[ti]=data[framedata+ti];\r
694         }\r
695         //compute file size in seconds\r
696         //should be (#of frames -1) *samplesPerFrame / sampleRate\r
697         //TODO: difference for Mono?\r
698         ULONG samplesPerFrame=384; //layer1\r
699         if (layer != 3) samplesPerFrame=1152;\r
700         vbr->fileSeconds=(vbr->numFrames-1)*samplesPerFrame/hdrSamplingRate;\r
701         Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"successfully read VBR %ldbytes, %ld frames, %ldsec",\r
702                         vbr->numBytes,vbr->numFrames,vbr->fileSeconds);\r
703         return hdrFramelen;\r
704 }\r
705 \r
706 \r
707 \r
708 \r
709 \r
710 \r
711 \r
712 UCHAR * DemuxerAudio::findHeader(UCHAR *buf, int len, bool writeInfo) {\r
713   while (len >= 3) //assume hdr+crc\r
714   {\r
715     UCHAR pattern=*buf;\r
716     buf++; len--;\r
717     if (pattern != HDRBYTE1 ) continue;\r
718     if ((*buf & HDRBYTE2MASK) != HDRBYTE2) continue;\r
719     if (readHeader((buf-1),4,writeInfo) != 0) continue;\r
720     return buf-1;\r
721   }\r
722   return NULL;\r
723 }\r
724 \r
725 \r
726 \r
727 int DemuxerAudio::readHeader(UCHAR * hbuf,int len,bool writeInfo) {\r
728   int curFramelen=0;\r
729   int curBitrate=0;\r
730   int curSamplingRate=0;\r
731   if (*hbuf != HDRBYTE1) return -1;\r
732   hbuf++;\r
733   if ((*hbuf & HDRBYTE2MASK) != HDRBYTE2) return -1;\r
734   UCHAR mpgtype=(*hbuf & 0x18)>>3;\r
735   if (mpgtype == 1) {\r
736     log->log("DemuxerAudio",Log::DEBUG,"header invalid mpgtype %s %i %i %i",\r
737         mpegString(mpgtype),*hbuf,*(hbuf+1),*(hbuf+2));\r
738     return 1;\r
739   }\r
740   UCHAR layer=(*hbuf & 0x06) >>1;\r
741   //bool hasCRC=!(*hbuf & 1);\r
742   hbuf++;\r
743   UCHAR bitrateCode=(*hbuf & 0xf0) >>4;\r
744   UCHAR samplingCode=(*hbuf & 0x0c) >> 2;\r
745   bool padding=*hbuf & 0x02;\r
746   hbuf++;\r
747   //0 Stereo, 1 JointStereo, 2 Dual, 3 Mono\r
748   UCHAR chmode=(*hbuf & 0xc0) >> 6;\r
749   //UCHAR extension=(*hbuf & 0x30) >> 4;\r
750 \r
751   //layercode: 1-L3, 2-L2, 3-L1\r
752   //columns 0,1,2 for MPEG1\r
753   UCHAR bitrateColumn=3-layer;\r
754   if (bitrateColumn > 2) {\r
755     log->log("DemuxerAudio",Log::DEBUG,"header invalid layer %s %i %i %i",\r
756         layerString(layer),*(hbuf-2),*(hbuf-1),*hbuf);\r
757     return 1;\r
758   }\r
759   if (mpgtype != 3) bitrateColumn+=3;\r
760   if (bitrateColumn>4) bitrateColumn=4;\r
761   curBitrate=1000*bitrateTable[bitrateCode][bitrateColumn];\r
762   UCHAR sampleRateColumn=0;\r
763   if (mpgtype == 10) sampleRateColumn=1;\r
764   if (mpgtype == 0) sampleRateColumn=2;\r
765   curSamplingRate=samplingRateTable[samplingCode][sampleRateColumn];\r
766   if (curSamplingRate < 0 || curBitrate < 0) {\r
767     log->log("DemuxerAudio",Log::DEBUG,"header invalid rates br=%d sr=%d %i %i %i",\r
768         curBitrate,curSamplingRate,*(hbuf-2),*(hbuf-1),*hbuf);\r
769     return 1;\r
770   }\r
771   int padbytes=0;\r
772   if (padding) {\r
773     if (layer == 3) padbytes=4;\r
774     else padbytes=1;\r
775   }\r
776   if (layer == 3) {\r
777     //Layer 1\r
778     //FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4\r
779     curFramelen=(12*curBitrate/curSamplingRate+padbytes) * 4;\r
780   }\r
781   else {\r
782     //Layer 2/3\r
783     //FrameLengthInBytes = 144 * BitRate / SampleRate + Padding\r
784     curFramelen=144*curBitrate/curSamplingRate+padbytes;\r
785   }\r
786   //the header itself\r
787   if (curFramelen < 32) {\r
788   log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d-invalid %i %i %i",\r
789       readHeaders,mpegString(mpgtype),layerString(layer),\r
790       curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf);\r
791       return 1;\r
792   }\r
793   if (writeInfo || isStarting){\r
794   log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d %i %i %i",\r
795       readHeaders,mpegString(mpgtype),layerString(layer),\r
796       curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf);\r
797     if (info) delete info;\r
798     info=new mpegInfo();\r
799     strcpy(info->mpegVersion,mpegString(mpgtype));\r
800     info->mpegLayer=4-layer;\r
801     info->bitRate=curBitrate;\r
802     info->avrBitrate=curBitrate;\r
803     info->sampleRate=curSamplingRate;\r
804     const char *chmodStr=tr("Stereo");\r
805     switch (chmode) {\r
806       case 1: chmodStr=tr("JointStereo");break;\r
807       case 2: chmodStr=tr("Dual");break;\r
808       case 3: chmodStr=tr("Mono");break;\r
809     }\r
810     SNPRINTF(info->info,sizeof(info->info)-1,"%s",chmodStr);\r
811   }\r
812         if (isStarting) avrBitrate=curBitrate;\r
813   isStarting=false;\r
814   readHeaders++;\r
815   //moving average F=0.005\r
816   avrBitrate=avrBitrate+((5*(curBitrate-avrBitrate))/1024);\r
817   hdrBitrate=curBitrate;\r
818   hdrFramelen=curFramelen;\r
819   hdrSamplingRate=curSamplingRate;\r
820         hasHdrInfo=true;\r
821   return 0;\r
822 }\r
823 \r
824 int DemuxerAudio::findPTS(UCHAR* buf, int len, ULLONG* dest)\r
825 {\r
826   //we have no PTS number ...\r
827   *dest=0;\r
828   return (findHeader(buf,len) != NULL)?1:0;\r
829 }\r
830 \r
831 bool PacketBuffer::doSkip() {\r
832         if (!bufferFull()) return false;\r
833         if (skipfactor == 0) return false;\r
834         numskip++;\r
835         if (numskip >= skipfactor) {\r
836                 //sent at least always 2 packets\r
837                 if (numskip > skipfactor) numskip=0;\r
838                 return false;\r
839         }\r
840         packetWritten();\r
841         return true;\r
842 }\r
843         \r
844 // just handle the real stream without dealing with the header\r
845 int PacketBuffer::putInternal(UCHAR * wbuf, int len,unsigned int &packetnum)\r
846 {\r
847    /* Important, the type passed to stream must be a mediapacket type as defined in \r
848        Draintarget.h and not the device setting of the mvp, so we have to translate it here,\r
849        in order to get it working on windows\r
850        --MR */\r
851   UCHAR mptype=0;\r
852   switch (streamtype) {\r
853         case Audio::MPEG1_PES: //?\r
854         case Audio::MPEG2_PES: //Important, this must be a PES !\r
855             mptype=MPTYPE_MPEG_AUDIO; break;\r
856         default:\r
857         case Audio::MP3: //this can be any Mpeg1 Audio not only layer 3 not packed into PES\r
858            mptype=MPTYPE_MPEG_AUDIO_LAYER3;break;\r
859   };\r
860   if (bufferFull()) {\r
861 #ifndef WIN32\r
862                 if (doSkip()) return 0;//NoSkip on Windows\r
863 #endif\r
864     //we are still full - so try to write\r
865     int sent=audio->put(store+bytesWritten,framelen-bytesWritten,/*streamtype*/mptype,packetnum);packetnum++;\r
866     //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream (still full) pp=%d, framelen=%d, written=%d",sent,partPacket,framelen, bytesWritten );\r
867     if (sent < (framelen - bytesWritten)) {\r
868       //packet still not written\r
869       bytesWritten+=sent;\r
870       return 0;\r
871     }\r
872     packetWritten();\r
873     //let the demuxer come back with the rest - need to check header first\r
874     return 0;\r
875   }\r
876   if (partPacket+len >= framelen) {\r
877     //now we have at least a complete packet\r
878     int bytesConsumed=framelen-partPacket;\r
879     memcpy(store+partPacket,wbuf,bytesConsumed);\r
880     partPacket=framelen;\r
881     //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"stored packet %ld, length %d (last %d) for stream %p",numpackets,framelen,bytesConsumed,audio );\r
882 #ifndef WIN32 //No Skip on Windows\r
883                 if (doSkip()) return bytesConsumed;\r
884 #endif\r
885     int sent=audio->put(store,framelen,mptype,packetnum);packetnum++;\r
886     bytesWritten+=sent;\r
887     //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream",sent );\r
888     if (bytesWritten < framelen) {\r
889       //still not completely written\r
890       return bytesConsumed;\r
891     }\r
892     packetWritten();\r
893     //let the player come back...\r
894     return bytesConsumed;\r
895   }\r
896   //OK packet still not complete\r
897   if (len == 0) return 0;\r
898   int bytesConsumed=len;\r
899   memcpy(store+partPacket,wbuf,bytesConsumed);\r
900   partPacket+=bytesConsumed;\r
901   return bytesConsumed;\r
902 }\r
903 \r
904 /**\r
905   major entry for data from a player\r
906   the demuxer is either in the state headerSearch (packet written or\r
907   just at the beginning), writing garbage (inSync=false) or\r
908   handling data (none set)\r
909   A header is expected at the first byte after the previous packet -\r
910   otherwise we switch to garbage where we always search for a header\r
911   (but anyway provide the data to the underlying device - it's probably\r
912   more intelligent then we are...\r
913   We only loose a correct position display.\r
914   */\r
915 int DemuxerAudio::put(UCHAR* wbuf, int len)\r
916 {\r
917   //return audiostream.put(wbuf,len,streamtype);\r
918   int framelen=PACKET_SIZE;\r
919   UCHAR *hdr;\r
920   int bytesConsumed=0;\r
921   int oldBytes=0;\r
922   if (tmpFill != 0 || (buffer->bufferEmpty() && len < HDRLEN)) {\r
923     //OK we have to copy everything to the tmp buffer\r
924     int cp=(UINT)len<(PACKET_SIZE-tmpFill)?(UINT)len:(PACKET_SIZE-tmpFill);\r
925     memcpy(&tmpBuffer[tmpFill],wbuf,cp);\r
926     oldBytes=tmpFill;\r
927     tmpFill+=cp;\r
928     wbuf=tmpBuffer;\r
929     len=tmpFill;\r
930     //if there is no header here and our buffer\r
931     //is empty - just wait for the next header\r
932     if (len < HDRLEN && buffer->bufferEmpty()) {\r
933       log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten);\r
934       return cp;\r
935     }\r
936   }\r
937   while (bytesConsumed < len ) {\r
938     if (buffer->bufferFull()) {\r
939       //if this is the first part of the loop, try to write to the stream\r
940       if (bytesConsumed == 0) buffer->putInternal(wbuf,0,packetnum);\r
941       //if the buffer is full, no need to continue\r
942       if (buffer->bufferFull()) break;\r
943     }\r
944     //either we are in a packet (buffer != full && buffer != empty)\r
945     //or we are searching a header\r
946     if (buffer->bufferEmpty()) {\r
947       if (len-bytesConsumed < HDRLEN) {\r
948         // we cannot  still search\r
949         if (tmpFill != 0) {\r
950           //we are already working at the buffer\r
951           break;\r
952         }\r
953         memcpy(tmpBuffer,wbuf,len-bytesConsumed);\r
954         tmpFill=len-bytesConsumed;\r
955         log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten);\r
956         return len;\r
957       }\r
958 \r
959       int lastFramelen=hdrFramelen;\r
960       //if the header has been valid before and we are searching\r
961       //it should be here\r
962       int maxsearch=((len-bytesConsumed) < (int)PACKET_SIZE)?len-bytesConsumed:(int)PACKET_SIZE;\r
963       hdr=findHeader(wbuf,maxsearch);\r
964       if (hdr != NULL) {\r
965         //log->log("DemuxerAudio",Log::DEBUG,"header found at offset %d",hdr-wbuf);\r
966         }\r
967       //hdr now points to the new header\r
968       if (hdr !=  wbuf) {\r
969         //still at least bytes before the header\r
970         inSync=false;\r
971         outOfSync++;\r
972         int garbageBytes=(hdr!=NULL)?(hdr-wbuf):maxsearch;\r
973         //we consider the garbage now as an own packet\r
974         //we can consume at most our buffer\r
975         //basically the buffer should be empty here (we are\r
976         //in header search - and we fill up each garbage\r
977 \r
978         int nframesize=garbageBytes;\r
979         if (nframesize > (int)PACKET_SIZE) {\r
980           nframesize=(int)PACKET_SIZE;\r
981         }\r
982         if (! buffer->bufferEmpty()) {\r
983            log->log("DemuxerAudio",Log::WARN,"buffer not empty when having garbage to store");\r
984            //at the end no big problem, we only write the remaining bytes that we have...\r
985         }\r
986         else {\r
987           buffer->setFramelen(nframesize);\r
988         }\r
989         log->log("DemuxerAudio",Log::DEBUG,"garbage found at packet %ld (bytes %ld) of length %d, "\r
990             "framelen set to %d (last fl=%d)",\r
991             readHeaders,globalBytesWritten,garbageBytes,buffer->getFramelen(),lastFramelen);\r
992 #ifndef WIN32        \r
993         //hmm - we assume that he low level driver is more intelligent\r
994         //and give him the data "as is"\r
995         int written=buffer->putInternal(wbuf,garbageBytes,packetnum);\r
996         globalBytesWritten+=written;\r
997         bytesConsumed+=written;\r
998         if (written != garbageBytes || hdr == NULL ) {\r
999           break;\r
1000         }\r
1001 #else //DS is not intelligent\r
1002         globalBytesWritten+=garbageBytes;\r
1003         bytesConsumed+=garbageBytes;\r
1004 #endif\r
1005         //OK either all the "garbage" is written  or\r
1006         //we found the next header as expected\r
1007         //continue with the next package\r
1008         wbuf=hdr;\r
1009       }\r
1010       //we have to wait now until the buffer is \r
1011       //free for the next package\r
1012       if ( ! buffer->bufferEmpty()) return bytesConsumed;\r
1013       //this is the place where we start a new packet\r
1014       framelen=hdrFramelen;\r
1015       if ( !buffer->setFramelen(framelen) ) {\r
1016         log->log("DemuxerAudio",Log::DEBUG,"unable to set framelen should=%d, current=%d",\r
1017             framelen,buffer->getFramelen());\r
1018             }\r
1019       inSync=true;\r
1020     }\r
1021     //now we are surely within a packet\r
1022     int written=buffer->putInternal(wbuf,len-bytesConsumed,packetnum);\r
1023     //update the status\r
1024     globalBytesWritten+=written;\r
1025     wbuf+=written;\r
1026     bytesConsumed+=written;\r
1027     if (written == 0) {\r
1028       //stream is full\r
1029       break;\r
1030     }\r
1031     //OK - handle the rest\r
1032   }\r
1033   if (bytesConsumed >= oldBytes) {\r
1034     //if we consumed more than the old bytes - OK the buffer\r
1035     //can be thrown away\r
1036     tmpFill=0;\r
1037     return bytesConsumed-oldBytes;\r
1038   }\r
1039   else {\r
1040     //only consumed bytes from the buffer\r
1041     if (bytesConsumed == 0) {\r
1042       //restore the buffer\r
1043       tmpFill=oldBytes;\r
1044       return 0;\r
1045     }\r
1046     //shift bytes in buffer\r
1047     for (int i=0;i<oldBytes-bytesConsumed;i++) {\r
1048       tmpBuffer[i]=tmpBuffer[i+bytesConsumed];\r
1049     }\r
1050     tmpFill=oldBytes-bytesConsumed;\r
1051     return 0;\r
1052   }\r
1053 }\r
1054 \r
1055 //info functions\r
1056 const id3_tag * DemuxerAudio::getId3Tag() {\r
1057   return id3;\r
1058 \r
1059 }\r
1060 const DemuxerAudio::mpegInfo * DemuxerAudio::getMpegInfo() {\r
1061   if (! hasHdrInfo) return NULL;\r
1062         info->avrBitrate=avrBitrate;\r
1063         info->bitRate=hdrBitrate;\r
1064   return info;\r
1065 }\r
1066 \r
1067 const DemuxerAudio::vbrInfo * DemuxerAudio::getVBRINfo() {\r
1068         return vbr;\r
1069 }\r
1070 \r
1071 int DemuxerAudio::checkStart(UCHAR *b, int len) {\r
1072   UCHAR *start=b;\r
1073   int id3len=parseID3V2(b,len);\r
1074   if (id3len > 0) {\r
1075     char * str=id3->toString();\r
1076     log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 %d bytes of %d",id3len,len);\r
1077     log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 found:%s",str);\r
1078     delete str;\r
1079     b+=id3len;\r
1080     len-=id3len;\r
1081   }\r
1082         int vbrlen=parseVBR(b,len);\r
1083         if (vbrlen > 0 ) {\r
1084                 hasVBRInfo=true;\r
1085                 b+=vbrlen;\r
1086                 len-=vbrlen;\r
1087         }\r
1088   UCHAR * hdr=findHeader(b,len,true);\r
1089   if (hdr == NULL) return -1;\r
1090   return hdr-start;\r
1091 }\r
1092 \r
1093 int DemuxerAudio::checkID3(UCHAR *b, int len) {\r
1094   if (len != 128) return -1;\r
1095   int rt=parseID3V1(b,len);\r
1096   if (rt >= 0) {\r
1097     char * str=id3->toString();\r
1098     log->log("DemuxerAudio",Log::DEBUG,"parseID3V1 found:%s",str);\r
1099     delete str;\r
1100   }\r
1101   return rt;\r
1102 }\r
1103 \r
1104 bool DemuxerAudio::isSync() {\r
1105   return inSync;\r
1106 }\r
1107 \r
1108 UINT DemuxerAudio::getSyncErrors() {\r
1109   return outOfSync;\r
1110 }\r
1111 \r
1112 ULONG DemuxerAudio::getBytesPerSecond()\r
1113 {\r
1114   ULONG bps=hdrBitrate;\r
1115   if (! hasHdrInfo) return 0;\r
1116   if (hdrBitrate != avrBitrate) {\r
1117     //we seem to have vbr\r
1118     if (hasVBRInfo) {\r
1119       //vbr stuff TODO\r
1120       bps=avrBitrate;\r
1121     }\r
1122     else {\r
1123       //some guessing\r
1124       bps=avrBitrate;\r
1125     }\r
1126   }\r
1127   bps=bps/8;\r
1128   return bps;\r
1129 }\r
1130 \r
1131 ULONG DemuxerAudio::getSecondsFromLen(ULONG len) {\r
1132   if (! hasHdrInfo) return 0;\r
1133         if (vbr) {\r
1134                 //first find the index where we are between\r
1135                 //rough starting point:\r
1136                 ULONG idx=100*len/vbr->numBytes;\r
1137                 if (idx >= 100) idx=99;\r
1138                 ULONG idxPos=(vbr->table[idx]) * vbr->numBytes/256;\r
1139                 ULONG pbefore=idxPos;\r
1140                 ULONG pafter=idxPos;\r
1141                 //OK now we know whether we have to go up or down\r
1142                 if (idxPos > len) {\r
1143                         //down\r
1144                         while (idxPos > len && idx > 0) {\r
1145                                 pafter=idxPos;\r
1146                                 idx--;\r
1147                                 idxPos=(vbr->table[idx]) * vbr->numBytes/256;\r
1148                                 pbefore=idxPos;\r
1149                         }\r
1150                         //OK we are now before our postion\r
1151                 }\r
1152                 else {\r
1153                         //up\r
1154                         while (idxPos < len && idx < 100 ) {\r
1155                                 pbefore=idxPos;\r
1156                                 idx++;\r
1157                                 idxPos=(vbr->table[idx]) * vbr->numBytes/256;\r
1158                                 pafter=idxPos;\r
1159                         }\r
1160                         //after our position\r
1161                         if (idx > 0) idx --;\r
1162                 }\r
1163                 //idx is now the index before our position\r
1164                 //approximate between the 2 points\r
1165                 ULONG idxTime=idx * vbr->fileSeconds/100;\r
1166                 if (pafter == pbefore) return idxTime;\r
1167                 ULONG rt=idxTime+ (len-pbefore)* vbr->fileSeconds/(100 * (pafter-pbefore)) ;\r
1168                 if (rt > vbr -> fileSeconds) return vbr->fileSeconds;\r
1169                 if (rt < idxTime) return idxTime;\r
1170                 return rt;\r
1171         }\r
1172         else {\r
1173                 ULONG bps=getBytesPerSecond();\r
1174                 if (bps == 0) return 0;\r
1175                 return len/bps;\r
1176         }\r
1177 }\r
1178   \r
1179 ULONG DemuxerAudio::positionFromSeconds(ULONG seconds) {\r
1180   if (! hasHdrInfo) return 0;\r
1181         if (vbr) {\r
1182                 ULONG idx=seconds*100/vbr->fileSeconds;\r
1183                 if (idx > 99) idx=99;\r
1184                 ULONG idxPos=vbr->table[idx] * vbr->numBytes/256;\r
1185                 ULONG idx2=idx;\r
1186                 if (idx < 99) idx2++;\r
1187                 ULONG nextPos=vbr->table[idx] * vbr->numBytes/256;\r
1188                 ULONG rt=idxPos+(nextPos-idxPos) * (seconds - idx * vbr->fileSeconds /256);\r
1189                 if (rt < idxPos) return idxPos;\r
1190                 if ( rt > vbr->numBytes) return vbr->numBytes;\r
1191                 return rt;\r
1192         }\r
1193         else {\r
1194                 ULONG bps=getBytesPerSecond();\r
1195                 return bps*seconds;\r
1196         }\r
1197 }\r
1198 \r
1199 int id3_tag::stringlen(bool withTitle) const {\r
1200         if (!withTitle)\r
1201    return strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+\r
1202     strlen(track)+strlen(composer)+strlen(comment)+8+3+\r
1203     90; //30 chars for each name...\r
1204         else\r
1205    return strlen(title)+strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+\r
1206     strlen(track)+strlen(composer)+strlen(comment)+8+3+\r
1207     120; //30 chars for each name...\r
1208 }\r
1209 //create a string out of the id3 tag info\r
1210 //delete this string afterwards if you did not provide the buffer\r
1211 char * id3_tag::toString(char *b, int len, bool withTitle) const {\r
1212   const char *ta=tr("Artist");\r
1213 //char *tg=tr("Genre");\r
1214 //char *ty=tr("Year");\r
1215   const char *tx=tr("Album");\r
1216 //char *to=tr("Composer");\r
1217 //char *tc=tr("Comment");\r
1218   const char *tn=tr("Track");\r
1219   /* in the moment:\r
1220      Title: \r
1221      Artist:\r
1222      Album: year - name\r
1223      Track:\r
1224      */\r
1225   if (b == NULL) {\r
1226     len=stringlen(withTitle);\r
1227     b=new char[len];\r
1228   }\r
1229         const char * del=" - ";\r
1230         if (strlen(year) == 0) del="";\r
1231         if (withTitle){\r
1232  const char *tt=tr("Title");\r
1233                 SNPRINTF(b,len-1,"%s: %s\n%s: %s\n%s: %s%s%s\n%s: %s",\r
1234                                 tt,title,ta,artist,tx,year,del,album,tn,track);\r
1235         }\r
1236         else {\r
1237 SNPRINTF(b,len-1,"%s: %s\n%s: %s%s%s\n%s: %s",\r
1238                                 ta,artist,tx,year,del,album,tn,track);\r
1239         }\r
1240   b[len-1]=0;\r
1241   return b;\r
1242 }\r
1243 \r
1244 void DemuxerAudio::setSkipFactor(int factor) {\r
1245         Log::getInstance()->log("DemuxerAudio",Log::DEBUG,"set skipfactor %d\n",factor);\r
1246         buffer->setSkipFactor(factor);\r
1247 }\r
1248 \r