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, see <https://www.gnu.org/licenses/>.
20 #include "vvideomedia.h"
21 #include "vmedialist.h"
23 #include "mediaplayer.h"
29 #include "playermedia.h"
30 #include "recording.h"
31 #include "vaudioselector.h"
39 #include "messagequeue.h"
41 //use the picture channel
42 #define MEDIACHANNEL 1
44 //we misuse PLAYER_EVENTS for timer messages
45 //this should be larger then any player message
46 #define PLAYER_TIMER_BASE 100
48 VVideoMedia::VVideoMedia(Media* media, VMediaList *p)
51 boxstack = BoxStack::getInstance();
52 video = Video::getInstance();
53 timers = Timers::getInstance();
57 myMedia = new Media(media);
59 player = new PlayerMedia(this);
62 videoMode = video->getMode();
67 setSize(video->getScreenWidth(), video->getScreenHeight());
69 setBackgroundColour(DrawStyle::TRANSPARENT);
72 barRegion.y = video->getScreenHeight() - 58; // FIXME, need to be - 1? and below?
73 barRegion.w = video->getScreenWidth();
76 clocksRegion.x = barRegion.x + 140;
77 clocksRegion.y = barRegion.y + 12;
79 clocksRegion.h = getFontHeight();
82 barBlue.set(0, 0, 150, 150);
90 char* optionWSS = VDR::getInstance()->configLoad("General", "WSS");
93 if (strstr(optionWSS, "Yes")) dowss = true;
96 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Do WSS: %u", dowss);
100 wss.setFormat(video->getFormat());
106 wssRegion.w = video->getScreenWidth();
111 VVideoMedia::~VVideoMedia()
113 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Entering destructor");
117 boxstack->remove(vas);
126 if (playing) stopPlay();
127 video->setDefaultAspect();
129 timers->cancelTimer(this, 1);
130 timers->cancelTimer(this, 2);
133 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "shutting down player");
136 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "deleted");
139 void VVideoMedia::go(bool resume)
141 ULONG startFrameNum=0;
143 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Starting stream: %s at frame: %lu", myMedia->getFileName(), startFrameNum);
148 const MediaURI *u=myMedia->getURI();
150 Log::getInstance()->log("VVideoMedia", Log::ERR, "stream: %s has no URI", myMedia->getFileName());
154 rt=MediaPlayer::getInstance()->openMedium(MEDIACHANNEL,u,&lengthBytes,area.w,area.h);
158 //TODO: figure out len in frames
159 int seq=player->playNew(MEDIACHANNEL,lengthBytes,0);
160 int ok=player->waitForSequence(2,seq);
169 stopPlay(); // clean up
171 Message* m = new Message();
172 m->message = Message::CLOSE_ME;
174 m->p_to = Message::BOXSTACK;
175 MessageQueue::getInstance()->postMessage(m);
177 VInfo* vi = new VInfo();
178 vi->setSize(400, 150);
180 if (video->getFormat() == Video::PAL)
181 vi->setPosition(170, 200);
183 vi->setPosition(160, 150);
186 vi->setTitleBarOn(0);
187 vi->setOneLiner(tr("Error playing media"));
191 m->message = Message::ADD_VIEW;
192 m->p_to = Message::BOXSTACK;
193 m->data = reinterpret_cast<void*>(vi);
194 MessageQueue::getInstance()->postMessage(m);
198 int VVideoMedia::handleCommand(int command)
220 if (playing) stopPlay();
230 case Input::SKIPFORWARD:
233 player->skipForward(60);
236 case Input::SKIPBACK:
239 player->skipBackward(60);
244 player->fastForward();
250 player->fastBackward();
256 if (vsummary) removeSummary();
268 player->skipBackward(10);
274 player->skipForward(10);
280 player->skipBackward(10);
286 player->skipForward(10);
304 if (barShowing) removeBar();
312 case Input::ZERO: player->jumpToPercent(0); doBar(0); return 2;
313 case Input::ONE: player->jumpToPercent(10); doBar(0); return 2;
314 case Input::TWO: player->jumpToPercent(20); doBar(0); return 2;
315 case Input::THREE: player->jumpToPercent(30); doBar(0); return 2;
316 case Input::FOUR: player->jumpToPercent(40); doBar(0); return 2;
317 case Input::FIVE: player->jumpToPercent(50); doBar(0); return 2;
318 case Input::SIX: player->jumpToPercent(60); doBar(0); return 2;
319 case Input::SEVEN: player->jumpToPercent(70); doBar(0); return 2;
320 case Input::EIGHT: player->jumpToPercent(80); doBar(0); return 2;
321 case Input::NINE: player->jumpToPercent(90); doBar(0); return 2;
329 void VVideoMedia::processMessage(Message* m)
331 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Message received");
333 if (m->message == Message::MOUSE_LBDOWN)
335 UINT x = m->parameter - getScreenX();
336 UINT y = m->tag - getScreenY();
340 BoxStack::getInstance()->handleCommand(Input::OK); //simulate rok press
342 else if (barRegion.x<=x && barRegion.y<=y && (barRegion.x+barRegion.w)>=x && (barRegion.y+barRegion.h)>=y)
344 int progBarXbase = barRegion.x + 300;
345 if (x>=barRegion.x + progBarXbase + 24
346 && x<=barRegion.x + progBarXbase + 4 + 302
347 && y>=barRegion.y + 12 - 2
348 && y<=barRegion.y + 12 - 2+28)
350 int cx=x-(barRegion.x + progBarXbase + 4);
351 double percent=((double)cx)/302.*100.;
352 player->jumpToPercent(percent);
355 // int progressWidth = 302 * currentFrameNum / lengthFrames;
356 // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
361 BoxStack::getInstance()->handleCommand(Input::OK); //simulate rok press
364 else if (m->message == Message::PLAYER_EVENT)
368 case PlayerMedia::CONNECTION_LOST: // connection lost detected
370 // I can't handle this, send it to control
371 Message* m2 = new Message();
372 m2->p_to = Message::CONTROL;
373 m2->message = Message::CONNECTION_LOST;
374 MessageQueue::getInstance()->postMessage(m2);
377 case PlayerMedia::STREAM_END:
379 Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
380 m2->p_to = Message::BOXSTACK;
381 m2->message = Message::CLOSE_ME;
382 MessageQueue::getInstance()->postMessage(m2);
385 case PlayerMedia::STATUS_CHANGE:
388 case PlayerMedia::ASPECT43:
392 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 43");
395 boxstack->update(this, &wssRegion);
399 case PlayerMedia::ASPECT169:
403 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 169");
406 boxstack->update(this, &wssRegion);
410 case (PLAYER_TIMER_BASE+1) :
415 case (PLAYER_TIMER_BASE+2) :
418 if (!barShowing) break;
420 BoxStack::getInstance()->update(this,&barRegion);
421 if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000);
422 else timers->setTimerD(this, 2, 1);
426 else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
428 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received change audio channel to %i", m->parameter);
429 player->setAudioChannel(m->parameter);
431 else if (m->message == Message::CHILD_CLOSE)
437 if (!barGenHold && !barScanHold && !barVasHold) removeBar();
442 void VVideoMedia::stopPlay()
444 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Pre stopPlay");
451 MediaPlayer::getInstance()->closeMediaChannel(MEDIACHANNEL);
453 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Post stopPlay");
456 void VVideoMedia::toggleChopSides()
458 if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
460 if (videoMode == Video::NORMAL)
462 videoMode = Video::LETTERBOX;
463 video->setMode(Video::LETTERBOX);
467 videoMode = Video::NORMAL;
468 video->setMode(Video::NORMAL);
472 void VVideoMedia::doAudioSelector()
474 bool* availableMpegAudioChannels = player->getDemuxerMpegAudioChannels();
475 bool* availableAc3AudioChannels = 0;
476 int currentAudioChannel = player->getCurrentAudioChannel();
477 if (Audio::getInstance()->supportsAc3())
479 availableAc3AudioChannels = player->getDemuxerAc3AudioChannels();
484 ri.summary=new char[strlen(myMedia->getDisplayName())+1];
485 strcpy(ri.summary,myMedia->getDisplayName());
486 vas = new VAudioSelector(this, availableMpegAudioChannels, availableAc3AudioChannels, currentAudioChannel, NULL,NULL,0,0, &ri);
488 vas->setBackgroundColour(barBlue);
489 vas->setPosition(0, barRegion.y - 120);
498 boxstack->update(vas);
501 void VVideoMedia::doBar(int action)
503 Log::getInstance()->log("VVideoMedia",Log::DEBUG,"doBar %d",action);
506 rectangle(barRegion, barBlue);
508 /* Work out what to display - choices:
515 Specials, informed by parameter
527 w.setPosition(barRegion.x + 66, barRegion.y + 16);
529 UCHAR playerState = 0;
533 if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD;
534 else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK;
535 else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2;
536 else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2;
540 playerState = player->getState();
541 if (playerState == PlayerMedia::S_PLAY) w.nextSymbol = WSymbol::PLAY;
542 else if (playerState == PlayerMedia::S_FF) w.nextSymbol = WSymbol::FFWD;
543 else if (playerState == PlayerMedia::S_BACK) w.nextSymbol = WSymbol::FBWD;
544 else if (playerState == PlayerMedia::S_SEEK) w.nextSymbol = WSymbol::RIGHTARROW;
545 else if (playerState == PlayerMedia::S_STOP) w.nextSymbol = WSymbol::PAUSE;
546 else w.nextSymbol = WSymbol::PAUSE;
551 if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK))
553 // draw blips to show how fast the scan is
554 UCHAR scanrate = 2;//player->getIScanRate();
558 SNPRINTF(text, 5, "%ux", scanrate);
559 drawText(text, barRegion.x + 102, barRegion.y + 12, DrawStyle::LIGHTTEXT);
564 boxstack->update(this, &barRegion);
566 timers->cancelTimer(this, 1);
569 if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK)) barScanHold = true;
570 else barScanHold = false;
572 if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4);
574 if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000);
575 else timers->setTimerD(this, 2, 1);
578 void VVideoMedia::timercall(int clientReference)
580 Message *m=new Message();
581 m->message=Message::PLAYER_EVENT;
584 m->parameter=PLAYER_TIMER_BASE+clientReference;
585 MessageQueue::getInstance()->postMessage(m);
588 void VVideoMedia::drawBarClocks()
592 UCHAR playerState = player->getState();
593 // sticky bar is set if we are in ffwd/fbwd mode
594 // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which
595 // will repaint all the bar (it will call this function again, but
596 // this section won't run because stickyBarF will then == false)
598 if ((playerState != PlayerMedia::S_FF) && (playerState != PlayerMedia::S_BACK))
606 Log* logger = Log::getInstance();
607 logger->log("VVideoMedia", Log::DEBUG, "Draw bar clocks");
610 // Blank the area first
611 rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
616 LOCALTIME_R(&t, &tms);
618 strftime(timeString, 19, "%H:%M", &tms);
619 drawText(timeString, barRegion.x + 624, barRegion.y + 12, DrawStyle::LIGHTTEXT);
621 ULLONG lenPTS=player->getLenPTS();
624 rectangle(clocksRegion, barBlue);
626 ULLONG currentPTS = player->getCurrentPTS();
628 hmsf currentFrameHMSF = ptsToHMS(currentPTS);
629 hmsf lengthHMSF = ptsToHMS(lenPTS);
632 if (currentPTS > lenPTS && lenPTS != 0)
634 strcpy(buffer, "-:--:-- / -:--:--");
638 SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds);
640 logger->log("VVideoMedia", Log::DEBUG, "cur %llu,len %llu, txt %s",currentPTS,lenPTS,buffer);
642 drawText(buffer, clocksRegion.x, clocksRegion.y, DrawStyle::LIGHTTEXT);
651 int progBarXbase = barRegion.x + 300;
653 if (lenPTS == 0) return;
654 rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, DrawStyle::LIGHTTEXT);
655 rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
657 if (currentPTS > lenPTS) return;
659 // Draw yellow portion
660 int progressWidth = 302 * currentPTS / lenPTS;
661 rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
665 void VVideoMedia::removeBar()
667 if (!barShowing) return;
668 timers->cancelTimer(this, 2);
673 rectangle(barRegion, DrawStyle::TRANSPARENT);
674 BoxStack::getInstance()->update(this, &barRegion);
677 void VVideoMedia::doSummary()
679 vsummary = new VInfo();
680 vsummary->setTitleText(myMedia->getDisplayName());
681 vsummary->setBorderOn(1);
682 vsummary->setExitable();
683 const MediaURI *u=myMedia->getURI();
686 stlen+=strlen(myMedia->getFileName());
687 stlen+=strlen(tr("FileName"))+10;
689 stlen+=strlen(tr("Size"))+50;
690 stlen+=strlen(tr("Directory"))+500;
691 stlen+=strlen(tr("Time"))+50;
692 char *pinfo=player->getInfo();
693 stlen+=strlen(pinfo)+10;
694 char *buf=new char [stlen];
695 char *tsbuf=new char [stlen];
696 char *tsbuf2=new char [stlen];
697 char *tbuf=new char[Media::TIMEBUFLEN];
698 SNPRINTF(buf,stlen,"%s\n"
703 shortendedText(tr("FileName"),": ",myMedia->getFileName(),tsbuf,vsummary->getWidth()),
706 shortendedText(tr("Directory"),": ",lparent->getDirname(MEDIA_TYPE_VIDEO),tsbuf2,vsummary->getWidth()),
708 myMedia->getTimeString(tbuf),
713 Log::getInstance()->log("VVideoMedia",Log::DEBUG,"info %s",buf);
714 vsummary->setMainText(buf);
716 else vsummary->setMainText(tr("Info unavailable"));
718 if (Video::getInstance()->getFormat() == Video::PAL)
720 vsummary->setPosition(70, 100);
724 vsummary->setPosition(40, 70);
726 vsummary->setSize(580, 350);
730 BoxStack::getInstance()->update(this);
737 void VVideoMedia::removeSummary()
745 BoxStack::getInstance()->update(this);
750 hmsf VVideoMedia::ptsToHMS(ULLONG pts) {
751 ULLONG secs=pts/90000;
762 char * VVideoMedia::shortendedText(const char * title, const char * title2,const char * intext,char *buffer,UINT width) {
767 for (const char *p=title;*p!=0;p++) twidth+=(UINT)charWidth(*p);
768 for (const char *p=title2;*p!=0;p++) twidth+=(UINT)charWidth(*p);
769 const char *prfx="...";
770 UINT prfwidth=(UINT)(3*charWidth('.'));
771 const char *istart=intext+strlen(intext);
773 while (twidth+iwidth+prfwidth < width-2*paraMargin && istart> intext) {
775 iwidth+=(UINT)charWidth(*istart);
777 if (twidth+iwidth+prfwidth >= width-2*paraMargin && istart < intext+strlen(intext)) istart++;
778 if (istart == intext) prfx="";
779 sprintf(buffer,"%s%s%s%s",title,title2,prfx,intext);