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