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