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