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