]> git.vomp.tv Git - vompclient-marten.git/blob - vaudioplayer.cc
Client side timeouts
[vompclient-marten.git] / vaudioplayer.cc
1 /*
2     Copyright 2004-2005 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 <time.h>
22
23 #include "vaudioplayer.h"
24
25 #include "audioplayer.h"
26 #include "message.h"
27 #include "remote.h"
28 #include "wsymbol.h"
29 #include "boxstack.h"
30 #include "vdr.h"
31 #include "media.h"
32 #include "i18n.h"
33 #include "wjpeg.h"
34 #include "timers.h"
35 #include "video.h"
36 #include "command.h"
37
38 VAudioplayer::VAudioplayer(VMediaList *p)
39 {
40   parent=p;
41   Video* video = Video::getInstance();
42   setSize(video->getScreenWidth(),video->getScreenHeight());
43   createBuffer();
44   banner=NULL;
45   fullname=NULL;
46   filename=NULL;
47   ftime=0;
48   playall=false;
49   currentMedia=NULL;
50   justPlaying=false;
51   numentries=p->getNumEntries(MEDIA_TYPE_AUDIO);
52   bannerTime=0;
53  
54     barBlue.set(0, 0, 150, 150);
55
56 }
57
58 AudioPlayer * VAudioplayer::getPlayer(bool createIfNeeded)
59 {
60   AudioPlayer* rt=AudioPlayer::getInstance(this,false);
61   if (! createIfNeeded && rt == NULL) return NULL;
62   if (rt == NULL) {
63     rt=AudioPlayer::getInstance(this);
64     rt->run();
65   }
66   return rt;
67 }
68
69 VAudioplayer::~VAudioplayer()
70 {
71   if (banner) BoxStack::getInstance()->remove(banner);
72   if (fullname) delete fullname;
73   if (filename) delete filename;
74   Timers::getInstance()->cancelTimer(this,1);
75   Timers::getInstance()->cancelTimer(this,2);
76   Timers::getInstance()->cancelTimer(this,3);
77   //TODO leave this to medialist and stop only...
78   if (getPlayer(false)) {
79     AudioPlayer::getInstance(NULL,false)->shutdown();
80   }
81 }
82   
83
84
85 void VAudioplayer::draw()
86 {
87   Log::getInstance()->log("VAudioplayer::draw", Log::DEBUG, "p=%p", this);
88 }
89
90
91 int VAudioplayer::handleCommand(int command)
92 {
93   Log::getInstance()->log("VAudioplayer::handleCommand", Log::DEBUG, "p=%p,cmd=%d", this,command);
94   Timers::getInstance()->cancelTimer(this,1);
95   int rt=1;
96   switch(command)
97   {
98     case Remote::DF_UP:
99     case Remote::UP:
100       play(VMediaList::MV_PREV);
101       BoxStack::getInstance()->update(this);
102       rt= 2;
103       break;
104     case Remote::FORWARD:
105                         getPlayer()->fastForward();
106       rt=2;
107       break;
108     case Remote::DF_DOWN:
109     case Remote::DOWN:
110       play(VMediaList::MV_NEXT);
111       BoxStack::getInstance()->update(this);
112       rt= 2;
113       break;
114     case Remote::SKIPFORWARD:
115                         getPlayer()->skipForward(10);
116                         rt=2;
117                         break;
118     case Remote::SKIPBACK:
119                         getPlayer()->skipBackward(10);
120                         rt=2;
121                         break;
122     case Remote::REVERSE:
123       rt=2;
124       break;
125                 case Remote::ZERO:
126                         getPlayer()->jumpToPercent(0);
127                         rt=2;
128                         break;
129                 case Remote::ONE:
130                         getPlayer()->jumpToPercent(10);
131                         rt=2;
132                         break;
133                 case Remote::TWO:
134                         getPlayer()->jumpToPercent(20);
135                         rt=2;
136                         break;
137                 case Remote::THREE:
138                         getPlayer()->jumpToPercent(30);
139                         rt=2;
140                         break;
141                 case Remote::FOUR:
142                         getPlayer()->jumpToPercent(40);
143                         rt=2;
144                         break;
145                 case Remote::FIVE:
146                         getPlayer()->jumpToPercent(50);
147                         rt=2;
148                         break;
149                 case Remote::SIX:
150                         getPlayer()->jumpToPercent(60);
151                         rt=2;
152                         break;
153                 case Remote::SEVEN:
154                         getPlayer()->jumpToPercent(70);
155                         rt=2;
156                         break;
157                 case Remote::EIGHT:
158                         getPlayer()->jumpToPercent(80);
159                         rt=2;
160                         break;
161                 case Remote::NINE:
162                         getPlayer()->jumpToPercent(90);
163                         rt=2;
164                         break;
165     case Remote::OK:
166     case Remote::GREEN:
167     {
168       if (banner) {
169         destroyBanner();
170         }
171       else showBanner(true);
172       if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
173         if (playall) play(VMediaList::MV_NEXT);
174       }
175       rt= 2;
176     }
177     break;
178     case Remote::PLAY:
179     {
180       getPlayer()->unpause();
181       if (getPlayer()->getState() != AudioPlayer::S_ERROR) justPlaying=true;
182       else if (playall) play(VMediaList::MV_NEXT);
183       rt= 2;
184     }
185     break;
186     case Remote::PAUSE:
187       //playall=false;
188       getPlayer()->pause();
189       justPlaying=false;
190       rt= 2;
191       break;
192     case Remote::STOP:
193       getPlayer()->stop();
194       justPlaying=false;
195       rt= 2;
196       break;
197     case Remote::BACK:
198     {
199       rt= 4;
200     }
201     break;
202   }
203   if (playall) startPlayall();
204         if (rt == 2) retriggerBanner();
205   // stop command getting to any more views
206   Log::getInstance()->log("VAudioplayer::handleCommand", Log::DEBUG, "returns p=%p,cmd=%d, rt=%d", this,command,rt);
207   return rt;
208 }
209
210 void VAudioplayer::processMessage(Message* m)
211 {
212   if (m->message == Message::MOUSE_MOVE)
213   {
214     ;
215   }
216   else if (m->message == Message::MOUSE_LBDOWN)
217   {
218     
219     //check if press is outside this view! then simulate cancel
220     int x=(m->parameter>>16)-getScreenX();
221     int y=(m->parameter&0xFFFF)-getScreenY();
222     if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
223     {
224       BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press
225     }
226   }
227   else if (m->message == Message::PLAYER_EVENT) {
228     switch (m->parameter) {
229       case AudioPlayer::STREAM_ERR:
230       case AudioPlayer::STREAM_END:
231         if (playall) play(VMediaList::MV_NEXT);
232         else sendViewMsg(this,true);
233         break;
234       case AudioPlayer::STATUS_CHANGE:
235         updateBanner(false);
236         break;
237       case AudioPlayer::SHORT_UPDATE:
238                                 if (banner) {
239                                         drawClocks();
240                                         BoxStack::getInstance()->update(banner,&clocksRegion);
241                                   BoxStack::getInstance()->update(banner,&barRegion);
242                                         Timers::getInstance()->setTimerD(this, 3, 1);
243                                 }
244         break;
245       case AudioPlayer::NEW_SONG:
246         updateBanner(true);
247         break;
248       case AudioPlayer::CONNECTION_LOST:
249         destroyBanner();
250         if (AudioPlayer *player=getPlayer(false)) {
251           player->shutdown();
252           player=NULL;
253         }
254         Command::getInstance()->connectionLost();
255         break;
256     }
257   }
258 }
259
260 VAudioplayer * VAudioplayer::createPlayer(VMediaList * mparent, bool bplayall) {
261    Log::getInstance()->log("VAudioplayer::createPlayer", Log::DEBUG, "p=%p",
262   mparent);
263    VAudioplayer *vmn=new VAudioplayer(mparent);
264    BoxStack::getInstance()->add(vmn);
265    BoxStack::getInstance()->update(vmn);
266    vmn->play();
267    if (bplayall) vmn->startPlayall();
268    BoxStack::getInstance()->update(vmn);
269    return vmn;
270 }
271
272 void VAudioplayer::startPlayall() {
273    playall=true;
274 }
275
276 void VAudioplayer::play(ULONG move) {
277   currentMedia=parent->getMedia(MEDIA_TYPE_AUDIO,move);
278   mediaError=NULL;
279   const char *fname=currentMedia->getFileName();
280   ftime=currentMedia->getTime();
281   int len=strlen(fname)+2+strlen(parent->getDirname());
282   if (fullname) {
283      delete fullname;
284      fullname=NULL;
285      }
286   fullname=new char[len];
287   sprintf(fullname,"%s/%s",parent->getDirname(),fname);
288   if (filename) delete filename;
289   len=strlen(fname)+1;
290   filename=new char[len];
291   strcpy(filename,fname);
292   Log::getInstance()->log("VAudioplayer::load", Log::DEBUG, "filename=%s,p=%p",
293   fullname,this);
294   VDR* vdr=VDR::getInstance();
295   if (vdr->isConnected()) {
296      Log::getInstance()->log("VAudioplayer", Log::DEBUG, "request file %s",filename);
297      int wseq=getPlayer()->play(fullname);
298      if (getPlayer()->waitForSequence(wseq,2)<0) {
299        mediaError=tr("unable to open audio file");
300      }
301      justPlaying=true;
302      //leave this to the update message from the player:
303      //showBanner();
304     }
305   else {
306     if (AudioPlayer * player=getPlayer(false)) {
307       player->shutdown();
308     }
309     Command::getInstance()->connectionLost();
310     }
311   if (mediaError) {
312     showBanner(true);
313   }
314   Log::getInstance()->log("VAudioplayer", Log::DEBUG, "player started for %s",filename);
315 }
316
317 void VAudioplayer::retriggerBanner() {
318         if (! banner) return;
319         bannerTime=time(NULL);
320 }
321
322 void VAudioplayer::showBanner(bool forceNewTime) {
323   //if the loading flag is set,
324   //we are in the main thread - so we can (and must) safely hard destroy/create the banner
325   Timers::getInstance()->cancelTimer(this,1);
326   Timers::getInstance()->cancelTimer(this,3);
327   if (! filename || ! currentMedia) {
328     //hmm...
329     destroyBanner();
330     return;
331     }
332   drawBanner();
333   time_t curtime=time(NULL);
334   if (forceNewTime) retriggerBanner();
335   time_t remainingTime=BANNER_TIME+2-(curtime-bannerTime);
336   if (remainingTime < 2) remainingTime=2;
337   bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR;
338   if (remainingTime < BANNER_ERROR_TIME+2 && playerError) remainingTime=BANNER_ERROR_TIME+2;
339   Timers::getInstance()->setTimerD(this,1,remainingTime);
340   if (playerError) Timers::getInstance()->setTimerD(this,2,BANNER_ERROR_TIME);
341         Timers::getInstance()->setTimerD(this,3, 1);
342   BoxStack::getInstance()->update(banner);
343   }
344
345 void VAudioplayer::destroyBanner() {
346   if (banner) {
347     BoxStack::getInstance()->remove(banner);
348     banner=NULL;
349     }
350   Timers::getInstance()->cancelTimer(this,1);
351   Timers::getInstance()->cancelTimer(this,3);
352 }
353 void VAudioplayer::updateBanner(bool restart) {
354   time_t currTime=time(NULL);
355   time_t lastBanner=bannerTime;
356   if (currTime - lastBanner > BANNER_TIME){
357     destroyBanner();
358   }
359   if (banner||restart) {
360     showBanner(restart);
361     }
362 }
363 void VAudioplayer::timercall(int clientref) {
364    Log::getInstance()->log("VAudioplayer::timercall", Log::DEBUG, "id=%d",clientref);
365    switch(clientref)
366    {
367       case 1:
368       case 3:
369         {
370           Message* m = new Message(); 
371           //we misuse PLAYER_EVENT here
372           m->message = Message::PLAYER_EVENT;
373           m->to = this;
374           m->from = this;
375           m->parameter=(clientref==1)?AudioPlayer::STATUS_CHANGE:
376                                                 AudioPlayer::SHORT_UPDATE;
377           Command::getInstance()->postMessageFromOuterSpace(m);
378           break;
379         }
380       case 2:
381         {
382           bool stillError=false;
383           if (AudioPlayer * player=getPlayer(false)) {
384             stillError=player->getState()==AudioPlayer::S_ERROR;
385           }
386           if (stillError) {
387             //send a stream end to trigger nex in playlist
388             Message* m = new Message(); 
389             //we misuse PLAYER_EVENT here
390             m->message = Message::PLAYER_EVENT;
391             m->to = this;
392             m->from = this;
393             m->parameter=AudioPlayer::STREAM_END;
394             Command::getInstance()->postMessageFromOuterSpace(m);
395           }
396           break;
397         }
398     }
399    
400   }
401
402 void VAudioplayer::sendViewMsg(Boxx *v,bool vdestroy) {
403   Message* m = new Message(); 
404   m->message = vdestroy?Message::CLOSE_ME:Message::ADD_VIEW;
405   m->to = BoxStack::getInstance();
406   m->from = v;
407   m->parameter=(ULONG)v;
408   Command::getInstance()->postMessageFromOuterSpace(m);
409 }
410
411 TBBoxx * VAudioplayer::createBannerView(int numlines) {
412   TBBoxx *rt=new TBBoxx();
413   UINT height=numlines*30+60;
414   UINT vheight=Video::getInstance()->getScreenHeight();
415   UINT vwidth=Video::getInstance()->getScreenWidth();
416   if (height > vheight-2*BANNER_BOTTOM_MARGIN)
417     height=vheight-2*BANNER_BOTTOM_MARGIN;
418   rt->setSize(vwidth -2*BANNER_X_MARGIN, height);
419   rt->createBuffer();
420   if (Video::getInstance()->getFormat() == Video::PAL)
421   {
422     rt->setPosition(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
423   }
424   else
425   {
426     rt->setPosition(BANNER_X_MARGIN, vheight-height-BANNER_BOTTOM_MARGIN);
427   }
428
429   rt->setTitleBarOn(0);
430   //from vvideorec 
431         //set the regions for the closcks and bars on banner
432   barRegion.x = rt->getWidth()-BARLEN-20;
433   barRegion.y = rt->getHeight() - 30;   // FIXME, need to be - 1? and below?
434   barRegion.w = rt->getWidth()-BARLEN+10;
435   barRegion.h = 30;
436
437   clocksRegion.x = 130;
438   clocksRegion.y = barRegion.y + 3;
439   clocksRegion.w = 190;
440   clocksRegion.h = surface->getFontHeight();
441   Log::getInstance()->log("VAudioPlayer",Log::DEBUG,"created banner %p",rt);
442   BoxStack::getInstance()->add(rt);
443   return rt;
444 }
445
446
447 void VAudioplayer::drawBanner(){
448   Log::getInstance()->log("VAudioPlayer",Log::DEBUG, "draw banner for %p",banner);
449   char *title=NULL;
450   char *playerTitle=getPlayer()->getTitle();
451   if (playerTitle) title=playerTitle;
452   else title=filename;
453   const char *pl=tr("Playlist");
454   int num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentMedia->index);
455   bool playerError=getPlayer()->getState() == AudioPlayer::S_ERROR;
456   //1more line for long dirs
457   int numlines=playall?5:4;
458   int len=0;
459   const char *first=NULL;
460   char *playerInfo=NULL;
461   if (playerError) {
462     numlines=3;
463     first=tr("Unable to play audio file");
464     len=strlen(first)+3;
465   }
466   else {
467     playerInfo=getPlayer()->getID3Info();
468     len=strlen(filename)+strlen(pl)+30+strlen(parent->getDirname())+Media::TIMEBUFLEN+100;
469     if (playerInfo) {
470       for (UINT i=0;i<strlen(playerInfo);i++) 
471         if (playerInfo[i] == '\n') numlines++;
472       len+=strlen(playerInfo);
473       first=playerInfo;
474     }
475     else {
476       first="";
477     }
478   }
479   if (numlines != bannerlines) {
480     if (banner) destroyBanner();
481     banner=createBannerView(numlines);
482   }
483   if (! banner) {
484     banner=createBannerView(numlines);
485   }
486   bannerlines=numlines;
487   char *buf=new char [len];
488   if (playerError) {
489     SNPRINTF(buf,len,"%s",first);
490   }
491   else {
492     char tbuf[Media::TIMEBUFLEN];
493     if (playall) {
494     SNPRINTF(buf,len,"%s\n"
495         "%s:%s\n"
496         "%s: %d/%d\n"
497         "%s:%s\n"
498         "%s:%s\n",
499         first,
500         tr("FileName"),currentMedia->getFileName(),
501         pl,num,numentries,
502         tr("Directory"),parent->getDirname(),
503         tr("Time"),currentMedia->getTimeString(tbuf));
504     }
505     else {
506     SNPRINTF(buf,len,"%s\n"
507         "%s:%s\n"
508         "%s:%s\n"
509         "%s:%s\n",
510         first,
511         tr("FileName"),currentMedia->getFileName(),
512         tr("Directory"),parent->getDirname(),
513         tr("Time"),currentMedia->getTimeString(tbuf));
514     }
515   }
516   Log::getInstance()->log("VAudioPlayer",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
517   //now the real drawing functions
518   banner->draw();
519   WSymbol w;
520   banner->TEMPADD(&w);
521 //  w.setSurface(banner->surface);
522   //title
523   banner->rectangle(0, 0, banner->getWidth(), 30, Colour::TITLEBARBACKGROUND);
524   banner->drawText(title, 5, 5, Colour::LIGHTTEXT);
525   banner->drawPara(buf,5,32,Colour::LIGHTTEXT);
526   delete [] buf;
527   int x=10;
528   int ybottom=banner->getHeight();
529   banner->rectangle(0, ybottom - barRegion.h, banner->getWidth(), barRegion.h, Colour::TITLEBARBACKGROUND);
530   bool drawSymbol=true;
531   switch(getPlayer()->getState()) {
532     case AudioPlayer::S_PAUSE:
533       w.nextSymbol = WSymbol::PAUSE;
534       break;
535     case AudioPlayer::S_PLAY:
536       w.nextSymbol = WSymbol::PLAY;
537       break;
538     case AudioPlayer::S_DONE:
539       if (playall) 
540         w.nextSymbol = WSymbol::PLAY;
541       else
542         drawSymbol=false;
543       break;
544     case AudioPlayer::S_BACK:
545       w.nextSymbol = WSymbol::FBWD ;
546       break;
547     case AudioPlayer::S_FF:
548       w.nextSymbol = WSymbol::FFWD ;
549       break;
550     default:
551       drawSymbol=false;
552       break;
553   }
554   if (drawSymbol) {
555     w.setPosition(x, ybottom-24);
556     w.draw();
557   }
558   else {
559     //stop symbol
560     banner->rectangle(x, ybottom - 23, 18, 16, Colour::LIGHTTEXT);
561   }
562   x+=30;
563   if (playerInfo) delete playerInfo;
564   if (playerTitle) delete playerTitle;
565         drawClocks();
566 }
567
568 void VAudioplayer::drawClocks() {
569         if (! banner) return;
570   //draw clocks and bar
571   banner->rectangle(clocksRegion, Colour::TITLEBARBACKGROUND);
572
573   time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
574   time_t lengthSec=(time_t)(getPlayer()->getSonglen());
575         struct tm cpos;
576         struct tm slen;
577 /*      gmtime_r(&currentSec,&cpos);
578         gmtime_r(&lengthSec,&slen);*/
579     cpos.tm_hour=currentSec/3600;
580     cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60;
581     cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60);
582     slen.tm_hour=0;
583     slen.tm_min=0;
584     slen.tm_sec=0;
585
586   char buffer[100];
587   if (currentSec >= lengthSec)
588   {
589     SNPRINTF(buffer,99, "-:--:-- / -:--:--  %03dk",getPlayer()->getCurrentBitrate()/1000);
590   }
591   else
592   {
593     SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i  %03dk", cpos.tm_hour, cpos.tm_min, cpos.tm_sec, slen.tm_hour, slen.tm_min, slen.tm_sec,
594                                 getPlayer()->getCurrentBitrate()/1000);
595                 //Log::getInstance()->log("VAudioplayer", Log::DEBUG, buffer);
596   }
597
598   banner->drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
599   
600         // Draw progress bar
601   int progBarXbase = 0;
602         int barlen=250;
603
604   banner->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, Colour::LIGHTTEXT);
605   banner->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue);
606
607   if (currentSec > lengthSec) currentSec=lengthSec;
608   if (lengthSec == 0) return;
609
610   // Draw yellow portion
611   int progressWidth = (barlen+2) * currentSec / lengthSec;
612   banner->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, Colour::SELECTHIGHLIGHT);
613
614 }
615
616