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