2 Copyright 2004-2005 Chris Tallon
\r
4 This file is part of VOMP.
\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
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
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
21 #include "vvideomedia.h"
\r
22 #include "vmedialist.h"
\r
24 #include "mediaplayer.h"
\r
26 #include "command.h"
\r
28 #include "wsymbol.h"
\r
32 #include "playermedia.h"
\r
33 #include "recording.h"
\r
34 #include "vaudioselector.h"
\r
35 #include "message.h"
\r
37 #include "boxstack.h"
\r
41 #include "recinfo.h"
\r
43 //use the picture channel
\r
44 #define MEDIACHANNEL 1
\r
46 //we misuse PLAYER_EVENTS for timer messages
\r
47 //this should be larger then any player message
\r
48 #define PLAYER_TIMER_BASE 100
\r
50 VVideoMedia::VVideoMedia(Media* media, VMediaList *p)
\r
53 boxstack = BoxStack::getInstance();
\r
54 video = Video::getInstance();
\r
55 timers = Timers::getInstance();
\r
59 myMedia = new Media(media);
\r
61 player = new PlayerMedia(this);
\r
64 videoMode = video->getMode();
\r
69 setSize(video->getScreenWidth(), video->getScreenHeight());
\r
71 transparent.set(0, 0, 0, 0);
\r
72 setBackgroundColour(transparent);
\r
75 barRegion.y = video->getScreenHeight() - 58; // FIXME, need to be - 1? and below?
\r
76 barRegion.w = video->getScreenWidth();
\r
79 clocksRegion.x = barRegion.x + 140;
\r
80 clocksRegion.y = barRegion.y + 12;
\r
81 clocksRegion.w = 170;
\r
82 clocksRegion.h = getFontHeight();
\r
85 barBlue.set(0, 0, 150, 150);
\r
89 barScanHold = false;
\r
93 char* optionWSS = VDR::getInstance()->configLoad("General", "WSS");
\r
96 if (strstr(optionWSS, "Yes")) dowss = true;
\r
99 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Do WSS: %u", dowss);
\r
103 wss.setFormat(video->getFormat());
\r
109 wssRegion.w = video->getScreenWidth();
\r
114 VVideoMedia::~VVideoMedia()
\r
116 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Entering destructor");
\r
120 boxstack->remove(vas);
\r
129 if (playing) stopPlay();
\r
130 video->setDefaultAspect();
\r
132 timers->cancelTimer(this, 1);
\r
133 timers->cancelTimer(this, 2);
\r
136 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "shutting down player");
\r
137 player->shutdown();
\r
139 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "deleted");
\r
142 void VVideoMedia::go(bool resume)
\r
144 ULONG startFrameNum=0;
\r
146 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Starting stream: %s at frame: %lu", myMedia->getFileName(), startFrameNum);
\r
151 const MediaURI *u=myMedia->getURI();
\r
153 Log::getInstance()->log("VVideoMedia", Log::ERR, "stream: %s has no URI", myMedia->getFileName());
\r
157 rt=MediaPlayer::getInstance()->openMedium(MEDIACHANNEL,u,&lengthBytes,area.w,area.h);
\r
161 //TODO: figure out len in frames
\r
162 int seq=player->playNew(MEDIACHANNEL,lengthBytes,0);
\r
163 int ok=player->waitForSequence(2,seq);
\r
164 if (ok < 0) rt=-1;
\r
172 stopPlay(); // clean up
\r
174 Message* m = new Message();
\r
175 m->message = Message::CLOSE_ME;
\r
178 Command::getInstance()->postMessageNoLock(m);
\r
180 VInfo* vi = new VInfo();
\r
181 vi->setSize(400, 150);
\r
182 vi->createBuffer();
\r
183 if (video->getFormat() == Video::PAL)
\r
184 vi->setPosition(170, 200);
\r
186 vi->setPosition(160, 150);
\r
188 vi->setBorderOn(1);
\r
189 vi->setTitleBarOn(0);
\r
190 vi->setOneLiner(tr("Error playing media"));
\r
194 m->message = Message::ADD_VIEW;
\r
196 m->parameter = (ULONG)vi;
\r
197 Command::getInstance()->postMessageNoLock(m);
\r
201 int VVideoMedia::handleCommand(int command)
\r
223 if (playing) stopPlay();
\r
227 case Remote::PAUSE:
\r
233 case Remote::SKIPFORWARD:
\r
236 player->skipForward(60);
\r
239 case Remote::SKIPBACK:
\r
242 player->skipBackward(60);
\r
245 case Remote::FORWARD:
\r
247 player->fastForward();
\r
251 case Remote::REVERSE:
\r
253 player->fastBackward();
\r
259 if (vsummary) removeSummary();
\r
263 case Remote::GREEN:
\r
268 case Remote::YELLOW:
\r
271 player->skipBackward(10);
\r
277 player->skipForward(10);
\r
283 player->skipBackward(10);
\r
289 player->skipForward(10);
\r
307 if (barShowing) removeBar();
\r
315 case Remote::ZERO: player->jumpToPercent(0); doBar(0); return 2;
\r
316 case Remote::ONE: player->jumpToPercent(10); doBar(0); return 2;
\r
317 case Remote::TWO: player->jumpToPercent(20); doBar(0); return 2;
\r
318 case Remote::THREE: player->jumpToPercent(30); doBar(0); return 2;
\r
319 case Remote::FOUR: player->jumpToPercent(40); doBar(0); return 2;
\r
320 case Remote::FIVE: player->jumpToPercent(50); doBar(0); return 2;
\r
321 case Remote::SIX: player->jumpToPercent(60); doBar(0); return 2;
\r
322 case Remote::SEVEN: player->jumpToPercent(70); doBar(0); return 2;
\r
323 case Remote::EIGHT: player->jumpToPercent(80); doBar(0); return 2;
\r
324 case Remote::NINE: player->jumpToPercent(90); doBar(0); return 2;
\r
332 void VVideoMedia::processMessage(Message* m)
\r
334 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Message received");
\r
336 if (m->message == Message::MOUSE_LBDOWN)
\r
338 UINT x = (m->parameter>>16) - getScreenX();
\r
339 UINT y = (m->parameter&0xFFFF) - getScreenY();
\r
343 BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
\r
345 else if (barRegion.x<=x && barRegion.y<=y && (barRegion.x+barRegion.w)>=x && (barRegion.y+barRegion.h)>=y)
\r
347 int progBarXbase = barRegion.x + 300;
\r
348 if (x>=barRegion.x + progBarXbase + 24
\r
349 && x<=barRegion.x + progBarXbase + 4 + 302
\r
350 && y>=barRegion.y + 12 - 2
\r
351 && y<=barRegion.y + 12 - 2+28)
\r
353 int cx=x-(barRegion.x + progBarXbase + 4);
\r
354 double percent=((double)cx)/302.*100.;
\r
355 player->jumpToPercent(percent);
\r
358 // int progressWidth = 302 * currentFrameNum / lengthFrames;
\r
359 // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT);
\r
364 BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
\r
367 else if (m->message == Message::PLAYER_EVENT)
\r
369 switch(m->parameter)
\r
371 case PlayerMedia::CONNECTION_LOST: // connection lost detected
\r
373 // I can't handle this, send it to command
\r
374 Message* m2 = new Message();
\r
375 m2->to = Command::getInstance();
\r
376 m2->message = Message::CONNECTION_LOST;
\r
377 Command::getInstance()->postMessageNoLock(m2);
\r
380 case PlayerMedia::STREAM_END:
\r
382 Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
\r
383 m2->to = BoxStack::getInstance();
\r
384 m2->message = Message::CLOSE_ME;
\r
385 Command::getInstance()->postMessageNoLock(m2);
\r
388 case PlayerMedia::STATUS_CHANGE:
\r
391 case PlayerMedia::ASPECT43:
\r
395 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 43");
\r
396 wss.setWide(false);
\r
398 boxstack->update(this, &wssRegion);
\r
402 case PlayerMedia::ASPECT169:
\r
406 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 169");
\r
409 boxstack->update(this, &wssRegion);
\r
413 case (PLAYER_TIMER_BASE+1) :
\r
418 case (PLAYER_TIMER_BASE+2) :
\r
421 if (!barShowing) break;
\r
423 BoxStack::getInstance()->update(this,&barRegion);
\r
424 if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000);
\r
425 else timers->setTimerD(this, 2, 1);
\r
429 else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
\r
431 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received change audio channel to %i", m->parameter);
\r
432 player->setAudioChannel(m->parameter);
\r
434 else if (m->message == Message::CHILD_CLOSE)
\r
436 if (m->from == vas)
\r
439 barVasHold = false;
\r
440 if (!barGenHold && !barScanHold && !barVasHold) removeBar();
\r
445 void VVideoMedia::stopPlay()
\r
447 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Pre stopPlay");
\r
454 MediaPlayer::getInstance()->closeMediaChannel(MEDIACHANNEL);
\r
456 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Post stopPlay");
\r
459 void VVideoMedia::toggleChopSides()
\r
461 if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
\r
463 if (videoMode == Video::NORMAL)
\r
465 videoMode = Video::LETTERBOX;
\r
466 video->setMode(Video::LETTERBOX);
\r
470 videoMode = Video::NORMAL;
\r
471 video->setMode(Video::NORMAL);
\r
475 void VVideoMedia::doAudioSelector()
\r
477 bool* availableMpegAudioChannels = player->getDemuxerMpegAudioChannels();
\r
478 bool* availableAc3AudioChannels = 0;
\r
479 int currentAudioChannel = player->getCurrentAudioChannel();
\r
480 if (Audio::getInstance()->supportsAc3())
\r
482 availableAc3AudioChannels = player->getDemuxerAc3AudioChannels();
\r
487 ri.summary=new char[strlen(myMedia->getDisplayName())+1];
\r
488 strcpy(ri.summary,myMedia->getDisplayName());
\r
489 vas = new VAudioSelector(this, availableMpegAudioChannels, availableAc3AudioChannels, currentAudioChannel, NULL,NULL,0,0, &ri);
\r
491 vas->setBackgroundColour(barBlue);
\r
492 vas->setPosition(0, barRegion.y - 120);
\r
500 boxstack->add(vas);
\r
501 boxstack->update(vas);
\r
504 void VVideoMedia::doBar(int action)
\r
506 Log::getInstance()->log("VVideoMedia",Log::DEBUG,"doBar %d",action);
\r
509 rectangle(barRegion, barBlue);
\r
511 /* Work out what to display - choices:
\r
518 Specials, informed by parameter
\r
520 Skip forward 10s >|
\r
521 Skip backward 10s |<
\r
522 Skip forward 1m >>|
\r
523 Skip backward 1m |<<
\r
530 w.setPosition(barRegion.x + 66, barRegion.y + 16);
\r
532 UCHAR playerState = 0;
\r
536 if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD;
\r
537 else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK;
\r
538 else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2;
\r
539 else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2;
\r
543 playerState = player->getState();
\r
544 if (playerState == PlayerMedia::S_PLAY) w.nextSymbol = WSymbol::PLAY;
\r
545 else if (playerState == PlayerMedia::S_FF) w.nextSymbol = WSymbol::FFWD;
\r
546 else if (playerState == PlayerMedia::S_BACK) w.nextSymbol = WSymbol::FBWD;
\r
547 else if (playerState == PlayerMedia::S_SEEK) w.nextSymbol = WSymbol::RIGHTARROW;
\r
548 else if (playerState == PlayerMedia::S_STOP) w.nextSymbol = WSymbol::PAUSE;
\r
549 else w.nextSymbol = WSymbol::PAUSE;
\r
554 if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK))
\r
556 // draw blips to show how fast the scan is
\r
557 UCHAR scanrate = 2;//player->getIScanRate();
\r
561 SNPRINTF(text, 5, "%ux", scanrate);
\r
562 drawText(text, barRegion.x + 102, barRegion.y + 12, Colour::LIGHTTEXT);
\r
567 boxstack->update(this, &barRegion);
\r
569 timers->cancelTimer(this, 1);
\r
572 if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK)) barScanHold = true;
\r
573 else barScanHold = false;
\r
575 if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4);
\r
577 if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000);
\r
578 else timers->setTimerD(this, 2, 1);
\r
581 void VVideoMedia::timercall(int clientReference)
\r
583 Message *m=new Message();
\r
584 m->message=Message::PLAYER_EVENT;
\r
587 m->parameter=PLAYER_TIMER_BASE+clientReference;
\r
588 Command::getInstance()->postMessageFromOuterSpace(m);
\r
591 void VVideoMedia::drawBarClocks()
\r
595 UCHAR playerState = player->getState();
\r
596 // sticky bar is set if we are in ffwd/fbwd mode
\r
597 // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which
\r
598 // will repaint all the bar (it will call this function again, but
\r
599 // this section won't run because stickyBarF will then == false)
\r
601 if ((playerState != PlayerMedia::S_FF) && (playerState != PlayerMedia::S_BACK))
\r
603 barScanHold = false;
\r
609 Log* logger = Log::getInstance();
\r
610 logger->log("VVideoMedia", Log::DEBUG, "Draw bar clocks");
\r
613 // Blank the area first
\r
614 rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
\r
615 char timeString[20];
\r
618 struct tm* tms = localtime(&t);
\r
619 strftime(timeString, 19, "%H:%M", tms);
\r
620 drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT);
\r
622 ULLONG lenPTS=player->getLenPTS();
\r
625 rectangle(clocksRegion, barBlue);
\r
627 ULLONG currentPTS = player->getCurrentPTS();
\r
629 hmsf currentFrameHMSF = ptsToHMS(currentPTS);
\r
630 hmsf lengthHMSF = ptsToHMS(lenPTS);
\r
633 if (currentPTS > lenPTS && lenPTS != 0)
\r
635 strcpy(buffer, "-:--:-- / -:--:--");
\r
639 SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds);
\r
641 logger->log("VVideoMedia", Log::DEBUG, "cur %llu,len %llu, txt %s",currentPTS,lenPTS,buffer);
\r
643 drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
\r
651 // Draw progress bar
\r
652 int progBarXbase = barRegion.x + 300;
\r
654 if (lenPTS == 0) return;
\r
655 rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT);
\r
656 rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
\r
658 if (currentPTS > lenPTS) return;
\r
660 // Draw yellow portion
\r
661 int progressWidth = 302 * currentPTS / lenPTS;
\r
662 rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT);
\r
666 void VVideoMedia::removeBar()
\r
668 if (!barShowing) return;
\r
669 timers->cancelTimer(this, 2);
\r
670 barShowing = false;
\r
671 barGenHold = false;
\r
672 barScanHold = false;
\r
673 barVasHold = false;
\r
674 rectangle(barRegion, transparent);
\r
675 BoxStack::getInstance()->update(this, &barRegion);
\r
678 void VVideoMedia::doSummary()
\r
680 vsummary = new VInfo();
\r
681 vsummary->setTitleText(myMedia->getDisplayName());
\r
682 vsummary->setBorderOn(1);
\r
683 vsummary->setExitable();
\r
684 const MediaURI *u=myMedia->getURI();
\r
687 stlen+=strlen(myMedia->getFileName());
\r
688 stlen+=strlen(tr("FileName"))+10;
\r
690 stlen+=strlen(tr("Size"))+50;
\r
691 stlen+=strlen(tr("Directory"))+500;
\r
692 stlen+=strlen(tr("Time"))+50;
\r
693 char *pinfo=player->getInfo();
\r
694 stlen+=strlen(pinfo)+10;
\r
695 char *buf=new char [stlen];
\r
696 char *tsbuf=new char [stlen];
\r
697 char *tsbuf2=new char [stlen];
\r
698 char *tbuf=new char[Media::TIMEBUFLEN];
\r
699 SNPRINTF(buf,stlen,"%s\n"
\r
704 shortendedText(tr("FileName"),": ",myMedia->getFileName(),tsbuf,vsummary->getWidth()),
\r
707 shortendedText(tr("Directory"),": ",lparent->getDirname(MEDIA_TYPE_VIDEO),tsbuf2,vsummary->getWidth()),
\r
709 myMedia->getTimeString(tbuf),
\r
714 Log::getInstance()->log("VVideoMedia",Log::DEBUG,"info %s",buf);
\r
715 vsummary->setMainText(buf);
\r
717 else vsummary->setMainText(tr("Info unavailable"));
\r
719 if (Video::getInstance()->getFormat() == Video::PAL)
\r
721 vsummary->setPosition(70, 100);
\r
725 vsummary->setPosition(40, 70);
\r
727 vsummary->setSize(580, 350);
\r
731 BoxStack::getInstance()->update(this);
\r
738 void VVideoMedia::removeSummary()
\r
746 BoxStack::getInstance()->update(this);
\r
751 hmsf VVideoMedia::ptsToHMS(ULLONG pts) {
\r
752 ULLONG secs=pts/90000;
\r
755 rt.seconds=secs%60;
\r
757 rt.minutes=secs%60;
\r
763 char * VVideoMedia::shortendedText(const char * title, const char * title2,const char * intext,char *buffer,UINT width) {
\r
768 for (const char *p=title;*p!=0;p++) twidth+=charWidth(*p);
\r
769 for (const char *p=title2;*p!=0;p++) twidth+=charWidth(*p);
\r
770 const char *prfx="...";
\r
771 UINT prfwidth=3*charWidth('.');
\r
772 const char *istart=intext+strlen(intext);
\r
774 while (twidth+iwidth+prfwidth < width-2*paraMargin && istart> intext) {
\r
776 iwidth+=charWidth(*istart);
\r
778 if (twidth+iwidth+prfwidth >= width-2*paraMargin && istart < intext+strlen(intext)) istart++;
\r
779 if (istart == intext) prfx="";
\r
780 sprintf(buffer,"%s%s%s%s",title,title2,prfx,intext);
\r