]> git.vomp.tv Git - vompclient.git/blob - vradiorec.cc
DrawStyle: Add consts, add white and transparent statics
[vompclient.git] / vradiorec.cc
1 /*
2     Copyright 2004-2020 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, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "control.h"
21 #include "osd.h"
22 #include "wsymbol.h"
23 #include "recording.h"
24 #include "recinfo.h"
25 #include "message.h"
26 #include "vdr.h"
27 #include "video.h"
28 #include "playerradiorec.h"
29 #include "boxstack.h"
30 #include "input.h"
31 #include "vinfo.h"
32 #include "i18n.h"
33 #include "log.h"
34 #include "messagequeue.h"
35
36 #include "vradiorec.h"
37
38 VRadioRec::VRadioRec(Recording* rec)
39 {
40   boxstack = BoxStack::getInstance();
41   vdr = VDR::getInstance();
42   video = Video::getInstance();
43   timers = Timers::getInstance();
44   myRec = rec;
45   playing = false;
46   startMargin = 0;
47   endMargin = 0;
48
49   player = new PlayerRadioRec(Control::getInstance(), this);
50
51   char* cstartMargin = vdr->configLoad("Timers", "Start margin");
52   char* cendMargin = vdr->configLoad("Timers", "End margin");
53   if (!cstartMargin)
54   {
55     startMargin = 300; // 5 mins default
56   }
57   else
58   {
59     startMargin = atoi(cstartMargin) * 60;
60     delete[] cstartMargin;
61   }
62
63   if (!cendMargin)
64   {
65     endMargin = 300; // 5 mins default
66   }
67   else
68   {
69     endMargin = atoi(cendMargin) * 60;
70     delete[] cendMargin;
71   }
72
73   Log::getInstance()->log("VRadioRec", Log::DEBUG, "SM: %u EM: %u", startMargin, endMargin);
74
75   setSize(video->getScreenWidth(), video->getScreenHeight());
76   createBuffer();
77   setBackgroundColour(DrawStyle::TRANSPARENT);
78   setPosition(0, 0);
79
80   barRegion.x = 0;
81   barRegion.y = video->getScreenHeight() - 58;   // FIXME, need to be - 1? and below?
82   barRegion.w = video->getScreenWidth();
83   barRegion.h = 58;
84
85   clocksRegion.x = barRegion.x + 140;
86   clocksRegion.y = barRegion.y + 12;
87   clocksRegion.w = 170;
88   clocksRegion.h = getFontHeight();
89
90
91   barBlue.set(0, 0, 0, 128);
92
93   barShowing = false;
94 }
95
96 void VRadioRec::preDelete()
97 {
98   timers->cancelTimer(this, 1);
99   timers->cancelTimer(this, 2);
100 }
101
102 VRadioRec::~VRadioRec()
103 {
104   if (playing) stopPlay();
105
106
107   // kill recInfo in case resumePoint has changed (likely)
108   myRec->dropRecInfo();
109   // FIXME - do this properly - save the resume point back to the server manually and update
110   // rec->recInfo->resumePoint - this will fix the ~10s offset problem as well
111 }
112
113 void VRadioRec::draw()
114 {
115   fillColour(DrawStyle::TRANSPARENT);
116 }
117
118 void VRadioRec::go(bool resume)
119 {
120   ULONG startFrameNum;
121   if (resume)
122     startFrameNum = myRec->recInfo->resumePoint;
123   else
124     startFrameNum = 0;
125
126   Log::getInstance()->log("VRadioRec", Log::DEBUG, "Starting stream: %s", myRec->getFileName());
127   ULONG lengthFrames = 0;
128   bool isPesRecording;
129   ULLONG lengthBytes = vdr->streamRecording(myRec->getFileName(), &lengthFrames, &isPesRecording);
130   myRec->IsPesRecording = isPesRecording;
131
132   bool cantStart = false;
133
134   if (!lengthBytes) cantStart = true;
135   else if (!player->init(lengthBytes, lengthFrames, myRec->IsPesRecording)) cantStart = true;
136   else
137   {
138     doBar(0);
139     player->setCurrentFrameNumber(startFrameNum);
140     player->play();
141     playing = true;
142   }
143
144   if (cantStart)
145   {
146     stopPlay(); // clean up
147
148     if (!vdr->isConnected())
149     {
150       Control::getInstance()->connectionLost();
151       return;
152     }
153
154     Message* m = new Message();
155     m->message = Message::CLOSE_ME;
156     m->from = this;
157     m->p_to = Message::BOXSTACK;
158     MessageQueue::getInstance()->postMessage(m);
159
160     VInfo* vi = new VInfo();
161     vi->setSize(400, 150);
162     vi->createBuffer();
163     if (video->getFormat() == Video::PAL)
164       vi->setPosition(170, 200);
165     else
166       vi->setPosition(160, 150);
167     vi->setExitable();
168     vi->setBorderOn(1);
169     vi->setTitleBarOn(0);
170     vi->setOneLiner(tr("Error playing recording"));
171     vi->draw();
172
173     m = new Message();
174     m->message = Message::ADD_VIEW;
175     m->p_to = Message::BOXSTACK;
176     m->data = reinterpret_cast<void*>(vi);
177     MessageQueue::getInstance()->postMessage(m);
178   }
179 }
180
181 int VRadioRec::handleCommand(int command)
182 {
183   switch(command)
184   {
185     case Input::PLAY:
186     {
187       player->play();
188       doBar(0);
189       return 2;
190     }
191     case Input::PLAYPAUSE:
192     {
193         player->playpause();
194         doBar(0);
195         return 2;
196     }
197
198     case Input::STOP:
199     case Input::BACK:
200     case Input::MENU:
201     {
202       if (playing) stopPlay();
203       return 4;
204     }
205     case Input::PAUSE:
206     {
207       player->pause();
208       doBar(0);
209       return 2;
210     }
211     case Input::SKIPFORWARD:
212     {
213       doBar(3);
214       player->skipForward(60);
215       return 2;
216     }
217     case Input::SKIPBACK:
218     {
219       doBar(4);
220       player->skipBackward(60);
221       return 2;
222     }
223     case Input::YELLOW:
224     {
225       doBar(2);
226       player->skipBackward(10);
227       return 2;
228     }
229     case Input::BLUE:
230     {
231       doBar(1);
232       player->skipForward(10);
233       return 2;
234     }
235     case Input::OK:
236     {
237       if (barShowing) removeBar();
238       else doBar(0);
239       return 2;
240     }
241
242     case Input::ZERO:  player->jumpToPercent(0);  doBar(0);  return 2;
243     case Input::ONE:   player->jumpToPercent(10); doBar(0);  return 2;
244     case Input::TWO:   player->jumpToPercent(20); doBar(0);  return 2;
245     case Input::THREE: player->jumpToPercent(30); doBar(0);  return 2;
246     case Input::FOUR:  player->jumpToPercent(40); doBar(0);  return 2;
247     case Input::FIVE:  player->jumpToPercent(50); doBar(0);  return 2;
248     case Input::SIX:   player->jumpToPercent(60); doBar(0);  return 2;
249     case Input::SEVEN: player->jumpToPercent(70); doBar(0);  return 2;
250     case Input::EIGHT: player->jumpToPercent(80); doBar(0);  return 2;
251     case Input::NINE:  player->jumpToPercent(90); doBar(0);  return 2;
252
253 #ifdef DEV
254     case Input::RED:
255     {
256       //player->test1();
257
258       return 2;
259     }
260     case Input::GREEN:
261     {
262       //player->test2();
263       return 2;
264     }
265 #endif
266
267   }
268
269   return 1;
270 }
271
272 void VRadioRec::processMessage(Message* m)
273 {
274   if (m->message == Message::MOUSE_LBDOWN)
275   {
276     UINT x = m->parameter - getScreenX();
277     UINT y = m->tag - getScreenY();
278     if (!barShowing)
279     {
280       boxstack->handleCommand(Input::OK); //simulate rok press
281     }
282     else if (    (barRegion.x <= x)                  // If the click happened within the bar region...
283               && (barRegion.y <= y)
284               && ((barRegion.x + barRegion.w) >= x)
285               && ((barRegion.y + barRegion.h) >= y))
286     {
287       UINT progBarXbase = barRegion.x + 300;
288       if ( x >= barRegion.x + progBarXbase + 24
289         && x <= barRegion.x + progBarXbase + 4 + 302
290         && y >= barRegion.y + 12 - 2
291         && y <= barRegion.y + 12 - 2+28)
292       {
293         int cx = x - (barRegion.x + progBarXbase + 4);
294         double percent = 100 * cx / 302.;
295         player->jumpToPercent(percent); // FIXME check this still works
296         doBar(3);
297         return;
298         //  int progressWidth = 302 * currentFrameNum / lengthFrames;
299         //  rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
300       }
301     }
302     else
303     {
304       boxstack->handleCommand(Input::OK); //simulate rok press
305     }
306   }
307   else if (m->message == Message::PLAYER_EVENT)
308   {
309     if (m->from != player) return;
310
311     Log::getInstance()->log("VRadioRec", Log::DEBUG, "Message received");
312
313     switch(m->parameter)
314     {
315       case PlayerRadioRec::CONNECTION_LOST: // connection lost detected
316       {
317         // I can't handle this, send it to control
318         Message* m2 = new Message();
319         m2->p_to = Message::CONTROL;
320         m2->message = Message::CONNECTION_LOST;
321         MessageQueue::getInstance()->postMessage(m2);
322         break;
323       }
324       case PlayerRadioRec::STOP_PLAYBACK:
325       {
326         // FIXME Obselete ish - improve this
327         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
328         m2->p_to = Message::CONTROL;
329         m2->message = Message::STOP_PLAYBACK;
330         MessageQueue::getInstance()->postMessage(m2);
331         break;
332       }
333     }
334   }
335 }
336
337 void VRadioRec::stopPlay()
338 {
339   Log::getInstance()->log("VRadioRec", Log::DEBUG, "Pre stopPlay");
340
341   removeBar();
342   player->stop();
343   vdr->stopStreaming();
344   delete player;
345
346   playing = false;
347
348   if (!vdr->isConnected()) { Control::getInstance()->connectionLost(); return; }
349   Log::getInstance()->log("VRadioRec", Log::DEBUG, "Post stopPlay");
350 }
351
352 void VRadioRec::doBar(int action)
353 {
354   barShowing = true;
355
356   rectangle(barRegion, barBlue);
357
358   /* Work out what to display - choices:
359
360   Playing  >
361   Paused   ||
362
363   Specials, informed by parameter
364
365   Skip forward 10s    >|
366   Skip backward 10s   |<
367   Skip forward 1m     >>|
368   Skip backward 1m    |<<
369
370   */
371
372   WSymbol w;
373   TEMPADD(&w);
374   w.nextSymbol = 0;
375   w.setPosition(barRegion.x + 66, barRegion.y + 16);
376
377   UCHAR playerState = 0;
378
379   if (action)
380   {
381     if (action == 1)       w.nextSymbol = WSymbol::SKIPFORWARD;
382     else if (action == 2)  w.nextSymbol = WSymbol::SKIPBACK;
383     else if (action == 3)  w.nextSymbol = WSymbol::SKIPFORWARD2;
384     else if (action == 4)  w.nextSymbol = WSymbol::SKIPBACK2;
385   }
386   else
387   {
388     playerState = player->getState();
389     if (playerState == PlayerRadioRec::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
390     else if (playerState == PlayerRadioRec::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
391     else                                            w.nextSymbol = WSymbol::PLAY;
392   }
393
394   w.draw();
395
396   drawBarClocks();
397
398   BoxStack::getInstance()->update(this, &barRegion);
399
400   timers->setTimerD(this, 1, 4); // only set the getridofbar timer if not ffwd/fbwd
401   timers->setTimerD(this, 2, 0, 200000000);
402 }
403
404 void VRadioRec::timercall(int clientReference)
405 {
406   switch(clientReference)
407   {
408     case 1:
409     {
410       // Remove bar
411       removeBar();
412       break;
413     }
414     case 2:
415     {
416       // Update clock
417       if (!barShowing) break;
418       drawBarClocks();
419       boxstack->update(this, &barRegion);
420       
421       timers->setTimerD(this, 2, 0, 200000000);
422       break;
423     }
424   }
425 }
426
427 void VRadioRec::drawBarClocks()
428 {
429   Log* logger = Log::getInstance();
430   logger->log("VRadioRec", Log::DEBUG, "Draw bar clocks");
431
432   // Draw RTC
433   // Blank the area first
434   rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
435   char timeString[20];
436   time_t t;
437   time(&t);
438   struct tm tms;
439   LOCALTIME_R(&t, &tms);
440   strftime(timeString, 19, "%H:%M", &tms);
441   drawText(timeString, barRegion.x + 624, barRegion.y + 12, DrawStyle::LIGHTTEXT);
442
443   // Draw clocks
444
445   rectangle(clocksRegion, barBlue);
446
447   ULONG currentSeconds = player->getCurrentSeconds();
448   ULONG lengthSeconds = player->getLengthSeconds();
449   char buffer[100];
450
451   if (lengthSeconds && (currentSeconds < lengthSeconds))
452   {
453     ULONG dcurrentSeconds = currentSeconds;
454     ULONG dlengthSeconds = lengthSeconds;
455
456     ULONG currentHours = dcurrentSeconds / 3600;
457     dcurrentSeconds %= 3600;
458     ULONG currentMinutes = dcurrentSeconds / 60;
459     dcurrentSeconds %= 60;
460
461     ULONG lengthHours = dlengthSeconds / 3600;
462     dlengthSeconds %= 3600;
463     ULONG lengthMinutes = dlengthSeconds / 60;
464     dlengthSeconds %= 60;
465
466     SNPRINTF(buffer, 99, "%01lu:%02lu:%02lu / %01lu:%02lu:%02lu", currentHours, currentMinutes, dcurrentSeconds, lengthHours, lengthMinutes, dlengthSeconds);
467     logger->log("VRadioRec", Log::DEBUG, buffer);
468   }
469   else
470   {
471     strcpy(buffer, "-:--:-- / -:--:--");
472   }
473
474   drawText(buffer, clocksRegion.x, clocksRegion.y, DrawStyle::LIGHTTEXT);
475
476   // Draw progress bar
477   int progBarXbase = barRegion.x + 300;
478
479   rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, DrawStyle::LIGHTTEXT);
480   rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
481
482   if (currentSeconds > lengthSeconds) return;
483   if (lengthSeconds == 0) return;
484
485   // Draw yellow portion
486   int progressWidth = 302 * currentSeconds / lengthSeconds;
487   rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
488 /*
489
490   if (myRec->recInfo->timerEnd > time(NULL)) // if chasing
491   {
492     int nrWidth = (int)(302 * ((double)(lengthFrames - 0) / lengthFrames)); // 0 inserted instead of getlengthframes
493
494     Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames);
495 //    Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames());
496     Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth);
497     rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, DrawStyle::RED);
498   }
499 */
500
501   logger->log("VRadioRec", Log::DEBUG, "blips");
502
503   // Now calc position for start margin blips
504   int posPix;
505
506   posPix = 302 * startMargin / lengthSeconds;
507   logger->log("VRadioRec", Log::DEBUG, "posPix %i", posPix);
508
509   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, DrawStyle::LIGHTTEXT);
510   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, DrawStyle::LIGHTTEXT);
511
512   posPix = 302 * (lengthSeconds - endMargin) / lengthSeconds;
513
514   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, DrawStyle::LIGHTTEXT);
515   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, DrawStyle::LIGHTTEXT);
516 }
517
518 void VRadioRec::removeBar()
519 {
520   if (!barShowing) return;
521   timers->cancelTimer(this, 2);
522   barShowing = false;
523   rectangle(barRegion, DrawStyle::TRANSPARENT);
524   boxstack->update(this, &barRegion);
525 }
526