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