2 Copyright 2006 Mark Calderbank, Andreas Vogel
\r
4 This file is part of VOMP.
\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
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
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
21 #include "demuxeraudio.h"
\r
26 #define HDRBYTE1 0xff
\r
27 #define HDRBYTE2 0xe0
\r
28 #define HDRBYTE2MASK 0xe0
\r
32 class PacketBuffer {
\r
35 PacketBuffer(Stream *as,UCHAR strtype) {
\r
36 log=Log::getInstance();
\r
41 //just handle the data (do not deal with headers)
\r
42 int putInternal(UCHAR* buf,int len,unsigned int &packetnum);
\r
46 framelen=DemuxerAudio::PACKET_SIZE;
\r
56 return (partPacket>=framelen);
\r
58 //can we write a new packet?
\r
59 bool bufferEmpty() {
\r
60 return partPacket==0;
\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
70 //how much bytes do we need to fill the packet?
\r
71 int bytesMissing() {
\r
72 return framelen-partPacket;
\r
77 void setSkipFactor(int factor) {
\r
82 void packetWritten() {
\r
84 //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written packet %ld l=%d, bytes %ld",numpackets,framelen,numbytes);
\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
104 DemuxerAudio::DemuxerAudio(int p_vID, int p_aID)
\r
108 log=Log::getInstance();
\r
110 streamtype=Audio::MP3;
\r
111 buffer=new PacketBuffer(&audiostream,streamtype);
\r
112 // buffer=new PacketBuffer(&teststream,streamtype);
\r
113 globalBytesWritten=0;
\r
121 DemuxerAudio::~DemuxerAudio() {
\r
123 if(info) delete info;
\r
124 if(id3) delete id3;
\r
125 if (vbr) delete vbr;
\r
128 void DemuxerAudio::flush()
\r
130 Demuxer::flushAudio();
\r
131 buffer->newStream();
\r
135 void DemuxerAudio::reset() {
\r
136 buffer->newStream();
\r
141 globalBytesWritten=0;
\r
142 if (id3) delete id3;
\r
144 if (info) delete info;
\r
146 if (vbr) delete vbr;
\r
153 hdrSamplingRate=44100;
\r
159 int DemuxerAudio::scan(UCHAR *buf, int len)
\r
161 //no differend pids here
\r
165 void DemuxerAudio::setVID(int p_vID)
\r
169 void DemuxerAudio::setAID(int p_aID,int type)
\r
173 static const char * id3_1_genre[] = {
\r
220 "Instrumental Pop",
\r
221 "Instrumental Rock",
\r
225 "Techno-Industrial",
\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
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
284 static const char * mpegString(UCHAR code) {
\r
298 static const char * layerString(UCHAR code) {
\r
301 return "Layer reserved";
\r
303 return "Layer III";
\r
309 return "Layer UNKNOWN";
\r
312 * parse an id3 Header
\r
313 * provided by Brian Walton
\r
314 * @returns -1 of nothing found
\r
317 int DemuxerAudio::id3_2_3_FrameParse(unsigned char buf[], id3_frame *frame)
\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
336 * parse an id3 Header
\r
337 * provided by Brian Walton
\r
338 * @returns -1 of nothing found
\r
341 int DemuxerAudio::id3_2_2_FrameParse(unsigned char buf[], id3_frame *frame)
\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
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
358 {"TIT2",1}, //title
\r
359 {"TPE1",2}, //artist
\r
360 {"TCON",3}, //genre
\r
361 {"TRCK",6}, //track
\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
374 {"TCO",3 }, //(genreNumber)
\r
380 #define NUMKNOWN (sizeof(knownFrames)/sizeof(knownFrames[0]))
\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
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
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
400 if (tagIndex < 0) return false;
\r
401 UCHAR encoding=*(frameData+dataOffset);
\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
411 strncpy(tag->title,(char*)(frameData+dataOffset),MAXLEN(title));
\r
412 tag->title[MAXLEN(title)]=0;
\r
415 strncpy(tag->artist,(char*)(frameData+dataOffset),MAXLEN(artist));
\r
416 tag->artist[MAXLEN(artist)]=0;
\r
420 UCHAR * st=frameData+dataOffset;
\r
423 genre=atoi((const char *)(st+1)) && 31;
\r
424 st=(UCHAR *)id3_1_genre[genre];
\r
426 strncpy(tag->genre,(char*)st,MAXLEN(genre));
\r
427 tag->genre[MAXLEN(genre)]=0;
\r
431 strncpy(tag->year,(char *)(frameData+dataOffset),MAXLEN(year));
\r
432 tag->year[MAXLEN(year)]=0;
\r
435 strncpy(tag->album,(char *)(frameData+dataOffset),MAXLEN(album));
\r
436 tag->album[MAXLEN(album)]=0;
\r
439 strncpy(tag->track,(char *)(frameData+dataOffset),MAXLEN(track));
\r
440 tag->track[MAXLEN(track)]=0;
\r
443 strncpy(tag->composer,(char *)(frameData+dataOffset),MAXLEN(composer));
\r
444 tag->composer[MAXLEN(composer)]=0;
\r
447 strncpy(tag->comment,(char *)(frameData+dataOffset),MAXLEN(comment));
\r
448 tag->comment[MAXLEN(comment)]=0;
\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
464 int DemuxerAudio::parseID3V2(UCHAR *data, int len) {
\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
476 if(data[0]=='I' && data[1]=='D' && data[2]=='3')
\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
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
498 if (id3header.flags.extended_header)
\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
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
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
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
524 //skip over some padding - found this in lame MCDI tag...
\r
530 switch(id3header.major)
\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
538 if (id3_2_2_FrameParse(data, &id3frame) < 0)
\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
548 case 3: //ID3 V2.3.x
\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
556 if (id3_2_3_FrameParse(data, &id3frame) <0)
\r
560 if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame size: %d\n", id3frame.size);
\r
562 if (id3frame.flags.groupId)
\r
565 if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame group: %d\n", data[dataOffset]);
\r
567 if (id3frame.flags.compression)
\r
569 if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame compressed: %d\n", id3frame.flags.compression);
\r
571 if (id3frame.flags.encryption)
\r
574 if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame encryption method: %d\n", data[dataOffset]);
\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
583 //don't support this version
\r
590 //store the found tag
\r
591 if (id3) delete id3;
\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
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
606 int DemuxerAudio::parseID3V1(UCHAR *data, int len) {
\r
608 if (len < 128) return -1;
\r
609 if(data[0]=='T' && data[1]=='A' && data[2]=='G')
\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
617 if (data[125]==0 && data[126]!=0)
\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
623 if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.0 tag\n");
\r
624 MEMCPY(comment,30,97);
\r
626 if (data[127] < sizeof(id3_1_genre)/sizeof(id3_1_genre[0]))
\r
628 sprintf(tag->genre, id3_1_genre[data[127]]);
\r
630 if (id3) delete id3;
\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
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
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
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
665 int framedata=vbridpos+strlen(VBRHDR);
\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
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
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
684 if (vbr) delete vbr;
\r
686 vbr->numFrames=data[framedata] << 24 | data[framedata+1]<<16|
\r
687 data[framedata+2]<<8 |data[framedata+3];
\r
689 vbr->numBytes=data[framedata] << 24 | data[framedata+1]<<16|
\r
690 data[framedata+2]<<8 |data[framedata+3];
\r
692 for (int ti=0;ti<100;ti++) {
\r
693 vbr->table[ti]=data[framedata+ti];
\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
712 UCHAR * DemuxerAudio::findHeader(UCHAR *buf, int len, bool writeInfo) {
\r
713 while (len >= 3) //assume hdr+crc
\r
715 UCHAR pattern=*buf;
\r
717 if (pattern != HDRBYTE1 ) continue;
\r
718 if ((*buf & HDRBYTE2MASK) != HDRBYTE2) continue;
\r
719 if (readHeader((buf-1),4,writeInfo) != 0) continue;
\r
727 int DemuxerAudio::readHeader(UCHAR * hbuf,int len,bool writeInfo) {
\r
730 int curSamplingRate=0;
\r
731 if (*hbuf != HDRBYTE1) return -1;
\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
740 UCHAR layer=(*hbuf & 0x06) >>1;
\r
741 //bool hasCRC=!(*hbuf & 1);
\r
743 UCHAR bitrateCode=(*hbuf & 0xf0) >>4;
\r
744 UCHAR samplingCode=(*hbuf & 0x0c) >> 2;
\r
745 bool padding=*hbuf & 0x02;
\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
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
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
773 if (layer == 3) padbytes=4;
\r
778 //FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4
\r
779 curFramelen=(12*curBitrate/curSamplingRate+padbytes) * 4;
\r
783 //FrameLengthInBytes = 144 * BitRate / SampleRate + Padding
\r
784 curFramelen=144*curBitrate/curSamplingRate+padbytes;
\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
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
806 case 1: chmodStr=tr("JointStereo");break;
\r
807 case 2: chmodStr=tr("Dual");break;
\r
808 case 3: chmodStr=tr("Mono");break;
\r
810 SNPRINTF(info->info,sizeof(info->info)-1,"%s",chmodStr);
\r
812 if (isStarting) avrBitrate=curBitrate;
\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
824 int DemuxerAudio::findPTS(UCHAR* buf, int len, ULLONG* dest)
\r
826 //we have no PTS number ...
\r
828 return (findHeader(buf,len) != NULL)?1:0;
\r
831 bool PacketBuffer::doSkip() {
\r
832 if (!bufferFull()) return false;
\r
833 if (skipfactor == 0) return false;
\r
835 if (numskip >= skipfactor) {
\r
836 //sent at least always 2 packets
\r
837 if (numskip > skipfactor) numskip=0;
\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
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
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
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
860 if (bufferFull()) {
\r
862 if (doSkip()) return 0;//NoSkip on Windows
\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
873 //let the demuxer come back with the rest - need to check header first
\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
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
893 //let the player come back...
\r
894 return bytesConsumed;
\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
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
915 int DemuxerAudio::put(UCHAR* wbuf, int len)
\r
917 //return audiostream.put(wbuf,len,streamtype);
\r
918 int framelen=PACKET_SIZE;
\r
920 int bytesConsumed=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
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
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
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
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
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
965 //log->log("DemuxerAudio",Log::DEBUG,"header found at offset %d",hdr-wbuf);
\r
967 //hdr now points to the new header
\r
969 //still at least bytes before the header
\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
978 int nframesize=garbageBytes;
\r
979 if (nframesize > (int)PACKET_SIZE) {
\r
980 nframesize=(int)PACKET_SIZE;
\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
987 buffer->setFramelen(nframesize);
\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
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
1001 #else //DS is not intelligent
\r
1002 globalBytesWritten+=garbageBytes;
\r
1003 bytesConsumed+=garbageBytes;
\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
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
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
1026 bytesConsumed+=written;
\r
1027 if (written == 0) {
\r
1031 //OK - handle the rest
\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
1037 return bytesConsumed-oldBytes;
\r
1040 //only consumed bytes from the buffer
\r
1041 if (bytesConsumed == 0) {
\r
1042 //restore the buffer
\r
1046 //shift bytes in buffer
\r
1047 for (int i=0;i<oldBytes-bytesConsumed;i++) {
\r
1048 tmpBuffer[i]=tmpBuffer[i+bytesConsumed];
\r
1050 tmpFill=oldBytes-bytesConsumed;
\r
1056 const id3_tag * DemuxerAudio::getId3Tag() {
\r
1060 const DemuxerAudio::mpegInfo * DemuxerAudio::getMpegInfo() {
\r
1061 if (! hasHdrInfo) return NULL;
\r
1062 info->avrBitrate=avrBitrate;
\r
1063 info->bitRate=hdrBitrate;
\r
1067 const DemuxerAudio::vbrInfo * DemuxerAudio::getVBRINfo() {
\r
1071 int DemuxerAudio::checkStart(UCHAR *b, int len) {
\r
1073 int id3len=parseID3V2(b,len);
\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
1082 int vbrlen=parseVBR(b,len);
\r
1083 if (vbrlen > 0 ) {
\r
1088 UCHAR * hdr=findHeader(b,len,true);
\r
1089 if (hdr == NULL) return -1;
\r
1093 int DemuxerAudio::checkID3(UCHAR *b, int len) {
\r
1094 if (len != 128) return -1;
\r
1095 int rt=parseID3V1(b,len);
\r
1097 char * str=id3->toString();
\r
1098 log->log("DemuxerAudio",Log::DEBUG,"parseID3V1 found:%s",str);
\r
1104 bool DemuxerAudio::isSync() {
\r
1108 UINT DemuxerAudio::getSyncErrors() {
\r
1112 ULONG DemuxerAudio::getBytesPerSecond()
\r
1114 ULONG bps=hdrBitrate;
\r
1115 if (! hasHdrInfo) return 0;
\r
1116 if (hdrBitrate != avrBitrate) {
\r
1117 //we seem to have vbr
\r
1131 ULONG DemuxerAudio::getSecondsFromLen(ULONG len) {
\r
1132 if (! hasHdrInfo) return 0;
\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
1144 while (idxPos > len && idx > 0) {
\r
1147 idxPos=(vbr->table[idx]) * vbr->numBytes/256;
\r
1150 //OK we are now before our postion
\r
1154 while (idxPos < len && idx < 100 ) {
\r
1157 idxPos=(vbr->table[idx]) * vbr->numBytes/256;
\r
1160 //after our position
\r
1161 if (idx > 0) idx --;
\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
1173 ULONG bps=getBytesPerSecond();
\r
1174 if (bps == 0) return 0;
\r
1179 ULONG DemuxerAudio::positionFromSeconds(ULONG seconds) {
\r
1180 if (! hasHdrInfo) return 0;
\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
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
1194 ULONG bps=getBytesPerSecond();
\r
1195 return bps*seconds;
\r
1199 int id3_tag::stringlen(bool withTitle) const {
\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
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
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
1222 Album: year - name
\r
1226 len=stringlen(withTitle);
\r
1229 const char * del=" - ";
\r
1230 if (strlen(year) == 0) del="";
\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
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
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