]> git.vomp.tv Git - vompclient-marten.git/blob - audioplayer.cc
Live radio prebuffering display - code upgrades for connection lost handling
[vompclient-marten.git] / audioplayer.cc
1 /*
2     Copyright 2004-2006 Chris Tallon, Andreas Vogel
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "audioplayer.h"
22 #include "vaudioplayer.h"
23 #include "demuxeraudio.h"
24 #include "timers.h"
25 #include "video.h"
26 #include "command.h"
27 #include "i18n.h"
28 #include "boxx.h"
29
30 AudioPlayer * AudioPlayer::instance=NULL;
31
32 AudioPlayer * AudioPlayer::getInstance(Boxx * parent,bool create) {
33 //      Log::getInstance()->log("AudioPlayer",Log::DEBUG,"getInstance for view %p, instance=%p",parent,instance);
34         AudioPlayer *np=instance;
35         if (! np && ! create) return NULL;
36         if (! np) {
37                 np=new AudioPlayer(parent);
38                 np->run();
39                 instance=np;
40         }
41         instance->threadLock();
42         instance->frontend=parent;
43         instance->threadUnlock();
44         return instance;
45 }
46
47 AudioPlayer::AudioPlayer(Boxx *parent) : afeed(this)
48 {
49         frontend=parent;
50   audio = Audio::getInstance();
51   logger = Log::getInstance();
52   vdr = VDR::getInstance();
53   logger->log("AudioPlayer", Log::DEBUG, "Audio player ctorI");
54   running=true;
55         playerRunnig=false;
56   streampos = 0;
57         bytesWritten=0;
58   state = S_STOP;
59         requestState=S_STOP;
60         feederState=FEEDER_STOP;
61
62   threadBuffer = NULL;
63   
64         Video::getInstance()->turnVideoOff();
65         requestedSequence=0;
66         sequence=0;
67         playSequence=0;
68         currentPlaySequence=-1;
69         filename=NULL;
70         demuxer=new DemuxerAudio();
71   logger->log("AudioPlayer", Log::DEBUG, "Audio player ctorII");
72         if (!demuxer->init(this, audio, NULL, 0, DemuxerAudio::PACKET_SIZE+200))
73   {
74     logger->log("AudioPlayer", Log::ERR, "Demuxer failed to init");
75     shutdown();
76     return ;
77   }
78   logger->log("AudioPlayer", Log::DEBUG, "Audio player ctorIII");
79   afeed.init();
80         audio->reset();
81         lenInBytes=0;
82   logger->log("AudioPlayer", Log::DEBUG, "Audio player created");
83 }
84
85 AudioPlayer::~AudioPlayer()
86 {
87   if (threadBuffer) free(threadBuffer);
88         Timers::getInstance()->cancelTimer(this,1);
89         controlFeeder(FEEDER_STOP);
90         audio->reset();
91         audio->setStreamType(Audio::MPEG2_PES);
92         delete demuxer;
93         demuxer=NULL;
94         delete filename;
95 }
96
97 void AudioPlayer::controlFeeder(int feederAction) {
98         logger->log("AudioPlayer",Log::DEBUG,"control feeder old=%d, new=%d",feederState,feederAction);
99         switch (feederAction) {
100                 case FEEDER_START:
101                 case FEEDER_UNPAUSE:
102                         if (feederState == FEEDER_STOP) 
103                                 afeed.start();
104                         afeed.enable();
105                         break;
106                 case FEEDER_STOP:
107                         if (feederState != FEEDER_STOP)
108                                 afeed.stop();
109                         break;
110                 case FEEDER_PAUSE:
111                         afeed.disable();
112                         break;
113         }
114         feederState=feederAction;
115 }
116
117
118 void AudioPlayer::run() {
119         if (playerRunnig) return;
120         playerRunnig=true;
121         threadStart();
122 }
123
124
125 void AudioPlayer::shutdown()
126 {
127         running=false;
128         if (playerRunnig) {
129                 threadSignalNoLock();
130                 //wait for the player thread to stop
131                 logger->log("AudioPlayer",Log::DEBUG,"shutdown - warting for player thread to stop");
132                 //at most wait 10s
133                 for (int loopcount=200;playerRunnig && loopcount > 0;loopcount--) {
134                         MILLISLEEP(50);
135                 }
136                 if (playerRunnig) {
137                   logger->log("AudioPlayer",Log::ERR,"shutdown - unable to stop player within 10s");
138                 }
139         }
140         instance=NULL;
141         delete this;
142 }
143
144 bool AudioPlayer::isPlayerRunning() {
145         return playerRunnig;
146 }
147
148 int AudioPlayer::setRequestedState(UCHAR rstate) {
149         int rt=0;
150         threadLock();
151         requestState=rstate;
152         requestedSequence++;
153         rt=requestedSequence;
154         threadUnlock();
155         return rt;
156 }
157
158
159 UCHAR AudioPlayer::getState() {
160         UCHAR rt=0;
161         threadLock();
162         rt=state;
163         threadUnlock();
164         return rt;
165 }
166
167 void AudioPlayer::setState(UCHAR s) {
168         threadLock();
169         state=s;
170         threadUnlock();
171 }
172
173 //------------------- externally called functions --------------------------
174 int AudioPlayer::play(const char * fn)
175 {
176         logger->log("AudioPlayer", Log::DEBUG, "play request for %s", fn);
177         int rt=0;
178         threadLock();
179         if (filename) delete filename;
180         filename=new char[strlen(fn)+1];
181         strcpy(filename,fn);
182         requestState=S_PLAY;
183         requestedSequence++;
184         rt=requestedSequence;
185         playSequence++;
186         threadUnlock();
187         threadSignalNoLock();
188         return rt;
189 }
190
191 int AudioPlayer::stop()
192 {
193         int rt= setRequestedState(S_STOP);
194         threadSignalNoLock();
195         return rt;
196 }
197
198 int AudioPlayer::pause()
199 {
200         int rt= setRequestedState(S_PAUSE);
201         threadSignalNoLock();
202         return rt;
203 }
204 int AudioPlayer::unpause()
205 {
206         int rt= setRequestedState(S_PLAY);
207         threadSignalNoLock();
208         return rt;
209 }
210
211 int AudioPlayer::fastForward(){
212         int rt=setRequestedState(S_FF);
213         threadSignalNoLock();
214         return rt;
215 }
216
217 int AudioPlayer::fastBackward(){
218         int rt=setRequestedState(S_BACK);
219         threadSignalNoLock();
220         return rt;
221 }
222 int AudioPlayer::jumpToPercent(double percent){
223         threadLock();
224         ULONG fsec=demuxer->getSecondsFromLen(lenInBytes);
225         ULONG npos=streampos;
226         if (fsec != 0) {
227                 fsec=(ULONG)(((double)fsec*percent)/(double)100);
228                 npos=demuxer->positionFromSeconds(fsec);
229                 logger->log("AudioPlayer",Log::DEBUG,"new pos %ld from demux",npos);
230         }
231         if (npos == 0) {
232                 //the demuxer cannot help us
233                 npos=(ULONG)(((double)lenInBytes*percent)/(double)100);
234                 logger->log("AudioPlayer",Log::DEBUG,"new pos %ld without demux",npos);
235         }
236         if (npos > lenInBytes) npos=lenInBytes-1;
237         requestedStreampos=npos;
238         requestState=S_POSITION;
239         requestedSequence++;
240         threadUnlock();
241         //no need to wait here...
242         return 0;
243 }
244
245 int AudioPlayer::skipForward(int seconds) {
246         threadLock();
247         ULONG curr=demuxer->getSecondsFromLen(streampos);
248         ULONG dest=demuxer->positionFromSeconds(curr+(UINT)seconds);
249         if (dest != 0) {
250                 logger->log("AudioPlayer",Log::DEBUG,"new pos %ld  skip %ds",dest,seconds);
251                 requestedStreampos=dest;
252         }
253         requestState=S_POSITION;
254         requestedSequence++;
255         threadUnlock();
256         return 0;
257 }
258 int AudioPlayer::skipBackward(int seconds) {
259         threadLock();
260         ULONG curr=demuxer->getSecondsFromLen(streampos);
261         if (curr > (UINT)seconds) {
262                 ULONG dest=demuxer->positionFromSeconds(curr-(UINT)seconds);
263                 if (dest != 0) {
264                         logger->log("AudioPlayer",Log::DEBUG,"new pos %ld  skip %ds",dest,seconds);
265                         requestedStreampos=dest;
266                         requestState=S_POSITION;
267                         requestedSequence++;
268                 }
269         }
270         threadUnlock();
271         return 0;
272 }
273
274
275
276 // ----------------------------------- Internal functions
277
278
279 void AudioPlayer::sendFrontendMessage(ULONG para)
280 {
281   logger->log("AudioPlayer", Log::DEBUG, "sending frontend message %ld",para);
282   Message* m = new Message();
283         threadLock();
284   m->to = frontend;
285         threadUnlock();
286   m->from = this;
287   m->message = Message::PLAYER_EVENT;
288   m->parameter = para;
289         Command::getInstance()->postMessageFromOuterSpace(m);
290 }
291
292 void AudioPlayer::handleVDRerror(){
293                         if (!vdr->isConnected())
294                         {
295                                 logger->log("AudioPlayer", Log::ERR, "disconnect");
296                                 sendFrontendMessage(CONNECTION_LOST);
297                                 setState(S_ERROR);
298                         }
299 }
300 //open a new file
301 //called within the thread!
302 int AudioPlayer::openFile() {
303           char * fn=NULL;
304                 threadLock();
305                 if (filename) {
306                 fn=new char[strlen(filename)+1];
307                 strcpy(fn,filename);
308                 }
309                 threadUnlock();
310     demuxer->reset();
311           streampos=0;
312                 bytesWritten=0;
313     lenInBytes=vdr->loadImage(fn,0,0);
314     Log::getInstance()->log("Audioplayer", Log::DEBUG, "request file rt=%d file=%s",lenInBytes,fn);
315     handleVDRerror();
316                 if (lenInBytes <= 0) {
317                         return 1;
318                 }
319     UINT rsize=0;
320     UCHAR *idbuf=vdr->getImageBlock(0,demuxer->headerBytes(),&rsize);
321     handleVDRerror();
322     if (rsize < demuxer->headerBytes() || idbuf == NULL) {
323       if (idbuf) free(idbuf);
324       Log::getInstance()->log("VAudioplayer", Log::DEBUG, "unable to get header for file %s",fn);
325       return 0;
326     }
327     threadLock();
328     int hdrpos=demuxer->checkStart(idbuf,rsize);
329     threadUnlock();
330     if (hdrpos >= 0) {
331       streampos=hdrpos;
332     }
333     if (idbuf) free(idbuf);
334     idbuf=NULL;
335     if (demuxer->getId3Tag() == NULL) {
336       //OK - look at the end
337       idbuf=vdr->getImageBlock(lenInBytes-demuxer->footerBytes(),demuxer->footerBytes(),&rsize);
338       handleVDRerror();
339       if (rsize < demuxer->footerBytes() || idbuf == NULL) {
340         if (idbuf) free(idbuf);
341         Log::getInstance()->log("VAudioplayer", Log::DEBUG, "unable to get footer for file %s",fn);
342         return 0;
343       }
344       threadLock();
345       hdrpos=demuxer->checkID3(idbuf,rsize);
346       threadUnlock();
347       if (hdrpos < 0) {
348         Log::getInstance()->log("VAudioplayer", Log::DEBUG, "no ID3 in footer for file %s",fn);
349       }
350       free(idbuf);
351     }
352                 return 0;
353 }
354 //method called by the playing thread to handle 
355 //"commands" by the frontend
356 UCHAR AudioPlayer::checkState()
357 {
358         threadLock();
359         UCHAR rstate=requestState;
360         UCHAR cstate=state;
361         int rseq=requestedSequence;
362         int cseq=sequence;
363         int fseq=playSequence;
364         threadUnlock();
365         //flag to decide which message to send
366         //to frontend
367         bool newFile=false;
368
369         if ( rseq > cseq) {
370                 logger->log("AudioPlayer", Log::DEBUG, "Switch state from %u to %u", cstate, rstate);
371                 switch(rstate)
372                 {
373                         case S_PAUSE: 
374                                 if (cstate != S_PLAY && cstate != S_FF) rstate=cstate; //ignore request
375                                 else {
376                                         skipfactor=0;
377                                         demuxer->setSkipFactor(0);
378                                   audio->mute();
379                                   audio->pause();
380                                 }
381                                 break;
382                         case S_PLAY: // to S_PLAY
383                                 skipfactor=0;
384                                 demuxer->setSkipFactor(0);
385                                 if (fseq != currentPlaySequence || cstate == S_STOP || cstate == S_ERROR || cstate == S_DONE) {
386                                         //this is a new play request interrupting the current
387                       logger->log("AudioPlayer", Log::DEBUG, "replay from start fseq=%d, startseq=%d", fseq, currentPlaySequence);
388                                         threadLock();
389                                         playSequence=fseq;
390                                         threadUnlock();
391                                         currentPlaySequence=fseq;
392                                         newFile=true;
393                                         if (cstate != S_DONE) audio->mute();
394                                         int rt=openFile();
395                                         if (rt != 0) {
396                                                 rstate=S_ERROR;
397                                         }
398                                         else {
399                                                 audio->unPause();
400                                                 if (cstate != S_DONE) {
401                                                         //flush only if we are not coming from stream end
402                                                         thisRead=0;
403                                                         thisWrite=0;
404                                                         if (threadBuffer) free(threadBuffer);
405                                                   threadBuffer=NULL;
406                                                         controlFeeder(FEEDER_STOP);
407                                                         demuxer->flush();
408                                                         controlFeeder(FEEDER_START);
409                                                         audio->reset();
410                                                         audio->systemMuteOff();
411                                                         audio->setStreamType(Audio::MP3); 
412                                                 }
413                                                 audio->unMute();
414                                                 audio->play();
415                                         }
416                                 }
417                                 else if (cstate == S_PAUSE){
418                                         newFile=true;
419                                         audio->unPause();
420                                         audio->unMute();
421                                 }
422                                 else if (cstate == S_FF || S_BACK) {
423                                         ;
424                                 }
425                                 else {
426                                         rstate=cstate;
427                                 }
428                                 break;
429                         case S_DONE:
430                                 Timers::getInstance()->setTimerD(this,1,4);
431                                 //inform the frontend
432                                 break;
433                         case S_FF:
434                                 if (cstate != S_PLAY && cstate != S_PAUSE && cstate != S_FF) {
435                                         rstate=cstate;
436                                 }
437                                 else {
438                                         if (skipfactor == 0) skipfactor=2;
439                                         else skipfactor=skipfactor<<1;
440                                         if (skipfactor > 16 ) skipfactor=2;
441                                         demuxer->setSkipFactor(skipfactor);
442                                 }
443                                 if (cstate == S_PAUSE) {
444                                         audio->unPause();
445                                         audio->unMute();
446                                 }
447                                 break;
448                         case S_POSITION:
449                                 if (cstate != S_PLAY && cstate != S_PAUSE) {
450                                         rstate=cstate;
451                                 }
452                                 else {
453                                         audio->mute();
454                                         audio->unPause();
455                                         controlFeeder(FEEDER_STOP);
456                                         demuxer->flush();
457                                         thisRead=0;
458                                         thisWrite=0;
459                                         if (threadBuffer) free(threadBuffer);
460                                         threadBuffer=NULL;
461                                         streampos=requestedStreampos;
462                                         bytesWritten=streampos;
463                                         audio->reset();
464                                         audio->setStreamType(Audio::MP3); 
465                                         controlFeeder(FEEDER_START);
466                                         audio->unMute();
467                                         audio->play();
468                                         rstate=S_PLAY;
469                                 }
470                                 break;
471                         default: // to S_STOP
472                                 rstate=S_STOP;
473                                 audio->mute();
474                                 audio->stop();
475                                 audio->unPause();
476                                 controlFeeder(FEEDER_STOP);
477                                 demuxer->flush();
478                                 thisRead=0;
479                                 thisWrite=0;
480                                 if (threadBuffer) free(threadBuffer);
481                                 threadBuffer=NULL;
482                     logger->log("AudioPlayer", Log::DEBUG, "stop handled fseq: %d startseq %d completed", playSequence, currentPlaySequence);
483                                 break;
484                 }
485                 threadLock();
486                 state=rstate;
487                 sequence=rseq;
488                 threadUnlock();
489                 if (newFile) sendFrontendMessage(NEW_SONG);
490                 else if (cstate != rstate && rstate != S_DONE ) {
491                         sendFrontendMessage(STATUS_CHANGE);
492                         //any change after done cancels the "done" timer
493                         Timers::getInstance()->cancelTimer(this,1);
494                 }
495                 logger->log("AudioPlayer", Log::DEBUG, "Switch state from %u to %u completed nf=%s", cstate, rstate,newFile?"true":"false");
496                 //we return the newly set state
497                 return rstate;
498         }
499         //rstate could be different but no new request - so return cstate
500         return cstate;
501 }
502
503
504
505
506 // ----------------------------------- Feed thread
507
508 void AudioPlayer::waitTimed(int ms) {
509         threadLock();
510         struct timespec nt;
511         int sec=ms/1000;
512         int us=1000*(ms - 1000*sec);
513 #ifndef WIN32
514         struct timeval ct;
515         gettimeofday(&ct,NULL);
516         nt.tv_sec=ct.tv_sec+sec;
517         nt.tv_nsec=1000*us+1000*ct.tv_usec;
518 #else 
519     DWORD ct=timeGetTime();
520     nt.tv_sec=ct/1000+sec;
521     nt.tv_nsec=1000*us+1000*ct*1000;
522 #endif
523         threadWaitForSignalTimed(&nt);
524         threadUnlock();
525 }
526
527 void AudioPlayer::threadMethod()
528 {
529         logger->log("AudioPlayer", Log::DEBUG, "player thread started");
530         thisWrite=0;
531         thisRead=0;
532   while(1)
533   {
534                 UCHAR cstate=checkState();
535     if (! running) {
536                         break;
537                 }
538                 if (cstate != S_PLAY && cstate != S_FF && cstate != S_BACK) {
539                         waitTimed(500);
540                         continue;
541                 }
542     threadCheckExit();
543
544                 if (thisWrite == thisRead) {
545                         //TODO: use normal blocks...
546                         thisRead=0;
547       thisWrite=0;
548                         threadBuffer = vdr->getImageBlock(streampos, BUFLEN , &thisRead);
549       handleVDRerror();
550                         if (!threadBuffer || thisRead == 0) {
551                                 //OK we count this as end of stream
552                                 //hmm --- we should be able to detect if the audio has gone...
553                                 logger->log("AudioPlayer", Log::DEBUG, "stream end");
554                                 setRequestedState(S_DONE);
555                                 continue;
556                         }
557                         //logger->log("AudioPlayer", Log::DEBUG, "read %ld bytes at pos %ld",thisRead,streampos);
558                         streampos+=thisRead;
559                 }
560     
561     threadCheckExit();
562                 /*
563     MediaPacket p;
564                 memset(&p,sizeof(p),0);
565                 p.pos_buffer=0;
566                 p.length=thisRead;
567     MediaPacketList pl;
568                 pl.push_back(p);
569                 audio->PrepareMediaSample(pl,0);
570                 UINT bytesWritten=0;
571                 UINT rt=audio->DeliverMediaSample(threadBuffer,&bytesWritten);
572                 ULONG written=thisRead;
573                 if (rt == 0)
574                    written=bytesWritten;
575                 */
576     ULONG written= demuxer->put(threadBuffer + thisWrite, thisRead - thisWrite);
577     thisWrite+=written;
578                 bytesWritten+=written;
579                 if (thisWrite < thisRead) {
580                         if (written == 0) {
581         // demuxer is full and can't take anymore
582                                 waitTimed(200);
583                         }
584       }
585                 else {
586       //logger->log("AudioPlayer", Log::DEBUG, "block written %d", thisWrite);
587                         thisWrite=0;
588                         thisRead=0;
589       free(threadBuffer);
590       threadBuffer = NULL;
591                 }
592
593   }
594
595   logger->log("AudioPlayer", Log::DEBUG, "finished");
596         playerRunnig=false;
597         return;
598 }
599
600 int AudioPlayer::waitForSequence(int timeout, int seq) {
601         time_t starttime=time(NULL)+timeout;
602         time_t curtime=0;
603   logger->log("AudioPlayer", Log::DEBUG, "waiting for sequence %d",seq);
604         while ((curtime=time(NULL)) < starttime) {
605                 int cseq=getSequence();
606                 if (cseq >= seq) return cseq;
607                 MILLISLEEP(10);
608         }
609         return -1;
610 }
611
612 int AudioPlayer::getSequence() {
613         int rt=0;
614         threadLock();
615         rt=sequence;
616         threadUnlock();
617         return rt;
618 }
619
620
621
622 void AudioPlayer::threadPostStopCleanup()
623 {
624   if (threadBuffer)
625   {
626     delete(threadBuffer);
627     threadBuffer = NULL;
628   }
629         playerRunnig=false;
630 }
631
632 void AudioPlayer::call(void *) {
633         threadSignalNoLock();
634 }
635
636 void AudioPlayer::timercall(int ref) {
637         if (ref == 1) {
638                 logger->log("AudioPlayer", Log::DEBUG, "stream end - informing frontend");
639                 sendFrontendMessage(STREAM_END);
640         }
641 }
642
643 //--------------------------- info functions -------------------
644
645 char * AudioPlayer::getTitle() {
646         logger->log("AudioPlayer", Log::DEBUG, "getTitle");
647         threadLock();
648         const id3_tag * tag=demuxer->getId3Tag();
649         const char * title=NULL;
650         char *rt=NULL;
651         if (tag != NULL) {
652                 title=tag->title;
653         }
654         if (title && strlen(title) != 0) {
655                 rt=new char[strlen(title)+1];
656                 strcpy(rt,title);
657                 rt[strlen(title)]=0;
658         }
659         //let the frontend fill in something
660         threadUnlock();
661         return rt;
662 }
663
664 char * AudioPlayer::getID3Info() {
665         logger->log("AudioPlayer", Log::DEBUG, "getID3Info");
666         threadLock();
667         int len=0;
668         const id3_tag * tag=demuxer->getId3Tag();
669         int taglen=0;
670         if (tag) taglen=tag->stringlen(false);
671         len+=taglen;
672         const DemuxerAudio::mpegInfo *info=demuxer->getMpegInfo();
673         if (info) len+=30;
674         char * rt=NULL;
675         if (len > 0) {
676                 char bitrateType='C';
677                 if (info && info->avrBitrate != info->bitRate) bitrateType='V';
678                 rt=new char[len];
679                 if (!tag && info) {
680                         SNPRINTF(rt,len-1,"%s: %s/L%d %cBR,SR=%dk,%s\n",tr("MpegInfo"),
681                                         info->mpegVersion,info->mpegLayer,bitrateType,info->sampleRate/1000,
682                                         info->info);
683                 }
684                 else if (tag && info){
685                         char *tmp=new char[taglen+1];
686                         SNPRINTF(rt,len-1,"%s\n"
687                                         "%s: %s/L%d %cBR,SR=%dk,%s\n",
688                                         tag->toString(tmp,taglen,false),
689                                         tr("MpegInfo"),
690                                         info->mpegVersion,info->mpegLayer,bitrateType,info->sampleRate/1000,
691                                         info->info);
692             delete [] tmp;
693                 }
694                 else if (tag && !info){
695                         char *tmp=new char[taglen+1];
696                         SNPRINTF(rt,len-1,"%s\n",
697                                         tag->toString(tmp,taglen,false));
698             delete [] tmp;
699                 }
700                 rt[len-1]=0;
701         }
702         threadUnlock();
703         logger->log("AudioPlayer", Log::DEBUG, "getID3Info returns %s",rt);
704         return rt;
705 }
706
707 ULONG AudioPlayer::getCurrentTimes(){
708         ULONG rt=0;
709         threadLock();
710         if (streampos != 0){
711           rt=demuxer->getSecondsFromLen(bytesWritten);
712           if (rt == 0) {
713                 //we can only guess
714                 rt= bytesWritten/DEFAULT_BITRATE;
715                 }
716         }
717         threadUnlock();
718         return rt;
719 }
720
721 ULONG AudioPlayer::getSonglen(){
722         ULONG rt=0;
723         threadLock();
724         const DemuxerAudio::vbrInfo * vbr=demuxer->getVBRINfo();
725         if (vbr) rt=vbr->fileSeconds;
726         else {
727                 if (lenInBytes != 0) {
728                         rt=demuxer->getSecondsFromLen(lenInBytes);
729                         if (rt == 0) {
730                                 //we can only guess
731                                 rt= lenInBytes/DEFAULT_BITRATE;
732                         }
733                 }
734         }
735         threadUnlock();
736         return rt;
737 }
738
739 int AudioPlayer::getCurrentBitrate(){
740         int rt=DEFAULT_BITRATE;
741         threadLock();
742         const DemuxerAudio::mpegInfo *info=demuxer->getMpegInfo();
743         if (info) rt=info->bitRate;
744         threadUnlock();
745         return rt;
746 }