]> git.vomp.tv Git - vompclient.git/blob - vvideorec.cc
Windows readme and makefile updated!
[vompclient.git] / vvideorec.cc
1 /*
2     Copyright 2004-2005 Chris Tallon
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 <math.h>
22
23 #include "vvideorec.h"
24 #include "vteletextview.h"
25
26 #include "command.h"
27 #include "osd.h"
28 #include "wsymbol.h"
29 #include "audio.h"
30 #include "vdr.h"
31 #include "video.h"
32 #include "timers.h"
33 #include "player.h"
34 #include "recording.h"
35 #include "vaudioselector.h"
36 #include "message.h"
37 #include "remote.h"
38 #include "boxstack.h"
39 #include "vinfo.h"
40 #include "i18n.h"
41 #include "bitmap.h"
42 #include "recinfo.h"
43 #include "log.h"
44 #include "channel.h"
45  
46 VVideoRec::VVideoRec(Recording* rec, bool ish264)
47 {
48   boxstack = BoxStack::getInstance();
49   vdr = VDR::getInstance();
50   video = Video::getInstance();
51   timers = Timers::getInstance();
52   vas = NULL;
53   vsummary = NULL;
54
55   videoMode = video->getMode();
56   myRec = rec;
57
58   video->seth264mode(ish264);
59
60   player = new Player(Command::getInstance(), this, this);
61   player->init(myRec->IsPesRecording,myRec->recInfo->fps);
62
63   playing = false;
64
65   startMargin = 0;
66   endMargin = 0;
67   char* cstartMargin = vdr->configLoad("Timers", "Start margin");
68   char* cendMargin = vdr->configLoad("Timers", "End margin");
69   if (!cstartMargin)
70   {
71     startMargin = 300; // 5 mins default
72   }
73   else
74   {
75     startMargin = atoi(cstartMargin) * 60;
76     delete[] cstartMargin;
77   }
78
79   if (!cendMargin)
80   {
81     endMargin = 300; // 5 mins default
82   }
83   else
84   {
85     endMargin = atoi(cendMargin) * 60;
86     delete[] cendMargin;
87   }
88
89   Log::getInstance()->log("VVideoRec", Log::DEBUG, "SM: %u EM: %u", startMargin, endMargin);
90
91   setSize(video->getScreenWidth(), video->getScreenHeight());
92   createBuffer();
93   transparent.set(0, 0, 0, 0);
94   setBackgroundColour(transparent);
95
96   barRegion.x = 0;
97   barRegion.y = video->getScreenHeight() - 58;   // FIXME, need to be - 1? and below?
98   barRegion.w = video->getScreenWidth();
99   barRegion.h = 58;
100
101   clocksRegion.x = barRegion.x + 140;
102   clocksRegion.y = barRegion.y + 12;
103   clocksRegion.w = 170;
104   clocksRegion.h = getFontHeight();
105 //  barBlue.set(0, 0, 150, 150);
106   barBlue.set(0, 0, 0, 128);
107
108   barShowing = false;
109   barGenHold = false;
110   barScanHold = false;
111   barVasHold = false;
112 #ifdef PAL_WSS
113   dowss = false;
114   char* optionWSS = vdr->configLoad("General", "WSS");
115   if (optionWSS)
116   {
117     if (strstr(optionWSS, "Yes")) dowss = true;
118     delete[] optionWSS;
119   }
120   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Do WSS: %u", dowss);
121
122   if (dowss)
123   {
124     wss.setFormat(video->getFormat());
125     wss.setWide(true);
126     add(&wss);
127
128     wssRegion.x = 0;
129     wssRegion.y = 0;
130     wssRegion.w = video->getScreenWidth();
131     wssRegion.h = 300;
132   }
133 #endif
134   vdisplay.mode=Fullscreen;
135   vdisplay.fallbackMode=Fullscreen;
136   vdisplay.x=0;
137   vdisplay.y=0;
138   vdisplay.width=0;
139   vdisplay.height=0;
140
141   lastbar = -1;
142 }
143
144 void VVideoRec::preDelete()
145 {
146   timers->cancelTimer(this, 1);
147   timers->cancelTimer(this, 2);
148
149   if (vas)
150   {
151     boxstack->remove(vas);
152     vas = NULL;
153   }
154
155   if (vsummary) delete vsummary;
156
157   if (playing) stopPlay();
158 }
159
160 VVideoRec::~VVideoRec()
161 {
162   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Entering vvideorec destructor");
163
164   video->setDefaultAspect();
165
166   // kill recInfo in case resumePoint has changed (likely)
167   myRec->dropRecInfo();
168   // FIXME - do this properly - save the resume point back to the server manually and update
169   // rec->recInfo->resumePoint - this will fix the ~10s offset problem as well
170 }
171
172 void VVideoRec::go(bool resume)
173 {
174   ULONG startFrameNum;
175   if (resume)
176     startFrameNum = myRec->recInfo->resumePoint;
177   else
178     startFrameNum = 0;
179
180   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Starting stream: %s at frame: %lu", myRec->getFileName(), startFrameNum);
181   ULONG lengthFrames = 0;
182   bool isPesRecording;
183   ULLONG lengthBytes = vdr->streamRecording(myRec->getFileName(), &lengthFrames, &isPesRecording);
184   myRec->IsPesRecording = isPesRecording;
185   if (lengthBytes)
186   {
187     player->setLengthBytes(lengthBytes);
188     player->setLengthFrames(lengthFrames);
189     player->setStartFrame(startFrameNum);
190     player->play();
191     playing = true;
192     doBar(0);
193   }
194   else
195   {
196     stopPlay(); // clean up
197
198     if (!vdr->isConnected())
199     {
200       Command::getInstance()->connectionLost();
201       return;
202     }
203
204     Message* m = new Message();
205     m->message = Message::CLOSE_ME;
206     m->from = this;
207     m->to = boxstack;
208     Command::getInstance()->postMessageNoLock(m);
209
210     VInfo* vi = new VInfo();
211     vi->setSize(400, 150);
212     vi->createBuffer();
213     if (video->getFormat() == Video::PAL)
214       vi->setPosition(170, 200);
215     else
216       vi->setPosition(160, 150);
217     vi->setExitable();
218     vi->setBorderOn(1);
219     vi->setTitleBarOn(0);
220     vi->setOneLiner(tr("Error playing recording"));
221     vi->draw();
222
223     m = new Message();
224     m->message = Message::ADD_VIEW;
225     m->to = boxstack;
226     m->parameter.num = (ULONG)vi;
227     Command::getInstance()->postMessageNoLock(m);
228   }
229 }
230
231 int VVideoRec::handleCommand(int command)
232 {
233   switch(command)
234   {
235     case Remote::UP:
236     case Remote::PLAY:
237     {
238       player->play();
239       doBar(0);
240       return 2;
241     }
242
243     case Remote::PLAYPAUSE:
244     {
245         player->playpause();
246         doBar(0);
247         return 2;
248     }
249
250     case Remote::BACK:
251     {
252       if (vsummary)
253       {
254         removeSummary();
255         return 2;
256       }
257     } // DROP THROUGH
258     case Remote::STOP:
259     case Remote::MENU:
260     {
261       if (playing) stopPlay();
262
263       return 4;
264     }
265     case Remote::DOWN:
266     case Remote::PAUSE:
267     {
268       player->pause();
269       doBar(0);
270       return 2;
271     }
272     case Remote::SKIPFORWARD:
273     {
274       doBar(3);
275       player->skipForward(60);
276       return 2;
277     }
278     case Remote::SKIPBACK:
279     {
280       doBar(4);
281       player->skipBackward(60);
282       return 2;
283     }
284     case Remote::RIGHT:
285     case Remote::FORWARD:
286     {
287       player->fastForward();
288       doBar(0);
289       return 2;
290     }
291     case Remote::LEFT:
292     case Remote::REVERSE:
293     {
294       player->fastBackward();
295       doBar(0);
296       return 2;
297     }
298     case Remote::RED:
299     {
300       if (vsummary) removeSummary();
301       else doSummary();
302       return 2;
303     }
304     case Remote::GREEN:
305     {
306       doAudioSelector();
307       return 2;
308     }
309     case Remote::YELLOW:
310     {
311       if (myRec->hasMarks())
312       {
313         // skip to previous mark
314         Log* logger = Log::getInstance();
315         int currentFrame = (player->getCurrentFrameNum()); // get current Frame
316         currentFrame -= 5. * myRec->recInfo->fps; // subtrack 5 seconds, else you cannot skip more than once back ..
317
318         int prevMark = myRec->getPrevMark(currentFrame); // find previous Frame
319         if (prevMark)
320         {
321           logger->log("VVideoRec", Log::NOTICE, "jump back from pos %i to mark at %i",currentFrame,prevMark);
322           player->jumpToMark(prevMark);
323         }
324         doBar(4);
325       }
326       else
327       {
328         doBar(2);
329         player->skipBackward(10);
330       }
331       return 2;
332     }
333     case Remote::BLUE:
334     {
335       if (myRec->hasMarks())
336       {
337         // skip to next mark
338         Log* logger = Log::getInstance();
339         int currentFrame = (player->getCurrentFrameNum());
340
341         int nextMark = myRec->getNextMark(currentFrame);
342
343         if (nextMark)
344         {
345           logger->log("VVideoRec", Log::NOTICE, "jump forward from pos %i to mark at %i",currentFrame,nextMark);
346           player->jumpToMark(nextMark);
347         }
348         doBar(3);
349       }
350       else
351       {
352         doBar(1);
353         player->skipForward(10);
354       }
355       return 2;
356     }
357     case Remote::STAR:
358     {
359       doBar(2);
360       player->skipBackward(10);
361       return 2;
362     }
363     case Remote::HASH:
364     {
365       doBar(1);
366       player->skipForward(10);
367       return 2;
368     }
369     case Remote::FULL:
370     case Remote::TV:
371     {
372       toggleChopSides();
373       return 2;
374     }
375
376     case Remote::OK:
377     {
378       if (vsummary)
379       {
380         removeSummary();
381         return 2;
382       }
383       
384       if (barShowing) removeBar();
385       else doBar(0);
386       return 2;
387     }
388
389     case Remote::ZERO:  player->jumpToPercent(0);  doBar(0);  return 2;
390     case Remote::ONE:   player->jumpToPercent(10); doBar(0);  return 2;
391     case Remote::TWO:   player->jumpToPercent(20); doBar(0);  return 2;
392     case Remote::THREE: player->jumpToPercent(30); doBar(0);  return 2;
393     case Remote::FOUR:  player->jumpToPercent(40); doBar(0);  return 2;
394     case Remote::FIVE:  player->jumpToPercent(50); doBar(0);  return 2;
395     case Remote::SIX:   player->jumpToPercent(60); doBar(0);  return 2;
396     case Remote::SEVEN: player->jumpToPercent(70); doBar(0);  return 2;
397     case Remote::EIGHT: player->jumpToPercent(80); doBar(0);  return 2;
398     case Remote::NINE:  player->jumpToPercent(90); doBar(0);  return 2;
399
400     case Remote::RECORD: player->toggleSubtitles(); return 2;
401 #ifdef DEV
402 //    case Remote::RED:
403 //    {
404       //Don't use RED for anything. It will eventually be recording summary
405
406       //player->test1();
407
408
409       /*
410       // for testing EPG in NTSC with a NTSC test video
411       Video::getInstance()->setMode(Video::QUARTER);
412       Video::getInstance()->setPosition(170, 5);
413       VEpg* vepg = new VEpg(NULL, 0);
414       vepg->draw();
415       BoxStack::getInstance()->add(vepg);
416       BoxStack::getInstance()->update(vepg);
417       */
418
419 //      return 2;
420 //    }
421
422 #endif
423
424   }
425
426   return 1;
427 }
428
429 void VVideoRec::doTeletext()
430 {
431   
432   bool exists=true;
433
434   
435   // Draw the teletxt
436   VTeletextView *vtxv=player->getTeletextDecoder()->getTeletxtView();
437   if (vtxv==NULL) {
438        vtxv= new VTeletextView((player)->getTeletextDecoder(),this,NULL);
439       (player)->getTeletextDecoder()->registerTeletextView(vtxv);
440       exists=false;
441   }
442   vtxv->setSubtitleMode(true);
443   vtxv->draw();
444   draw();
445   
446   if (!exists) {
447       BoxStack::getInstance()->add(vtxv);
448   }
449   BoxStack::getInstance()->update(this);
450   BoxStack::getInstance()->update(vtxv); 
451 }
452
453 void VVideoRec::processMessage(Message* m)
454 {
455   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Message received");
456
457   if (m->message == Message::MOUSE_LBDOWN)
458   {
459     UINT x = (m->parameter.num>>16) - getScreenX();
460     UINT y = (m->parameter.num&0xFFFF) - getScreenY();
461
462     if (!barShowing)
463     {
464       BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
465     }
466     else if (barRegion.x<=x && barRegion.y<=y && (barRegion.x+barRegion.w)>=x && (barRegion.y+barRegion.h)>=y)
467     {
468       int progBarXbase = barRegion.x + 300;
469       if (myRec->hasMarks())
470       {
471         MarkList* markList = myRec->getMarkList();
472         MarkList::iterator i;
473         Mark* loopMark = NULL;
474         int posPix;
475         ULONG lengthFrames;
476         if (myRec->recInfo->timerEnd > time(NULL))
477         {
478           // chasing playback
479           // Work out an approximate length in frames (good to 1s...)
480                         lengthFrames = (ULONG)((double)(myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * myRec->recInfo->fps);
481         }
482         else
483         {
484           lengthFrames = player->getLengthFrames();
485         }
486         for(i = markList->begin(); i != markList->end(); i++)
487         {
488           loopMark = *i;
489           if (loopMark->pos)
490           {
491             posPix = 302 * loopMark->pos / lengthFrames;
492             rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 3, 28, DrawStyle::DANGER);
493             if (x>=barRegion.x + progBarXbase + 2 + posPix
494                 && x<=barRegion.x + progBarXbase + 2 + posPix+3
495                 && y>=barRegion.y + 12 - 2
496                 && y<=barRegion.y + 12 - 2+28)
497             {
498               player->jumpToMark(loopMark->pos);
499               doBar(3);
500               return;
501             }
502           }
503         }
504       }
505
506       if (x>=barRegion.x + progBarXbase + 24
507           && x<=barRegion.x + progBarXbase + 4 + 302
508           && y>=barRegion.y + 12 - 2
509           && y<=barRegion.y + 12 - 2+28)
510       {
511         int cx=x-(barRegion.x + progBarXbase + 4);
512         double percent=((double)cx)/302.*100.;
513         player->jumpToPercent(percent);
514         doBar(3);
515         return;
516         //  int progressWidth = 302 * currentFrameNum / lengthFrames;
517         //  rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
518       }
519     }
520     else
521     {
522       BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
523     }
524   }
525   else if (m->from == player)
526   {
527     if (m->message != Message::PLAYER_EVENT) return;
528     switch(m->parameter.num)
529     {
530       case Player::CONNECTION_LOST: // connection lost detected
531       {
532         // I can't handle this, send it to command
533         Message* m2 = new Message();
534         m2->to = Command::getInstance();
535         m2->message = Message::CONNECTION_LOST;
536         Command::getInstance()->postMessageNoLock(m2);
537         break;
538       }
539       case Player::STOP_PLAYBACK:
540       {
541         // FIXME Obselete ish - improve this
542         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
543         m2->to = Command::getInstance();
544         m2->message = Message::STOP_PLAYBACK;
545         Command::getInstance()->postMessageNoLock(m2);
546         break;
547       }
548       case Player::ASPECT43:
549       {
550 #ifdef PAL_WSS
551         if (dowss)
552         {
553           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
554           wss.setWide(false);
555           wss.draw();
556           boxstack->update(this, &wssRegion);
557         }
558 #endif
559         break;
560       }
561       case Player::ASPECT169:
562       {
563 #ifdef PAL_WSS
564         if (dowss)
565         {
566           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
567           wss.setWide(true);
568           wss.draw();
569           boxstack->update(this, &wssRegion);
570         }
571 #endif
572         break;
573       }
574     }
575   }
576   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
577   {
578     Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received change audio channel to %i", m->parameter.num);
579     player->setAudioChannel(m->parameter.num&0xFFFF,(m->parameter.num&0xFF0000)>> 16,(m->parameter.num&0xFF000000)>> 24 );
580   }
581   else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL)
582   {
583       Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received change subtitle channel to %i", m->parameter.num);
584       int type=((m->parameter.num & 0xFF0000)>>16);
585       switch (type) {
586       case 0x10: { //dvbsubtitle
587           player->setSubtitleChannel((m->parameter.num & 0xFFFF));
588           player->turnSubtitlesOn(true);
589           VTeletextView *vtxt=((Player*)player)->getTeletextDecoder()->getTeletxtView();
590           if (vtxt && vtxt->isInSubtitleMode()) {
591               BoxStack::getInstance()->remove(vtxt);
592           }
593                  } break;
594       case 0xFF: { //nosubtitles
595           
596            player->turnSubtitlesOn(false);
597            VTeletextView *vtxt=((Player*)player)->getTeletextDecoder()->getTeletxtView();
598            if (vtxt && vtxt->isInSubtitleMode()) {
599               BoxStack::getInstance()->remove(vtxt);
600            }  
601           
602                  } break;
603       case 0x11: { //videotext
604           player->turnSubtitlesOn(false);
605           doTeletext();
606           ((Player*)player)->getTeletextDecoder()->setPage((m->parameter.num & 0xFFFF));
607                  } break;
608       };
609       if (vas) {
610         BoxStack::getInstance()->update(vas);
611       }
612       BoxStack::getInstance()->update(this);
613
614       
615   } 
616   else if (m->message == Message::CHILD_CLOSE)
617   {
618     if (m->from == vas)
619     {
620       vas = NULL;
621       barVasHold = false;
622       if (!barGenHold && !barScanHold && !barVasHold) removeBar();
623     }
624   }
625 }
626
627 void VVideoRec::stopPlay()
628 {
629   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Pre stopPlay");
630
631   removeBar();
632   Log::getInstance()->log("VVideoRec", Log::DEBUG, "1");
633   player->stop();
634   Log::getInstance()->log("VVideoRec", Log::DEBUG, "2");
635   vdr->stopStreaming();
636   Log::getInstance()->log("VVideoRec", Log::DEBUG, "3");
637   delete player;
638
639   playing = false;
640
641   if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; }
642   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Post stopPlay");
643 }
644
645 void VVideoRec::toggleChopSides()
646 {
647   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
648
649   if (videoMode == Video::NORMAL)
650   {
651     videoMode = Video::LETTERBOX;
652     video->setMode(Video::LETTERBOX);
653   }
654   else
655   {
656     videoMode = Video::NORMAL;
657     video->setMode(Video::NORMAL);
658   }
659 }
660
661 void VVideoRec::doAudioSelector()
662 {
663     int subtitleChannel=player->getCurrentSubtitleChannel();
664     int subtitleType=0x10;
665     if (!(player)->isSubtitlesOn()) {
666         if ((player)->getTeletextDecoder()->getTeletxtView() &&
667             (player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() 
668             ) {
669                 subtitleChannel=(player)->getTeletextDecoder()->getPage();
670                 subtitleType=0x11;
671       
672            } else {
673                 subtitleType=0xFF; //turnedOff
674                 subtitleChannel=0;
675           }
676     }
677     if (player->isPesRecording()) {
678         bool* availableMpegAudioChannels = player->getDemuxerMpegAudioChannels();
679         bool* availableAc3AudioChannels = NULL;
680         bool* availableSubtitleChannels = player->getDemuxerSubtitleChannels();
681         int *availableTTxtpages = player->getTeletxtSubtitlePages();
682         int currentAudioChannel = player->getCurrentAudioChannel();
683         if (Audio::getInstance()->supportsAc3())
684         {
685             availableAc3AudioChannels = player->getDemuxerAc3AudioChannels();
686         }
687         
688         vas = new VAudioSelector(this, availableMpegAudioChannels, availableAc3AudioChannels, currentAudioChannel,availableSubtitleChannels, availableTTxtpages,
689             subtitleChannel, subtitleType, myRec->recInfo);
690     } else {
691         // Draw the selector
692         Channel *temp_channel=player->getDemuxerChannel();
693        // RecInfo *cur_info= myRec->recInfo;
694      /*   unsigned char numchan_recinfo = cur_info->numComponents;
695         unsigned char numchan_subtitles_siz = temp_channel.numSPids;
696         ULONG mp_audcounter = 0;
697         ULONG ac3_counter = 0;
698         int dvb_subcounter = 1;*/
699         int i;
700         
701         /*unsigned char type;
702         char* lang;
703         char* description;
704         for (i = 0; i < numchan_recinfo; i++)
705         {   
706             apid* ac = NULL;
707             type = cur_info->types[i];
708             lang = cur_info->languages[i];
709             description = cur_info->descriptions[i];
710             
711
712             if (cur_info->streams[i] == 2) {
713                 switch (type)
714                 {
715                 case 1: //mpaudio mono
716                 case 3: //mpaudio stereo
717                     if (mp_audcounter < temp_channel.numAPids) ac = &temp_channel.apids[mp_audcounter];
718                     
719                     mp_audcounter++;
720                     break;
721                 case 5: //ac3
722                     if (ac3_counter < temp_channel.numDPids) ac = &temp_channel.dpids[ac3_counter];
723                     ac3_counter++;
724                     break;
725                 }
726             } else if (cur_info->streams[i] == 3){
727                 if (dvb_subcounter < numchan_subtitles_siz) ac = &temp_channel.spids[dvb_subcounter];
728             } else continue; //neither audio nor subtitle
729             if (ac)
730             {
731                 if (description && (strlen(description) > 0))
732                 {
733                     ac->name = new char[strlen(description) + 1];
734                     strcpy(ac->name, description);
735                     
736                 } else if (lang && strlen(lang) > 0)
737                 {
738                     ac->name = new char[strlen(lang) + 1];
739                     strcpy(ac->name, lang);
740                     
741                 }
742             }
743         }*/
744         for (i=0;i<temp_channel->numAPids;i++) {
745             apid *ac=&temp_channel->apids[i];
746             if (ac->desc[0]==0) {
747                 strncpy(ac->desc, tr("unknown"),9);
748             }
749         }
750         for (i=0;i<temp_channel->numDPids;i++) {
751             apid *ac=&temp_channel->dpids[i];
752             if (ac->desc[0]==0) {
753                 strncpy(ac->desc, tr("unknown"),9);
754             }
755         }
756         for (i=0;i<temp_channel->numSPids;i++) {
757             apid *ac=&temp_channel->spids[i];
758             if (ac->desc[0]==0) {
759                 strncpy(ac->desc, tr("unknown"),9);
760             }
761         }
762
763         vas = new VAudioSelector(this,temp_channel , (player)->getCurrentAudioChannel(),
764             subtitleType,subtitleChannel,player->getTeletxtSubtitlePages());  
765       /*   for (i=0;i<temp_channel.numAPids;i++) {
766             apid *ac=&temp_channel.apids[i];
767             delete[] ac->name;
768             ac->name=NULL;
769         }
770         for (i=0;i<temp_channel.numDPids;i++) {
771             apid *ac=&temp_channel.dpids[i];
772             delete[] ac->name;
773             ac->name=NULL;
774         }
775         for (i=0;i<temp_channel.numSPids;i++) {
776             apid *ac=&temp_channel.spids[i];
777             delete[] ac->name;
778             ac->name=NULL;
779         }*/
780     }
781
782
783   vas->setBackgroundColour(barBlue);
784   vas->setPosition(0, barRegion.y - 120);
785
786 // pal 62, ntsc 57
787
788   barVasHold = true;
789   doBar(0);
790
791   vas->draw();
792   boxstack->add(vas);
793   boxstack->update(vas);
794 }
795
796 void VVideoRec::doBar(int action_in)
797 {
798   if (player->isSubtitlesOn()) clearOSD(); // remove dvbsubtitles
799   player->tellSubtitlesOSDVisible(true);
800   barShowing = true;
801
802   int action = action_in;
803   if (action == -1) {
804           action = lastbar;
805   }
806   else {
807           lastbar = action;
808   }
809
810   rectangle(barRegion, barBlue);
811
812   /* Work out what to display - choices:
813
814   Playing  >
815   Paused   ||
816   FFwd     >>
817   FBwd     <<
818
819   Specials, informed by parameter
820
821   Skip forward 10s    >|
822   Skip backward 10s   |<
823   Skip forward 1m     >>|
824   Skip backward 1m    |<<
825
826   */
827
828   WSymbol w;
829   TEMPADD(&w);
830   w.nextSymbol = 0;
831   w.setPosition(barRegion.x + 66, barRegion.y + 16);
832
833   UCHAR playerState = 0;
834
835   if (action)
836   {
837     if (action == 1)       w.nextSymbol = WSymbol::SKIPFORWARD;
838     else if (action == 2)  w.nextSymbol = WSymbol::SKIPBACK;
839     else if (action == 3)  w.nextSymbol = WSymbol::SKIPFORWARD2;
840     else if (action == 4)  w.nextSymbol = WSymbol::SKIPBACK2;
841   }
842   else
843   {
844     playerState = player->getState();
845     if (playerState == Player::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
846     else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
847     else if (playerState == Player::S_FFWD)    w.nextSymbol = WSymbol::FFWD;
848     else if (playerState == Player::S_FBWD)    w.nextSymbol = WSymbol::FBWD;
849     else                                       w.nextSymbol = WSymbol::PLAY;
850   }
851
852   w.draw();
853
854   if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD))
855   {
856     // draw blips to show how fast the scan is
857     UCHAR scanrate = player->getIScanRate();
858     if (scanrate >= 2)
859     {
860       char text[5];
861       SNPRINTF(text, 5, "%ux", scanrate);
862       drawText(text, barRegion.x + 102, barRegion.y + 12, DrawStyle::LIGHTTEXT);
863     }
864   }
865
866   drawBarClocks();
867
868   boxstack->update(this, &barRegion);
869
870   if (action_in != -1) {
871           timers->cancelTimer(this, 1);
872
873
874           if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD)) barScanHold = true;
875           else barScanHold = false;
876
877           if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4);
878
879           timers->setTimerD(this, 2, 0, 200000000);
880   }
881 }
882
883 void VVideoRec::timercall(int clientReference)
884 {
885   switch(clientReference)
886   {
887     case 1:
888     {
889       // Remove bar
890       removeBar();
891       break;
892     }
893     case 2:
894     {
895       // Update clock
896       if (!barShowing) break;
897 #ifndef GRADIENT_DRAWING
898       drawBarClocks();
899       boxstack->update(this, &barRegion);
900 #else 
901           doBar(-1);
902 #endif
903       timers->setTimerD(this, 2, 0, 200000000);
904       break;
905     }
906   }
907 }
908
909 hmsf VVideoRec::framesToHMSF(ULONG frames)
910 {
911   hmsf ret;
912   /* from vdr */
913   double Seconds;
914   double fps=myRec->recInfo->fps;
915   ret.frames= int(modf((frames + 0.5) / fps, &Seconds) * fps + 1);
916   int s = int(Seconds);
917   ret.seconds=s % 60;
918   ret.minutes = s / 60 % 60;
919   ret.hours = s / 3600;
920
921
922   return ret;
923 }
924
925 void VVideoRec::drawBarClocks()
926 {
927   if (barScanHold)
928   {
929     UCHAR playerState = player->getState();
930     // sticky bar is set if we are in ffwd/fbwd mode
931     // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which
932     // will repaint all the bar (it will call this function again, but
933     // this section won't run because stickyBarF will then == false)
934
935     if ((playerState != Player::S_FFWD) && (playerState != Player::S_FBWD))
936     {
937       barScanHold = false;
938       doBar(0);
939       return; // doBar will call this function and do the rest
940     }
941   }
942
943   Log* logger = Log::getInstance();
944   logger->log("VVideoRec", Log::DEBUG, "Draw bar clocks");
945
946   // Draw RTC
947   // Blank the area first
948 #ifndef GRADIENT_DRAWING
949   rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
950 #endif
951   char timeString[20];
952   time_t t;
953   time(&t);
954   struct tm* tms = localtime(&t);
955   strftime(timeString, 19, "%H:%M", tms);
956   drawText(timeString, barRegion.x + 624, barRegion.y + 12, DrawStyle::LIGHTTEXT);
957
958   // Draw clocks
959 #ifndef GRADIENT_DRAWING
960   rectangle(clocksRegion, barBlue);
961 #endif
962
963   ULONG currentFrameNum = player->getCurrentFrameNum();
964   ULONG lengthFrames;
965   if (myRec->recInfo->timerEnd > time(NULL))
966   {
967     // chasing playback
968     // Work out an approximate length in frames (good to 1s...)
969     lengthFrames =(ULONG) ((double)(myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * myRec->recInfo->fps);
970   }
971   else
972   {
973     lengthFrames = player->getLengthFrames();
974   }
975
976   hmsf currentFrameHMSF = framesToHMSF(currentFrameNum);
977   hmsf lengthHMSF = framesToHMSF(lengthFrames);
978
979   char buffer[100];
980   if (currentFrameNum >= lengthFrames)
981   {
982     strcpy(buffer, "-:--:-- / -:--:--");
983   }
984   else
985   {
986     SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds);
987     logger->log("VVideoRec", Log::DEBUG, buffer);
988   }
989
990   drawText(buffer, clocksRegion.x, clocksRegion.y, DrawStyle::LIGHTTEXT);
991
992
993
994
995
996
997
998   // Draw progress bar
999   int progBarXbase = barRegion.x + 300;
1000
1001   rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, DrawStyle::LIGHTTEXT);
1002   rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
1003
1004   if (currentFrameNum > lengthFrames) return;
1005   if (lengthFrames == 0) return;
1006
1007   // Draw yellow portion
1008   int progressWidth = 302 * currentFrameNum / lengthFrames;
1009   rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
1010
1011   if (myRec->recInfo->timerEnd > time(NULL)) // if chasing
1012   {
1013     int nrWidth = (int)(302 * ((double)(lengthFrames - player->getLengthFrames()) / lengthFrames));
1014
1015     Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames);
1016     Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames());
1017     Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth);
1018     rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, DrawStyle::RED);
1019   }
1020
1021   int posPix;
1022   // Now calc position for blips
1023
1024   if (myRec->hasMarks())
1025   {
1026     // Draw blips where there are cut marks
1027     MarkList* markList = myRec->getMarkList();
1028     MarkList::iterator i;
1029     Mark* loopMark = NULL;
1030
1031     for(i = markList->begin(); i != markList->end(); i++)
1032     {
1033       loopMark = *i;
1034       if (loopMark->pos)
1035       {
1036         logger->log("VVideoRec", Log::DEBUG, "Drawing mark at frame %i", loopMark->pos);
1037         posPix = 302 * loopMark->pos / lengthFrames;
1038         rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 3, 28, DrawStyle::DANGER);
1039       }
1040     }
1041   }
1042   else
1043   {
1044     // Draw blips where start and end margins probably are
1045
1046           posPix =(int) (302. * myRec->recInfo->fps * ((double)startMargin) /((double) lengthFrames));
1047
1048     rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, DrawStyle::LIGHTTEXT);
1049     rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, DrawStyle::LIGHTTEXT);
1050
1051     posPix = (int)(302. * ((double)lengthFrames - ((double)endMargin) * myRec->recInfo->fps) / ((double)lengthFrames));
1052
1053     rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, DrawStyle::LIGHTTEXT);
1054     rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, DrawStyle::LIGHTTEXT);
1055   }
1056 }
1057
1058 void VVideoRec::removeBar()
1059 {
1060   if (!barShowing) return;
1061   timers->cancelTimer(this, 2);
1062   barShowing = false;
1063   barGenHold = false;
1064   barScanHold = false;
1065   barVasHold = false;
1066   rectangle(barRegion, transparent);
1067   boxstack->update(this, &barRegion);
1068   player->tellSubtitlesOSDVisible(false);
1069 }
1070
1071 void VVideoRec::doSummary()
1072 {
1073   clearOSD(); // remove dvbsubtitles
1074   player->tellSubtitlesOSDVisible(true);
1075   vsummary = new VInfo();
1076   vsummary->setTitleText(myRec->getProgName());
1077   vsummary->setBorderOn(1);
1078   vsummary->setExitable();
1079   if (myRec->recInfo->summary) vsummary->setMainText(myRec->recInfo->summary);
1080   else vsummary->setMainText(tr("Summary unavailable"));
1081   if (Video::getInstance()->getFormat() == Video::PAL)
1082   {
1083     vsummary->setPosition(120, 130);
1084   }
1085   else
1086   {
1087     vsummary->setPosition(110, 90);
1088   }
1089   vsummary->setSize(510, 270);
1090   add(vsummary);
1091   vsummary->draw();
1092
1093   BoxStack::getInstance()->update(this);
1094 }
1095
1096 void VVideoRec::removeSummary()
1097 {
1098   if (vsummary)
1099   {
1100     remove(vsummary);
1101     delete vsummary;
1102     vsummary = NULL;
1103     draw();
1104     BoxStack::getInstance()->update(this);
1105     player->tellSubtitlesOSDVisible(false);
1106   }
1107 }
1108
1109 void VVideoRec::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm, const DisplayRegion& region)
1110 {
1111   drawBitmap(posX, posY, bm, region);
1112   Region r;
1113   r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight();
1114   boxstack->update(this, &r);
1115 }
1116
1117 void VVideoRec::clearOSD()
1118 {
1119   rectangle(area, transparent);
1120   boxstack->update(this, &area);
1121 }
1122
1123 void VVideoRec::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height, const DisplayRegion& region)
1124 {
1125   Region r;
1126   r.x = posX+region.windowx; r.y = posY+region.windowy; r.w = width; r.h = height;
1127   //now convert to our display
1128   float scalex=720.f/((float) (region.framewidth+1));
1129   float scaley=576.f/((float) (region.frameheight+1));
1130   r.x=floor(scalex*((float)r.x));
1131   r.y=floor(scaley*((float)r.y));
1132   r.w=ceil(scalex*((float)r.w))+1.f;
1133   r.h=ceil(scaley*((float)r.h))+1.f;
1134
1135   rectangle(r, transparent);
1136   boxstack->update(this, &r);
1137 }