]> git.vomp.tv Git - vompclient.git/blob - vradiorec.cc
New options screen system, new remote control handling, and all other
[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
223       /*
224       // for testing EPG in NTSC with a NTSC test video
225       Video::getInstance()->setMode(Video::QUARTER);
226       Video::getInstance()->setPosition(170, 5);
227       VEpg* vepg = new VEpg(NULL, 0);
228       vepg->draw();
229       ViewMan::getInstance()->add(vepg);
230       ViewMan::getInstance()->updateView(vepg);
231       */
232
233       return 2;
234     }
235     case Remote::GREEN:
236     {
237       //player->test2();
238       return 2;
239     }
240 #endif
241
242   }
243
244   return 1;
245 }
246
247 void VRadioRec::processMessage(Message* m)
248 {
249   if (m->message == Message::MOUSE_LBDOWN)
250   {
251     int x=(m->parameter>>16)-(int)getScreenX();
252     int y=(m->parameter&0xFFFF)-(int)getScreenY();
253     if (!barShowing)
254     {
255       ViewMan::getInstance()->handleCommand(Remote::OK); //simulate rok press
256     }
257     else if ((int)barRegion.x<=x && (int)barRegion.y<=y && ((int)barRegion.x+(int)barRegion.w)>=x
258              &&  ((int)barRegion.y+(int)barRegion.h)>=y)
259     {
260       int progBarXbase = barRegion.x + 300;
261       if (x>=(int)barRegion.x + progBarXbase + 24
262         && x<=(int)barRegion.x + progBarXbase + 4 + 302
263         && y>=(int)barRegion.y + 12 - 2
264         && y<=(int)barRegion.y + 12 - 2+28)
265       {
266         int cx=x-(barRegion.x + progBarXbase + 4);
267         double percent=((double)cx)/302.*100.;
268         player->jumpToPercent(percent);
269         doBar(3);
270         return;
271         //  int progressWidth = 302 * currentFrameNum / lengthFrames;
272         //  rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT);
273       }
274     }
275     else
276     {
277       ViewMan::getInstance()->handleCommand(Remote::OK); //simulate rok press
278     }
279   }
280   else if (m->message == Message::PLAYER_EVENT)
281   {
282     if (m->from != player) return;
283
284     Log::getInstance()->log("VRadioRec", Log::DEBUG, "Message received");
285
286     switch(m->parameter)
287     {
288       case Player::CONNECTION_LOST: // connection lost detected
289       {
290         // I can't handle this, send it to command
291         Message* m2 = new Message();
292         m2->to = Command::getInstance();
293         m2->message = Message::CONNECTION_LOST;
294         Command::getInstance()->postMessageNoLock(m2);
295         break;
296       }
297       case Player::STOP_PLAYBACK:
298       {
299         // FIXME Obselete ish - improve this
300         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
301         m2->to = Command::getInstance();
302         m2->message = Message::STOP_PLAYBACK;
303         Command::getInstance()->postMessageNoLock(m2);
304         break;
305       }
306     }
307   }
308 }
309
310 void VRadioRec::stopPlay()
311 {
312   Log::getInstance()->log("VRadioRec", Log::DEBUG, "Pre stopPlay");
313
314   // FIXME work out a better soln for this
315   // Fix a problem to do with thread sync here
316   // because the bar gets a timer every 0.2s and it seems to take up to 0.1s,
317   // (or maybe just the wrong thread being selected?) for the main loop to lock and process
318   // the video stop message it is possible for a bar message to stack up after a stop message
319   // when the bar message is finally processed the prog crashes because this is deleted by then
320   removeBar();
321   //
322
323   player->stop();
324   vdr->stopStreaming();
325   delete player;
326
327   playing = false;
328
329   if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; }
330   Log::getInstance()->log("VRadioRec", Log::DEBUG, "Post stopPlay");
331 }
332
333 void VRadioRec::doBar(int action)
334 {
335   barShowing = true;
336
337   rectangle(barRegion, barBlue);
338
339   /* Work out what to display - choices:
340
341   Playing  >
342   Paused   ||
343
344   Specials, informed by parameter
345
346   Skip forward 10s    >|
347   Skip backward 10s   |<
348   Skip forward 1m     >>|
349   Skip backward 1m    |<<
350
351   */
352
353   WSymbol w;
354   w.setSurface(surface);
355   w.nextSymbol = 0;
356   w.setSurfaceOffset(barRegion.x + 66, barRegion.y + 16);
357
358   UCHAR playerState = 0;
359
360   if (action)
361   {
362     if (action == 1)       w.nextSymbol = WSymbol::SKIPFORWARD;
363     else if (action == 2)  w.nextSymbol = WSymbol::SKIPBACK;
364     else if (action == 3)  w.nextSymbol = WSymbol::SKIPFORWARD2;
365     else if (action == 4)  w.nextSymbol = WSymbol::SKIPBACK2;
366   }
367   else
368   {
369     playerState = player->getState();
370     if (playerState == Player::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
371     else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
372     else                                       w.nextSymbol = WSymbol::PLAY;
373   }
374
375   w.draw();
376
377   drawBarClocks();
378
379   ViewMan::getInstance()->updateView(this, &barRegion);
380
381   timers->setTimerD(this, 1, 4); // only set the getridofbar timer if not ffwd/fbwd
382   timers->setTimerD(this, 2, 0, 200000000);
383 }
384
385 void VRadioRec::timercall(int clientReference)
386 {
387   switch(clientReference)
388   {
389     case 1:
390     {
391       // Remove bar
392       removeBar();
393       break;
394     }
395     case 2:
396     {
397       // Update clock
398       if (!barShowing) break;
399       drawBarClocks();
400       Message* m = new Message();
401       m->message = Message::REDRAW;
402       m->to = ViewMan::getInstance();
403       m->from = this;
404       m->parameter = (ULONG)&barRegion;
405       Command::getInstance()->postMessageFromOuterSpace(m);
406       timers->setTimerD(this, 2, 0, 200000000);
407       break;
408     }
409   }
410 }
411
412 void VRadioRec::drawBarClocks()
413 {
414   Log* logger = Log::getInstance();
415   logger->log("VRadioRec", Log::DEBUG, "Draw bar clocks");
416
417   // Draw RTC
418   // Blank the area first
419   rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
420   char timeString[20];
421   time_t t;
422   time(&t);
423   struct tm* tms = localtime(&t);
424   strftime(timeString, 19, "%H:%M", tms);
425   drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT);
426
427   // Draw clocks
428
429   rectangle(clocksRegion, barBlue);
430
431 /*
432   ULONG currentFrameNum = player->getCurrentFrameNum();
433   ULONG lengthFrames;
434   if (myRec->recInfo->timerEnd > time(NULL))
435   {
436     // chasing playback
437     // Work out an approximate length in frames (good to 1s...)
438     lengthFrames = (myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * video->getFPS();
439   }
440   else
441   {
442 //    lengthFrames = player->getLengthFrames();
443     lengthFrames = 0;
444   }
445
446   hmsf currentFrameHMSF = video->framesToHMSF(currentFrameNum);
447   hmsf lengthHMSF = video->framesToHMSF(lengthFrames);
448
449   char buffer[100];
450   if (currentFrameNum >= lengthFrames)
451   {
452     strcpy(buffer, "-:--:-- / -:--:--");
453   }
454   else
455   {
456     SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds);
457     logger->log("VRadioRec", Log::DEBUG, buffer);
458   }
459 */
460
461   ULONG currentSeconds = player->getCurrentSeconds();
462   ULONG lengthSeconds = player->getLengthSeconds();
463   char buffer[100];
464
465   if (lengthSeconds && (currentSeconds < lengthSeconds))
466   {
467     ULONG dcurrentSeconds = currentSeconds;
468     ULONG dlengthSeconds = lengthSeconds;
469
470     ULONG currentHours = dcurrentSeconds / 3600;
471     dcurrentSeconds %= 3600;
472     ULONG currentMinutes = dcurrentSeconds / 60;
473     dcurrentSeconds %= 60;
474
475     ULONG lengthHours = dlengthSeconds / 3600;
476     dlengthSeconds %= 3600;
477     ULONG lengthMinutes = dlengthSeconds / 60;
478     dlengthSeconds %= 60;
479
480     SNPRINTF(buffer, 99, "%01lu:%02lu:%02lu / %01lu:%02lu:%02lu", currentHours, currentMinutes, dcurrentSeconds, lengthHours, lengthMinutes, dlengthSeconds);
481     logger->log("VRadioRec", Log::DEBUG, buffer);
482   }
483   else
484   {
485     strcpy(buffer, "-:--:-- / -:--:--");
486   }
487
488   drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
489
490
491
492
493   // Draw progress bar
494   int progBarXbase = barRegion.x + 300;
495
496   rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT);
497   rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
498
499   if (currentSeconds > lengthSeconds) return;
500   if (lengthSeconds == 0) return;
501
502   // Draw yellow portion
503   int progressWidth = 302 * currentSeconds / lengthSeconds;
504   rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT);
505 /*
506
507   if (myRec->recInfo->timerEnd > time(NULL)) // if chasing
508   {
509     int nrWidth = (int)(302 * ((double)(lengthFrames - 0) / lengthFrames)); // 0 inserted instead of getlengthframes
510
511     Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames);
512 //    Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames());
513     Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth);
514     rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, Colour::RED);
515   }
516 */
517
518   logger->log("VRadioRec", Log::DEBUG, "blips");
519
520   // Now calc position for start margin blips
521   int posPix;
522
523   posPix = 302 * startMargin / lengthSeconds;
524   logger->log("VRadioRec", Log::DEBUG, "posPix %i", posPix);
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   posPix = 302 * (lengthSeconds - endMargin) / lengthSeconds;
530
531   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT);
532   rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT);
533 }
534
535 void VRadioRec::removeBar()
536 {
537   if (!barShowing) return;
538   timers->cancelTimer(this, 2);
539   barShowing = false;
540   rectangle(barRegion, Colour::BLACK);
541   ViewMan::getInstance()->updateView(this, &barRegion);
542 }