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