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