]> git.vomp.tv Git - vompclient.git/blob - vvideorec.cc
Update for windows
[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., 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       doBar(2);
244       player->skipBackward(10);
245       return 2;
246     }
247     case Remote::BLUE:
248     {
249       doBar(1);
250       player->skipForward(10);
251       return 2;
252     }
253     case Remote::GREEN:
254     {
255       doAudioSelector();
256       return 2;
257     }
258     case Remote::FULL:
259     case Remote::TV:
260     {
261       toggleChopSides();
262       return 2;
263     }
264
265     case Remote::OK:
266     {
267       if (barShowing) removeBar();
268       else doBar(0);
269       return 2;
270     }
271
272     case Remote::ZERO:  player->jumpToPercent(0);  doBar(0);  return 2;
273     case Remote::ONE:   player->jumpToPercent(10); doBar(0);  return 2;
274     case Remote::TWO:   player->jumpToPercent(20); doBar(0);  return 2;
275     case Remote::THREE: player->jumpToPercent(30); doBar(0);  return 2;
276     case Remote::FOUR:  player->jumpToPercent(40); doBar(0);  return 2;
277     case Remote::FIVE:  player->jumpToPercent(50); doBar(0);  return 2;
278     case Remote::SIX:   player->jumpToPercent(60); doBar(0);  return 2;
279     case Remote::SEVEN: player->jumpToPercent(70); doBar(0);  return 2;
280     case Remote::EIGHT: player->jumpToPercent(80); doBar(0);  return 2;
281     case Remote::NINE:  player->jumpToPercent(90); doBar(0);  return 2;
282
283 #ifdef DEV
284     case Remote::RED:
285     {
286       //Don't use RED for anything. It will eventually be recording summary
287
288       //player->test1();
289
290
291       /*
292       // for testing EPG in NTSC with a NTSC test video
293       Video::getInstance()->setMode(Video::QUARTER);
294       Video::getInstance()->setPosition(170, 5);
295       VEpg* vepg = new VEpg(NULL, 0);
296       vepg->draw();
297       ViewMan::getInstance()->add(vepg);
298       ViewMan::getInstance()->updateView(vepg);
299       */
300
301       return 2;
302     }
303
304 #endif
305
306   }
307
308   return 1;
309 }
310
311 void VVideoRec::processMessage(Message* m)
312 {
313   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Message received");
314
315   if (m->from == player)
316   {
317     if (m->message != Message::PLAYER_EVENT) return;
318     switch(m->parameter)
319     {
320       case Player::CONNECTION_LOST: // connection lost detected
321       {
322         // I can't handle this, send it to command
323         Message* m = new Message();
324         m->to = Command::getInstance();
325         m->message = Message::CONNECTION_LOST;
326         Command::getInstance()->postMessageNoLock(m);
327         break;
328       }
329       case Player::STOP_PLAYBACK:
330       {
331         // FIXME Obselete ish - improve this
332         Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
333         m->to = Command::getInstance();
334         m->message = Message::STOP_PLAYBACK;
335         Command::getInstance()->postMessageNoLock(m);
336         break;
337       }
338       case Player::ASPECT43:
339       {
340         if (dowss)
341         {
342           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
343           wss.setWide(false);
344           wss.draw();
345           viewman->updateView(this, &wssRegion);
346         }
347         break;
348       }
349       case Player::ASPECT169:
350       {
351         if (dowss)
352         {
353           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
354           wss.setWide(true);
355           wss.draw();
356           viewman->updateView(this, &wssRegion);
357         }
358         break;
359       }
360     }
361   }
362   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
363   {
364     Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received change audio channel to %i", m->parameter);
365     player->setAudioChannel(m->parameter);
366   }
367   else if (m->message == Message::CHILD_CLOSE)
368   {
369     if (m->from == vas)
370     {
371       vas = NULL;
372       barVasHold = false;
373       if (!barGenHold && !barScanHold && !barVasHold) removeBar();
374     }
375   }
376 }
377
378 void VVideoRec::stopPlay()
379 {
380   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Pre stopPlay");
381
382   // FIXME work out a better soln for this
383   // Fix a problem to do with thread sync here
384   // because the bar gets a timer every 0.2s and it seems to take up to 0.1s,
385   // (or maybe just the wrong thread being selected?) for the main loop to lock and process
386   // the video stop message it is possible for a bar message to stack up after a stop message
387   // when the bar message is finally processed the prog crashes because this is deleted by then
388   removeBar();
389   //
390
391   player->stop();
392   vdr->stopStreaming();
393   delete player;
394
395   playing = false;
396
397   if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; }
398   Log::getInstance()->log("VVideoRec", Log::DEBUG, "Post stopPlay");
399 }
400
401 void VVideoRec::toggleChopSides()
402 {
403   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
404
405   if (videoMode == Video::NORMAL)
406   {
407     videoMode = Video::LETTERBOX;
408     video->setMode(Video::LETTERBOX);
409   }
410   else
411   {
412     videoMode = Video::NORMAL;
413     video->setMode(Video::NORMAL);
414   }
415 }
416
417 void VVideoRec::doAudioSelector()
418 {
419   bool* availableAudioChannels = player->getDemuxerAudioChannels();
420   int currentAudioChannel = player->getCurrentAudioChannel();
421
422   vas = new VAudioSelector(this, availableAudioChannels, currentAudioChannel, myRec->recInfo);
423   vas->setBackgroundColour(barBlue);
424   if (video->getFormat() == Video::PAL)
425   {
426 //    vas->setScreenPos(62, barRegion.y - 120);
427     vas->setScreenPos(0, barRegion.y - 120);
428   }
429   else
430   {
431 //    vas->setScreenPos(57, barRegion.y - 120);
432     vas->setScreenPos(0, barRegion.y - 120);
433   }
434
435   barVasHold = true;
436   doBar(0);
437
438   vas->draw();
439   viewman->add(vas);
440   viewman->updateView(vas);
441 }
442
443 void VVideoRec::doBar(int action)
444 {
445   barShowing = true;
446
447   rectangle(barRegion, barBlue);
448
449   /* Work out what to display - choices:
450
451   Playing  >
452   Paused   ||
453   FFwd     >>
454   FBwd     <<
455
456   Specials, informed by parameter
457
458   Skip forward 10s    >|
459   Skip backward 10s   |<
460   Skip forward 1m     >>|
461   Skip backward 1m    |<<
462
463   */
464
465   WSymbol w;
466   w.setSurface(surface);
467   w.nextSymbol = 0;
468   w.setSurfaceOffset(barRegion.x + 66, barRegion.y + 16);
469
470   UCHAR playerState = 0;
471
472   if (action)
473   {
474     if (action == 1)       w.nextSymbol = WSymbol::SKIPFORWARD;
475     else if (action == 2)  w.nextSymbol = WSymbol::SKIPBACK;
476     else if (action == 3)  w.nextSymbol = WSymbol::SKIPFORWARD2;
477     else if (action == 4)  w.nextSymbol = WSymbol::SKIPBACK2;
478   }
479   else
480   {
481     playerState = player->getState();
482     if (playerState == Player::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
483     else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
484     else if (playerState == Player::S_FFWD)    w.nextSymbol = WSymbol::FFWD;
485     else if (playerState == Player::S_FBWD)    w.nextSymbol = WSymbol::FBWD;
486     else                                       w.nextSymbol = WSymbol::PLAY;
487   }
488
489   w.draw();
490
491   if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD))
492   {
493     // draw blips to show how fast the scan is
494     UCHAR scanrate = player->getIScanRate();
495     if (scanrate >= 2)
496     {
497       char* text = new char[5];
498       SNPRINTF(text, 5, "%ux", scanrate);
499       drawText(text, barRegion.x + 102, barRegion.y + 12, Colour::LIGHTTEXT);
500     }
501   }
502
503   drawBarClocks();
504
505   viewman->updateView(this, &barRegion);
506
507   timers->cancelTimer(this, 1);
508
509
510   if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD)) barScanHold = true;
511   else barScanHold = false;
512
513   if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4);
514
515   timers->setTimerD(this, 2, 0, 200000000);
516 }
517
518 void VVideoRec::timercall(int clientReference)
519 {
520   switch(clientReference)
521   {
522     case 1:
523     {
524       // Remove bar
525       removeBar();
526       break;
527     }
528     case 2:
529     {
530       // Update clock
531       if (!barShowing) break;
532       drawBarClocks();
533       viewman->updateView(this, &barRegion);
534       timers->setTimerD(this, 2, 0, 200000000);
535       break;
536     }
537   }
538 }
539
540 void VVideoRec::drawBarClocks()
541 {
542   if (barScanHold)
543   {
544     UCHAR playerState = player->getState();
545     // sticky bar is set if we are in ffwd/fbwd mode
546     // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which
547     // will repaint all the bar (it will call this function again, but
548     // this section won't run because stickyBarF will then == false)
549
550     if ((playerState != Player::S_FFWD) && (playerState != Player::S_FBWD))
551     {
552       barScanHold = false;
553       doBar(0);
554       return; // doBar will call this function and do the rest
555     }
556   }
557
558   Log* logger = Log::getInstance();
559   logger->log("VVideoRec", Log::DEBUG, "Draw bar clocks");
560
561   // Draw RTC
562   // Blank the area first
563   rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
564   char timeString[20];
565   time_t t;
566   time(&t);
567   struct tm* tms = localtime(&t);
568   strftime(timeString, 19, "%H:%M", tms);
569   drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT);
570
571   // Draw clocks
572
573   rectangle(clocksRegion, barBlue);
574
575   ULONG currentFrameNum = player->getCurrentFrameNum();
576   ULONG lengthFrames;
577   if (myRec->recInfo->timerEnd > time(NULL))
578   {
579     // chasing playback
580     // Work out an approximate length in frames (good to 1s...)
581     lengthFrames = (myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * video->getFPS();
582   }
583   else
584   {
585     lengthFrames = player->getLengthFrames();
586   }
587
588   hmsf currentFrameHMSF = video->framesToHMSF(currentFrameNum);
589   hmsf lengthHMSF = video->framesToHMSF(lengthFrames);
590
591   char buffer[100];
592   if (currentFrameNum >= lengthFrames)
593   {
594     strcpy(buffer, "-:--:-- / -:--:--");
595   }
596   else
597   {
598     SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds);
599     logger->log("VVideoRec", Log::DEBUG, buffer);
600   }
601
602   drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
603
604
605
606
607
608
609
610   // Draw progress bar
611   int progBarXbase = barRegion.x + 300;
612
613   rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT);
614   rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
615
616   if (currentFrameNum > lengthFrames) return;
617   if (lengthFrames == 0) return;
618
619   // Draw yellow portion
620   int progressWidth = 302 * currentFrameNum / lengthFrames;
621   rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT);
622
623   if (myRec->recInfo->timerEnd > time(NULL)) // if chasing
624   {
625     int nrWidth = (int)(302 * ((double)(lengthFrames - player->getLengthFrames()) / lengthFrames));
626
627     Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames);
628     Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames());
629     Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth);
630     rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, Colour::RED);
631   }
632
633   // Now calc position for start margin blips
634   int posPix;
635
636   posPix = 302 * startMargin * video->getFPS() / lengthFrames;
637
638   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT);
639   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT);
640
641   posPix = 302 * (lengthFrames - endMargin * video->getFPS()) / lengthFrames;
642
643   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT);
644   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT);
645 }
646
647 void VVideoRec::removeBar()
648 {
649   if (!barShowing) return;
650   timers->cancelTimer(this, 2);
651   barShowing = false;
652   barGenHold = false;
653   barScanHold = false;
654   barVasHold = false;
655   rectangle(barRegion, transparent);
656   viewman->updateView(this, &barRegion);
657 }