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