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