]> git.vomp.tv Git - vompclient-marten.git/blob - vvideorec.cc
Windows fixes
[vompclient-marten.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "vvideorec.h"
22
23 VVideoRec::VVideoRec(Recording* rec)
24 {
25   viewman = ViewMan::getInstance();
26   vdr = VDR::getInstance();
27   video = Video::getInstance();
28   timers = Timers::getInstance();
29   vas = NULL;
30
31   player = new Player(Command::getInstance(), this, true);
32   player->init();
33
34   videoMode = video->getMode();
35   myRec = rec;
36
37   playing = false;
38
39   startMargin = 0;
40   endMargin = 0;
41   char* cstartMargin = vdr->configLoad("Timers", "Start margin");
42   char* cendMargin = vdr->configLoad("Timers", "End margin");
43   if (!cstartMargin)
44   {
45     startMargin = 300; // 5 mins default
46   }
47   else
48   {
49     startMargin = atoi(cstartMargin) * 60;
50     delete[] cstartMargin;
51   }
52
53   if (!cendMargin)
54   {
55     endMargin = 300; // 5 mins default
56   }
57   else
58   {
59     endMargin = atoi(cendMargin) * 60;
60     delete[] cendMargin;
61   }
62
63   Log::getInstance()->log("VVideoRec", Log::DEBUG, "SM: %u EM: %u", startMargin, endMargin);
64
65   create(video->getScreenWidth(), video->getScreenHeight());
66   transparent.set(0, 0, 0, 0);
67   setBackgroundColour(transparent);
68
69   barRegion.x = 0;
70   barRegion.y = video->getScreenHeight() - 58;   // FIXME, need to be - 1? and below?
71   barRegion.w = video->getScreenWidth();
72   barRegion.h = 58;
73
74   clocksRegion.x = barRegion.x + 140;
75   clocksRegion.y = barRegion.y + 12;
76   clocksRegion.w = 170;
77   clocksRegion.h = surface->getFontHeight();
78
79
80   barBlue.set(0, 0, 150, 150);
81
82   barShowing = false;
83   barGenHold = false;
84   barScanHold = false;
85   barVasHold = false;
86
87   dowss = false;
88   char* optionWSS = vdr->configLoad("General", "WSS");
89   if (optionWSS)
90   {
91     if (strstr(optionWSS, "Yes")) dowss = true;
92     delete[] optionWSS;
93   }
94   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Do WSS: %u", dowss);
95
96   wss.setFormat(video->getFormat());
97   wss.setSurface(surface);
98   wss.setWide(true);
99
100 /*
101   wssRegion.x = 0;
102   wssRegion.y = 6;
103   wssRegion.w = video->getScreenWidth();
104   wssRegion.h = 2;
105 */
106   wssRegion.x = 0;
107   wssRegion.y = 0;
108   wssRegion.w = video->getScreenWidth();
109   wssRegion.h = 300;
110 }
111
112 VVideoRec::~VVideoRec()
113 {
114   if (vas)
115   {
116     viewman->removeView(vas);
117     vas = NULL;
118   }
119
120   if (playing) stopPlay();
121   video->setDefaultAspect();
122
123   timers->cancelTimer(this, 1);
124   timers->cancelTimer(this, 2);
125
126   // kill recInfo in case resumePoint has changed (likely)
127   myRec->dropRecInfo();
128   // FIXME - do this properly - save the resume point back to the server manually and update
129   // rec->recInfo->resumePoint - this will fix the ~10s offset problem as well
130 }
131
132 void VVideoRec::draw()
133 {
134   View::draw();
135 }
136
137 void VVideoRec::go(bool resume)
138 {
139   ULONG startFrameNum;
140   if (resume)
141     startFrameNum = myRec->recInfo->resumePoint;
142   else
143     startFrameNum = 0;
144
145   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Starting stream: %s at frame: %lu", myRec->getFileName(), startFrameNum);
146   ULONG lengthFrames = 0;
147   ULLONG lengthBytes = vdr->streamRecording(myRec->getFileName(), &lengthFrames);
148   if (lengthBytes)
149   {
150     player->setLengthBytes(lengthBytes);
151     player->setLengthFrames(lengthFrames);
152     player->setStartFrame(startFrameNum);
153     player->play();
154     playing = true;
155     doBar(0);
156   }
157   else
158   {
159     stopPlay(); // clean up
160
161     if (!vdr->isConnected())
162     {
163       Command::getInstance()->connectionLost();
164       return;
165     }
166
167     Message* m = new Message();
168     m->message = Message::CLOSE_ME;
169     m->from = this;
170     m->to = viewman;
171     Command::getInstance()->postMessageNoLock(m);
172
173     VInfo* vi = new VInfo();
174     vi->create(400, 150);
175     if (video->getFormat() == Video::PAL)
176       vi->setScreenPos(170, 200);
177     else
178       vi->setScreenPos(160, 150);
179     vi->setExitable();
180     vi->setBorderOn(1);
181     vi->setTitleBarOn(0);
182     vi->setOneLiner(tr("Error playing recording"));
183     vi->draw();
184
185     m = new Message();
186     m->message = Message::ADD_VIEW;
187     m->to = viewman;
188     m->parameter = (ULONG)vi;
189     Command::getInstance()->postMessageNoLock(m);
190   }
191 }
192
193 int VVideoRec::handleCommand(int command)
194 {
195   switch(command)
196   {
197     case Remote::PLAY:
198     {
199       player->play();
200       doBar(0);
201       return 2;
202     }
203
204     case Remote::STOP:
205     case Remote::BACK:
206     case Remote::MENU:
207     {
208       if (playing) stopPlay();
209       return 4;
210     }
211     case Remote::PAUSE:
212     {
213       player->pause();
214       doBar(0);
215       return 2;
216     }
217     case Remote::SKIPFORWARD:
218     {
219       doBar(3);
220       player->skipForward(60);
221       return 2;
222     }
223     case Remote::SKIPBACK:
224     {
225       doBar(4);
226       player->skipBackward(60);
227       return 2;
228     }
229     case Remote::FORWARD:
230     {
231       player->fastForward();
232       doBar(0);
233       return 2;
234     }
235     case Remote::REVERSE:
236     {
237       player->fastBackward();
238       doBar(0);
239       return 2;
240     }
241     case Remote::YELLOW:
242     {
243       if (myRec->hasMarks())
244       {
245         // skip to previous mark
246         Log* logger = Log::getInstance();
247         int currentFrame = (player->getCurrentFrameNum()); // get current Frame
248         currentFrame -= 5 * video->getFPS(); // subtrack 5 seconds, else you cannot skip more than once back ..
249
250         int prevMark = myRec->getPrevMark(currentFrame); // find previous Frame
251         if (prevMark)
252         {
253           logger->log("VVideoRec", Log::NOTICE, "jump back from pos %i to mark at %i",currentFrame,prevMark);
254           player->jumpToMark(prevMark);
255         }
256         doBar(4);
257       }
258       else
259       {
260         doBar(2);
261         player->skipBackward(10);
262       }
263       return 2;
264     }
265     case Remote::BLUE:
266     {
267       if (myRec->hasMarks())
268       {
269         // skip to next mark
270         Log* logger = Log::getInstance();
271         int currentFrame = (player->getCurrentFrameNum());
272
273         int nextMark = myRec->getNextMark(currentFrame);
274
275         if (nextMark)
276         {
277           logger->log("VVideoRec", Log::NOTICE, "jump forward from pos %i to mark at %i",currentFrame,nextMark);
278           player->jumpToMark(nextMark);
279         }
280         doBar(3);
281       }
282       else
283       {
284         doBar(1);
285         player->skipForward(10);
286       }
287       return 2;
288     }
289     case Remote::STAR:
290     {
291       doBar(2);
292       player->skipBackward(10);
293       return 2;
294     }
295     case Remote::HASH:
296     {
297       doBar(1);
298       player->skipForward(10);
299       return 2;
300     }
301     case Remote::GREEN:
302     {
303       doAudioSelector();
304       return 2;
305     }
306     case Remote::FULL:
307     case Remote::TV:
308     {
309       toggleChopSides();
310       return 2;
311     }
312
313     case Remote::OK:
314     {
315       if (barShowing) removeBar();
316       else doBar(0);
317       return 2;
318     }
319
320     case Remote::ZERO:  player->jumpToPercent(0);  doBar(0);  return 2;
321     case Remote::ONE:   player->jumpToPercent(10); doBar(0);  return 2;
322     case Remote::TWO:   player->jumpToPercent(20); doBar(0);  return 2;
323     case Remote::THREE: player->jumpToPercent(30); doBar(0);  return 2;
324     case Remote::FOUR:  player->jumpToPercent(40); doBar(0);  return 2;
325     case Remote::FIVE:  player->jumpToPercent(50); doBar(0);  return 2;
326     case Remote::SIX:   player->jumpToPercent(60); doBar(0);  return 2;
327     case Remote::SEVEN: player->jumpToPercent(70); doBar(0);  return 2;
328     case Remote::EIGHT: player->jumpToPercent(80); doBar(0);  return 2;
329     case Remote::NINE:  player->jumpToPercent(90); doBar(0);  return 2;
330
331 #ifdef DEV
332     case Remote::RED:
333     {
334       //Don't use RED for anything. It will eventually be recording summary
335
336       //player->test1();
337
338
339       /*
340       // for testing EPG in NTSC with a NTSC test video
341       Video::getInstance()->setMode(Video::QUARTER);
342       Video::getInstance()->setPosition(170, 5);
343       VEpg* vepg = new VEpg(NULL, 0);
344       vepg->draw();
345       ViewMan::getInstance()->add(vepg);
346       ViewMan::getInstance()->updateView(vepg);
347       */
348
349       return 2;
350     }
351
352 #endif
353
354   }
355
356   return 1;
357 }
358
359 void VVideoRec::processMessage(Message* m)
360 {
361   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Message received");
362
363   if (m->from == player)
364   {
365     if (m->message != Message::PLAYER_EVENT) return;
366     switch(m->parameter)
367     {
368       case Player::CONNECTION_LOST: // connection lost detected
369       {
370         // I can't handle this, send it to command
371         Message* m = new Message();
372         m->to = Command::getInstance();
373         m->message = Message::CONNECTION_LOST;
374         Command::getInstance()->postMessageNoLock(m);
375         break;
376       }
377       case Player::STOP_PLAYBACK:
378       {
379         // FIXME Obselete ish - improve this
380         Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
381         m->to = Command::getInstance();
382         m->message = Message::STOP_PLAYBACK;
383         Command::getInstance()->postMessageNoLock(m);
384         break;
385       }
386       case Player::ASPECT43:
387       {
388         if (dowss)
389         {
390           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
391           wss.setWide(false);
392           wss.draw();
393           viewman->updateView(this, &wssRegion);
394         }
395         break;
396       }
397       case Player::ASPECT169:
398       {
399         if (dowss)
400         {
401           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
402           wss.setWide(true);
403           wss.draw();
404           viewman->updateView(this, &wssRegion);
405         }
406         break;
407       }
408     }
409   }
410   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
411   {
412     Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received change audio channel to %i", m->parameter);
413     player->setAudioChannel(m->parameter);
414   }
415   else if (m->message == Message::CHILD_CLOSE)
416   {
417     if (m->from == vas)
418     {
419       vas = NULL;
420       barVasHold = false;
421       if (!barGenHold && !barScanHold && !barVasHold) removeBar();
422     }
423   }
424 }
425
426 void VVideoRec::stopPlay()
427 {
428   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Pre stopPlay");
429
430   // FIXME work out a better soln for this
431   // Fix a problem to do with thread sync here
432   // because the bar gets a timer every 0.2s and it seems to take up to 0.1s,
433   // (or maybe just the wrong thread being selected?) for the main loop to lock and process
434   // the video stop message it is possible for a bar message to stack up after a stop message
435   // when the bar message is finally processed the prog crashes because this is deleted by then
436   removeBar();
437   //
438
439   player->stop();
440   vdr->stopStreaming();
441   delete player;
442
443   playing = false;
444
445   if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; }
446   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Post stopPlay");
447 }
448
449 void VVideoRec::toggleChopSides()
450 {
451   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
452
453   if (videoMode == Video::NORMAL)
454   {
455     videoMode = Video::LETTERBOX;
456     video->setMode(Video::LETTERBOX);
457   }
458   else
459   {
460     videoMode = Video::NORMAL;
461     video->setMode(Video::NORMAL);
462   }
463 }
464
465 void VVideoRec::doAudioSelector()
466 {
467   bool* availableAudioChannels = player->getDemuxerAudioChannels();
468   int currentAudioChannel = player->getCurrentAudioChannel();
469
470   vas = new VAudioSelector(this, availableAudioChannels, currentAudioChannel, myRec->recInfo);
471   vas->setBackgroundColour(barBlue);
472   if (video->getFormat() == Video::PAL)
473   {
474 //    vas->setScreenPos(62, barRegion.y - 120);
475     vas->setScreenPos(0, barRegion.y - 120);
476   }
477   else
478   {
479 //    vas->setScreenPos(57, barRegion.y - 120);
480     vas->setScreenPos(0, barRegion.y - 120);
481   }
482
483   barVasHold = true;
484   doBar(0);
485
486   vas->draw();
487   viewman->add(vas);
488   viewman->updateView(vas);
489 }
490
491 void VVideoRec::doBar(int action)
492 {
493   barShowing = true;
494
495   rectangle(barRegion, barBlue);
496
497   /* Work out what to display - choices:
498
499   Playing  >
500   Paused   ||
501   FFwd     >>
502   FBwd     <<
503
504   Specials, informed by parameter
505
506   Skip forward 10s    >|
507   Skip backward 10s   |<
508   Skip forward 1m     >>|
509   Skip backward 1m    |<<
510
511   */
512
513   WSymbol w;
514   w.setSurface(surface);
515   w.nextSymbol = 0;
516   w.setSurfaceOffset(barRegion.x + 66, barRegion.y + 16);
517
518   UCHAR playerState = 0;
519
520   if (action)
521   {
522     if (action == 1)       w.nextSymbol = WSymbol::SKIPFORWARD;
523     else if (action == 2)  w.nextSymbol = WSymbol::SKIPBACK;
524     else if (action == 3)  w.nextSymbol = WSymbol::SKIPFORWARD2;
525     else if (action == 4)  w.nextSymbol = WSymbol::SKIPBACK2;
526   }
527   else
528   {
529     playerState = player->getState();
530     if (playerState == Player::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
531     else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
532     else if (playerState == Player::S_FFWD)    w.nextSymbol = WSymbol::FFWD;
533     else if (playerState == Player::S_FBWD)    w.nextSymbol = WSymbol::FBWD;
534     else                                       w.nextSymbol = WSymbol::PLAY;
535   }
536
537   w.draw();
538
539   if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD))
540   {
541     // draw blips to show how fast the scan is
542     UCHAR scanrate = player->getIScanRate();
543     if (scanrate >= 2)
544     {
545       char* text = new char[5];
546       SNPRINTF(text, 5, "%ux", scanrate);
547       drawText(text, barRegion.x + 102, barRegion.y + 12, Colour::LIGHTTEXT);
548     }
549   }
550
551   drawBarClocks();
552
553   viewman->updateView(this, &barRegion);
554
555   timers->cancelTimer(this, 1);
556
557
558   if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD)) barScanHold = true;
559   else barScanHold = false;
560
561   if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4);
562
563   timers->setTimerD(this, 2, 0, 200000000);
564 }
565
566 void VVideoRec::timercall(int clientReference)
567 {
568   switch(clientReference)
569   {
570     case 1:
571     {
572       // Remove bar
573       removeBar();
574       break;
575     }
576     case 2:
577     {
578       // Update clock
579       if (!barShowing) break;
580       drawBarClocks();
581       viewman->updateView(this, &barRegion);
582       timers->setTimerD(this, 2, 0, 200000000);
583       break;
584     }
585   }
586 }
587
588 void VVideoRec::drawBarClocks()
589 {
590   if (barScanHold)
591   {
592     UCHAR playerState = player->getState();
593     // sticky bar is set if we are in ffwd/fbwd mode
594     // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which
595     // will repaint all the bar (it will call this function again, but
596     // this section won't run because stickyBarF will then == false)
597
598     if ((playerState != Player::S_FFWD) && (playerState != Player::S_FBWD))
599     {
600       barScanHold = false;
601       doBar(0);
602       return; // doBar will call this function and do the rest
603     }
604   }
605
606   Log* logger = Log::getInstance();
607   logger->log("VVideoRec", Log::DEBUG, "Draw bar clocks");
608
609   // Draw RTC
610   // Blank the area first
611   rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
612   char timeString[20];
613   time_t t;
614   time(&t);
615   struct tm* tms = localtime(&t);
616   strftime(timeString, 19, "%H:%M", tms);
617   drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT);
618
619   // Draw clocks
620
621   rectangle(clocksRegion, barBlue);
622
623   ULONG currentFrameNum = player->getCurrentFrameNum();
624   ULONG lengthFrames;
625   if (myRec->recInfo->timerEnd > time(NULL))
626   {
627     // chasing playback
628     // Work out an approximate length in frames (good to 1s...)
629     lengthFrames = (myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * video->getFPS();
630   }
631   else
632   {
633     lengthFrames = player->getLengthFrames();
634   }
635
636   hmsf currentFrameHMSF = video->framesToHMSF(currentFrameNum);
637   hmsf lengthHMSF = video->framesToHMSF(lengthFrames);
638
639   char buffer[100];
640   if (currentFrameNum >= lengthFrames)
641   {
642     strcpy(buffer, "-:--:-- / -:--:--");
643   }
644   else
645   {
646     SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds);
647     logger->log("VVideoRec", Log::DEBUG, buffer);
648   }
649
650   drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
651
652
653
654
655
656
657
658   // Draw progress bar
659   int progBarXbase = barRegion.x + 300;
660
661   rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT);
662   rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
663
664   if (currentFrameNum > lengthFrames) return;
665   if (lengthFrames == 0) return;
666
667   // Draw yellow portion
668   int progressWidth = 302 * currentFrameNum / lengthFrames;
669   rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT);
670
671   if (myRec->recInfo->timerEnd > time(NULL)) // if chasing
672   {
673     int nrWidth = (int)(302 * ((double)(lengthFrames - player->getLengthFrames()) / lengthFrames));
674
675     Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames);
676     Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames());
677     Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth);
678     rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, Colour::RED);
679   }
680
681   int posPix;
682   // Now calc position for blips
683
684   if (myRec->hasMarks())
685   {
686     // Draw blips where there are cut marks
687     MarkList* markList = myRec->getMarkList();
688     MarkList::iterator i;
689     Mark* loopMark = NULL;
690
691     for(i = markList->begin(); i != markList->end(); i++)
692     {
693       loopMark = *i;
694       if (loopMark->pos)
695       {
696         logger->log("VVideoRec", Log::DEBUG, "Drawing mark at frame %i", loopMark->pos);
697         posPix = 302 * loopMark->pos / lengthFrames;
698         rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 3, 28, Colour::DANGER);
699       }
700     }
701   }
702   else
703   {
704     // Draw blips where start and end margins probably are
705
706     posPix = 302 * startMargin * video->getFPS() / lengthFrames;
707
708     rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT);
709     rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT);
710
711     posPix = 302 * (lengthFrames - endMargin * video->getFPS()) / lengthFrames;
712
713     rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT);
714     rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT);
715   }
716 }
717
718 void VVideoRec::removeBar()
719 {
720   if (!barShowing) return;
721   timers->cancelTimer(this, 2);
722   barShowing = false;
723   barGenHold = false;
724   barScanHold = false;
725   barVasHold = false;
726   rectangle(barRegion, transparent);
727   viewman->updateView(this, &barRegion);
728 }