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