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();
109 MessageQueue::getInstance()->addReceiver(this);
112 VVideoMedia::~VVideoMedia()
114 MessageQueue::getInstance()->removeReceiver(this);
115 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Entering destructor");
119 boxstack->remove(vas);
128 if (playing) stopPlay();
129 video->setDefaultAspect();
131 timers->cancelTimer(this, 1);
132 timers->cancelTimer(this, 2);
135 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "shutting down player");
138 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "deleted");
141 void VVideoMedia::go(bool resume)
145 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Starting stream: %s at frame: %lu", myMedia->getFileName(), startFrameNum);
150 const MediaURI *u=myMedia->getURI();
152 Log::getInstance()->log("VVideoMedia", Log::ERR, "stream: %s has no URI", myMedia->getFileName());
156 rt=MediaPlayer::getInstance()->openMedium(MEDIACHANNEL,u,&lengthBytes,area.w,area.h);
160 //TODO: figure out len in frames
161 int seq=player->playNew(MEDIACHANNEL,lengthBytes,0);
162 int ok=player->waitForSequence(2,seq);
171 stopPlay(); // clean up
173 Message* m = new Message();
174 m->message = Message::CLOSE_ME;
176 m->p_to = Message::BOXSTACK;
177 MessageQueue::getInstance()->postMessage(m);
179 VInfo* vi = new VInfo();
180 vi->setSize(400, 150);
182 if (video->getFormat() == Video::PAL)
183 vi->setPosition(170, 200);
185 vi->setPosition(160, 150);
188 vi->setTitleBarOn(0);
189 vi->setOneLiner(tr("Error playing media"));
193 m->message = Message::ADD_VIEW;
194 m->p_to = Message::BOXSTACK;
195 m->data = reinterpret_cast<void*>(vi);
196 MessageQueue::getInstance()->postMessage(m);
200 int VVideoMedia::handleCommand(int command)
208 return BoxStack::COMMAND_HANDLED;
216 return BoxStack::COMMAND_HANDLED;
222 if (playing) stopPlay();
224 return BoxStack::DELETE_ME;
230 return BoxStack::COMMAND_HANDLED;
232 case Input::SKIPFORWARD:
235 player->skipForward(60);
236 return BoxStack::COMMAND_HANDLED;
238 case Input::SKIPBACK:
241 player->skipBackward(60);
242 return BoxStack::COMMAND_HANDLED;
246 player->fastForward();
248 return BoxStack::COMMAND_HANDLED;
252 player->fastBackward();
254 return BoxStack::COMMAND_HANDLED;
258 if (vsummary) removeSummary();
260 return BoxStack::COMMAND_HANDLED;
265 return BoxStack::COMMAND_HANDLED;
270 player->skipBackward(10);
271 return BoxStack::COMMAND_HANDLED;
276 player->skipForward(10);
277 return BoxStack::COMMAND_HANDLED;
282 player->skipBackward(10);
283 return BoxStack::COMMAND_HANDLED;
288 player->skipForward(10);
289 return BoxStack::COMMAND_HANDLED;
295 return BoxStack::COMMAND_HANDLED;
303 return BoxStack::COMMAND_HANDLED;
306 if (barShowing) removeBar();
311 return BoxStack::COMMAND_HANDLED;
314 case Input::ZERO: player->jumpToPercent(0); doBar(0); return 2;
315 case Input::ONE: player->jumpToPercent(10); doBar(0); return 2;
316 case Input::TWO: player->jumpToPercent(20); doBar(0); return 2;
317 case Input::THREE: player->jumpToPercent(30); doBar(0); return 2;
318 case Input::FOUR: player->jumpToPercent(40); doBar(0); return 2;
319 case Input::FIVE: player->jumpToPercent(50); doBar(0); return 2;
320 case Input::SIX: player->jumpToPercent(60); doBar(0); return 2;
321 case Input::SEVEN: player->jumpToPercent(70); doBar(0); return 2;
322 case Input::EIGHT: player->jumpToPercent(80); doBar(0); return 2;
323 case Input::NINE: player->jumpToPercent(90); doBar(0); return 2;
328 return BoxStack::ABANDON_COMMAND;
331 void VVideoMedia::processMessage(Message* m)
333 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Message received");
335 if (m->message == Message::MOUSE_LBDOWN)
337 u4 x = m->parameter - getScreenX();
338 u4 y = m->tag - getScreenY();
342 Input::sendInputKey(Input::OK);
344 else if (barRegion.x<=x && barRegion.y<=y && (barRegion.x+barRegion.w)>=x && (barRegion.y+barRegion.h)>=y)
346 int progBarXbase = barRegion.x + 300;
347 if (x>=barRegion.x + progBarXbase + 24
348 && x<=barRegion.x + progBarXbase + 4 + 302
349 && y>=barRegion.y + 12 - 2
350 && y<=barRegion.y + 12 - 2+28)
352 int cx=x-(barRegion.x + progBarXbase + 4);
353 double percent=((double)cx)/302.*100.;
354 player->jumpToPercent(percent);
357 // int progressWidth = 302 * currentFrameNum / lengthFrames;
358 // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
363 Input::sendInputKey(Input::OK);
366 else if (m->message == Message::PLAYER_EVENT)
370 case PlayerMedia::CONNECTION_LOST: // connection lost detected
372 // I can't handle this, send it to control
373 Message* m2 = new Message();
374 m2->p_to = Message::CONTROL;
375 m2->message = Message::CONNECTION_LOST;
376 MessageQueue::getInstance()->postMessage(m2);
379 case PlayerMedia::STREAM_END:
381 Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
382 m2->p_to = Message::BOXSTACK;
383 m2->message = Message::CLOSE_ME;
384 MessageQueue::getInstance()->postMessage(m2);
387 case PlayerMedia::STATUS_CHANGE:
390 case PlayerMedia::ASPECT43:
394 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 43");
397 boxstack->update(this, &wssRegion);
401 case PlayerMedia::ASPECT169:
405 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 169");
408 boxstack->update(this, &wssRegion);
412 case (PLAYER_TIMER_BASE+1) :
417 case (PLAYER_TIMER_BASE+2) :
420 if (!barShowing) break;
422 BoxStack::getInstance()->update(this,&barRegion);
423 if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000);
424 else timers->setTimerD(this, 2, 1);
428 else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
430 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received change audio channel to %i", m->parameter);
431 player->setAudioChannel(m->parameter);
433 else if (m->message == Message::CHILD_CLOSE)
439 if (!barGenHold && !barScanHold && !barVasHold) removeBar();
444 void VVideoMedia::stopPlay()
446 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Pre stopPlay");
453 MediaPlayer::getInstance()->closeMediaChannel(MEDIACHANNEL);
455 Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Post stopPlay");
458 void VVideoMedia::toggleChopSides()
460 if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
462 if (videoMode == Video::NORMAL)
464 videoMode = Video::LETTERBOX;
465 video->setMode(Video::LETTERBOX);
469 videoMode = Video::NORMAL;
470 video->setMode(Video::NORMAL);
474 void VVideoMedia::doAudioSelector()
476 bool* availableMpegAudioChannels = player->getDemuxerMpegAudioChannels();
477 bool* availableAc3AudioChannels = 0;
478 int currentAudioChannel = player->getCurrentAudioChannel();
479 if (Audio::getInstance()->supportsAc3())
481 availableAc3AudioChannels = player->getDemuxerAc3AudioChannels();
486 ri.summary=new char[strlen(myMedia->getDisplayName())+1];
487 strcpy(ri.summary,myMedia->getDisplayName());
488 vas = new VAudioSelector(this, availableMpegAudioChannels, availableAc3AudioChannels, currentAudioChannel, NULL,NULL,0,0, &ri);
490 vas->setBackgroundColour(barBlue);
491 vas->setPosition(0, barRegion.y - 120);
500 boxstack->update(vas);
503 void VVideoMedia::doBar(int action)
505 Log::getInstance()->log("VVideoMedia",Log::DEBUG,"doBar %d",action);
508 rectangle(barRegion, barBlue);
510 /* Work out what to display - choices:
517 Specials, informed by parameter
529 w.setPosition(barRegion.x + 66, barRegion.y + 16);
535 if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD;
536 else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK;
537 else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2;
538 else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2;
542 playerState = player->getState();
543 if (playerState == PlayerMedia::S_PLAY) w.nextSymbol = WSymbol::PLAY;
544 else if (playerState == PlayerMedia::S_FF) w.nextSymbol = WSymbol::FFWD;
545 else if (playerState == PlayerMedia::S_BACK) w.nextSymbol = WSymbol::FBWD;
546 else if (playerState == PlayerMedia::S_SEEK) w.nextSymbol = WSymbol::RIGHTARROW;
547 else if (playerState == PlayerMedia::S_STOP) w.nextSymbol = WSymbol::PAUSE;
548 else w.nextSymbol = WSymbol::PAUSE;
553 if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK))
555 // draw blips to show how fast the scan is
556 u1 scanrate = 2;//player->getIScanRate();
560 SNPRINTF(text, 5, "%ux", scanrate);
561 drawText(text, barRegion.x + 102, barRegion.y + 12, DrawStyle::LIGHTTEXT);
566 boxstack->update(this, &barRegion);
568 timers->cancelTimer(this, 1);
571 if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK)) barScanHold = true;
572 else barScanHold = false;
574 if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4);
576 if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000);
577 else timers->setTimerD(this, 2, 1);
580 void VVideoMedia::timercall(int clientReference)
582 Message *m=new Message();
583 m->message=Message::PLAYER_EVENT;
586 m->parameter=PLAYER_TIMER_BASE+clientReference;
587 MessageQueue::getInstance()->postMessage(m);
590 void VVideoMedia::drawBarClocks()
594 u1 playerState = player->getState();
595 // sticky bar is set if we are in ffwd/fbwd mode
596 // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which
597 // will repaint all the bar (it will call this function again, but
598 // this section won't run because stickyBarF will then == false)
600 if ((playerState != PlayerMedia::S_FF) && (playerState != PlayerMedia::S_BACK))
608 Log* logger = Log::getInstance();
609 logger->log("VVideoMedia", Log::DEBUG, "Draw bar clocks");
612 // Blank the area first
613 rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
618 LOCALTIME_R(&t, &tms);
620 strftime(timeString, 19, "%H:%M", &tms);
621 drawText(timeString, barRegion.x + 624, barRegion.y + 12, DrawStyle::LIGHTTEXT);
623 u8 lenPTS=player->getLenPTS();
626 rectangle(clocksRegion, barBlue);
628 u8 currentPTS = player->getCurrentPTS();
630 hmsf currentFrameHMSF = ptsToHMS(currentPTS);
631 hmsf lengthHMSF = ptsToHMS(lenPTS);
634 if (currentPTS > lenPTS && lenPTS != 0)
636 strcpy(buffer, "-:--:-- / -:--:--");
640 SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds);
642 logger->log("VVideoMedia", Log::DEBUG, "cur %llu,len %llu, txt %s",currentPTS,lenPTS,buffer);
644 drawText(buffer, clocksRegion.x, clocksRegion.y, DrawStyle::LIGHTTEXT);
653 int progBarXbase = barRegion.x + 300;
655 if (lenPTS == 0) return;
656 rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, DrawStyle::LIGHTTEXT);
657 rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
659 if (currentPTS > lenPTS) return;
661 // Draw yellow portion
662 int progressWidth = 302 * currentPTS / lenPTS;
663 rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
667 void VVideoMedia::removeBar()
669 if (!barShowing) return;
670 timers->cancelTimer(this, 2);
675 rectangle(barRegion, DrawStyle::TRANSPARENT);
676 BoxStack::getInstance()->update(this, &barRegion);
679 void VVideoMedia::doSummary()
681 vsummary = new VInfo();
682 vsummary->setTitleText(myMedia->getDisplayName());
683 vsummary->setBorderOn(1);
684 vsummary->setExitable();
685 const MediaURI *u=myMedia->getURI();
688 stlen+=strlen(myMedia->getFileName());
689 stlen+=strlen(tr("FileName"))+10;
691 stlen+=strlen(tr("Size"))+50;
692 stlen+=strlen(tr("Directory"))+500;
693 stlen+=strlen(tr("Time"))+50;
694 char *pinfo=player->getInfo();
695 stlen+=strlen(pinfo)+10;
696 char *buf=new char [stlen];
697 char *tsbuf=new char [stlen];
698 char *tsbuf2=new char [stlen];
699 char *tbuf=new char[Media::TIMEBUFLEN];
700 SNPRINTF(buf,stlen,"%s\n"
705 shortendedText(tr("FileName"),": ",myMedia->getFileName(),tsbuf,vsummary->getWidth()),
708 shortendedText(tr("Directory"),": ",lparent->getDirname(MEDIA_TYPE_VIDEO),tsbuf2,vsummary->getWidth()),
710 myMedia->getTimeString(tbuf),
715 Log::getInstance()->log("VVideoMedia",Log::DEBUG,"info %s",buf);
716 vsummary->setMainText(buf);
718 else vsummary->setMainText(tr("Info unavailable"));
720 if (Video::getInstance()->getFormat() == Video::PAL)
722 vsummary->setPosition(70, 100);
726 vsummary->setPosition(40, 70);
728 vsummary->setSize(580, 350);
732 BoxStack::getInstance()->update(this);
739 void VVideoMedia::removeSummary()
747 BoxStack::getInstance()->update(this);
752 hmsf VVideoMedia::ptsToHMS(u8 pts) {
764 char * VVideoMedia::shortendedText(const char * title, const char * title2,const char * intext,char *buffer,u4 width) {
769 for (const char *p=title;*p!=0;p++) twidth+=(u4)charWidth(*p);
770 for (const char *p=title2;*p!=0;p++) twidth+=(u4)charWidth(*p);
771 const char *prfx="...";
772 u4 prfwidth=(u4)(3*charWidth('.'));
773 const char *istart=intext+strlen(intext);
775 while (twidth+iwidth+prfwidth < width-2*paraMargin && istart> intext) {
777 iwidth+=(u4)charWidth(*istart);
779 if (twidth+iwidth+prfwidth >= width-2*paraMargin && istart < intext+strlen(intext)) istart++;
780 if (istart == intext) prfx="";
781 sprintf(buffer,"%s%s%s%s",title,title2,prfx,intext);