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