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