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