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