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