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