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