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