2 Copyright 2004-2005 Chris Tallon, Andreas Vogel
\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
23 #include "vmediaview.h"
\r
25 #include "vpicturebanner.h"
\r
26 #include "vcolourtuner.h"
\r
27 #include "audioplayer.h"
\r
30 #include "wselectlist.h"
\r
32 #include "wsymbol.h"
\r
33 #include "boxstack.h"
\r
39 #include "message.h"
\r
40 #include "command.h"
\r
41 #include "mediaoptions.h"
\r
42 #include "mediaplayer.h"
\r
45 const int VMediaView::EVENT_SLIDESHOW=100;
\r
46 const int VMediaView::EVENT_DRAWINGDONE=101;
\r
47 const int VMediaView::EVENT_DRAWINGERROR=102;
\r
48 const int VMediaView::EVENT_DIRECTORYDONE=103;
\r
52 * the combined user interface for pictures and audio
\r
53 * has 2 surfaces to enable drawing in a separate thread
\r
54 * the info display can either show a picture info or and audio info
\r
55 * if the audio player comes on top it disables the picture info display
\r
56 * basically we have 3 modes:
\r
57 * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false)
\r
58 * if justPlaying=true the audioplayer is still running
\r
59 * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false
\r
60 * no AudioBanner visible
\r
61 * if justPlaying=true the audio player runs in bg
\r
62 * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true
\r
63 * the picture viewer is currently halted (should be able to continue if no AudioInfo)
\r
64 * no picture banner (we should have an audio banner to indicate the audio player on top!)
\r
65 * state transitions (via setPictureMode, setAudioMode)
\r
66 * - show Picture -> pictureEnabled=true,
\r
67 * only called from command handler if audioEnabled=false
\r
68 * call from mediaList only possible if audioEnabled=false
\r
69 * *** TODO: would be better to have separate function for external call/internal call
\r
70 * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO)
\r
71 * used for calls from medialist
\r
72 * internal calls do not set activate!
\r
73 * - YELLOW key - toggle
\r
74 * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE)
\r
75 * if audioActive==false -> audioActive=true (new mode AUDIO)
\r
76 * *** open handling if no audio file there (ignore key???) - currently empty player
\r
77 * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false
\r
78 * - BACK if mode PICTURE -> pictureEnabled=false;
\r
79 * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE)
\r
80 * info/banner handling:
\r
81 * - AudioInfo - alternativ to pictureInfo
\r
82 * off when disabling audio or after timer or with OK in AUDIO mode
\r
83 * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true
\r
84 * on on OK in AUDIO mode
\r
86 * off when disabling Picture or after timer, OK, green
\r
87 * on only on green when pictureEnabled==true
\r
89 * 2 modes: loading/normal
\r
91 * off when disabling picture, OK, after timer
\r
92 * on when enabling picture, with OK
\r
94 * off when disabling picture and when loading done
\r
95 * on on start loading
\r
96 * *** open: do not show when audio enabled and slide show running (currently slideshow is paused)
\r
98 * always shown when audioEnabled=true
\r
99 * on when enabling Audio
\r
100 * off when disabling audio
\r
102 * timers: 1 - slide show
\r
103 * 2 - pictureBanner
\r
104 * 3 - info showtime
\r
105 * 4 - audio short update
\r
108 #define DRAWING_THREAD
\r
110 DrawStyle VMediaView::pictureBack=DrawStyle(140,140,140);
\r
111 DrawStyle VMediaView::infoBack=DrawStyle(110,110,110);
\r
112 DrawStyle VMediaView::audioBannerBack=DrawStyle(110,110,110,20);
\r
115 //how long do we wait for a single picture chunk
\r
116 //the unit is 100ms (the timeout on the server side)
\r
117 #define MAXTRY 100
\r
118 class VPreader : public JpegReader {
\r
120 ImageReader *reader;
\r
121 VMediaView * parent;
\r
125 VPreader(VMediaView *p,ImageReader *r){
\r
131 virtual ULONG readChunk(ULONG offset,ULONG len,char ** buf) {
\r
132 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "read chunk o=%d,len=%d,buf=%p",
\r
136 for (int trycount=0;trycount < MAXTRY && ! dobreak && numrec == 0 && rt == 0;trycount++) {
\r
142 rt=reader->getImageChunk((ULLONG)offset,(UINT)len,&numrec,(UCHAR **)buf);
\r
145 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "got n=%d,buf=%p,rt=%d",
\r
149 virtual int initRead(const MediaURI *uri,ULLONG *sz,ULONG factor=100) {
\r
150 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s",uri->getDisplayName());
\r
151 Video* video = Video::getInstance();
\r
153 int rt=MediaPlayer::getInstance()->openMedium(1,uri,sz,video->getScreenWidth()*factor/100, video->getScreenHeight()*factor/100);
\r
156 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s returned %llu",uri->getDisplayName(),size);
\r
159 virtual ULONG getSize() {return size;}
\r
160 //seems to be thread safe (more or less...)
\r
167 * a separate thread for drawing pictures
\r
168 * will be started with the sfc and the draw control
\r
169 * will send a player event when done
\r
171 class DrawingThread : public Thread_TYPE {
\r
173 VMediaView *_parent;
\r
174 VPreader * _reader;
\r
175 WJpegComplex::JpegControl *_ctl;
\r
180 DrawingThread(VMediaView *parent) {
\r
185 _interrupted=false;
\r
187 virtual ~DrawingThread(){}
\r
189 return (threadIsActive()!=0);
\r
191 bool start(WJpegComplex::JpegControl *ctl,Surface *sfc,VPreader *reader,DrawStyle &c) {
\r
195 _interrupted=false;
\r
200 return (threadStart() == 1);
\r
203 Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop initiated");
\r
204 if (threadIsActive()) {
\r
206 if (_reader) _reader->setBreak();
\r
209 Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop done");
\r
214 virtual void threadMethod() {
\r
215 Log::getInstance()->log("DrawingThread",Log::DEBUG,"started");
\r
216 bool rt=WJpegComplex::drawJpeg(_ctl,_sfc,_reader,_colour);
\r
218 if (! _interrupted) {
\r
219 Message* m = new Message();
\r
220 //we misuse PLAYER_EVENT here
\r
221 m->message = Message::PLAYER_EVENT;
\r
224 m->parameter= rt?VMediaView::EVENT_DRAWINGDONE:VMediaView::EVENT_DRAWINGERROR;
\r
225 Command::getInstance()->postMessageFromOuterSpace(m);
\r
227 Log::getInstance()->log("DrawingThread",Log::DEBUG,"finishing interrupt=%d",(int)_interrupted);
\r
229 virtual void threadPostStopCleanup() {}
\r
234 VMediaView::VMediaView(VMediaList *p)
\r
236 Log::getInstance()->log("VMediaView::VMediaView", Log::DEBUG, "p=%p", this);
\r
237 audioEnabled=false;
\r
238 pictureEnabled=false;
\r
240 ireader=new ImageReader(1,MediaPlayer::getInstance());
\r
241 reader=new VPreader(this,ireader);
\r
242 //the surface handling
\r
243 Video* video = Video::getInstance();
\r
244 setSize(video->getScreenWidth(), video->getScreenHeight());
\r
248 //create the second surface
\r
253 //disable the surface
\r
256 //banners and infos
\r
257 pictureBanner=NULL;
\r
260 currentPicture=NULL;
\r
262 pictureLoading=false;
\r
265 showtime=INITIAL_SHOWTIME;
\r
266 rotate=WJpegComplex::ROT_0;
\r
268 options=MediaOptions::getInstance();
\r
269 VColourTuner::initFactors();
\r
270 int st=options->getIntOption("SlideShowInterval");
\r
271 if (st > 0) showtime=st;
\r
272 ctl.area.w=originalw;
\r
273 ctl.area.h=originalh;
\r
275 ctl.scaleafter=options->getIntOption("ScaleFactor");
\r
276 const char * mode=options->getStringOption("PictureMode");
\r
277 if (strcmp(mode,"clip") == 0) ctl.mode=WJpegComplex::CROP;
\r
278 else if (strcmp(mode,"letter") == 0) ctl.mode=WJpegComplex::LETTER;
\r
279 else if (strcmp(mode,"clipfactor") == 0) ctl.mode=WJpegComplex::CROPPERCENT;
\r
280 ctl.scaleAmount=options->getIntOption("PictureSize");
\r
281 if (ctl.scaleAmount < 10) ctl.scaleAmount=10;
\r
282 if (ctl.scaleAmount > 200) ctl.scaleAmount=200;
\r
285 //current control is the one used for DISPLAY (not the one for drawing - this is the other one)
\r
286 //this is closely coupled to the surface - i.e. ctl is for sfc1, ctl2 for sfc2
\r
287 currentControl=&ctl;
\r
288 bannerEnabled=true;
\r
289 pictureShowing=false;
\r
293 barBlue.set(0, 0, 150, 150);
\r
298 retriggerAudioInfo=false;
\r
300 drawingThread=new DrawingThread(this);
\r
303 VMediaView::~VMediaView()
\r
305 Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "p=%p,secondSfc=%s", this,(secondSurface()?"true":"false"));
\r
306 destroyPictureBanner();
\r
307 if (currentPicture) delete currentPicture;
\r
308 Timers::getInstance()->cancelTimer(this,1);
\r
309 Timers::getInstance()->cancelTimer(this,2);
\r
310 Timers::getInstance()->cancelTimer(this,3);
\r
311 Timers::getInstance()->cancelTimer(this,4);
\r
312 Timers::getInstance()->cancelTimer(this,5);
\r
314 destroyAudioBanner();
\r
315 if (getPlayer(false)) {
\r
316 AudioPlayer::getInstance(NULL,false)->shutdown();
\r
318 if (currentAudio) delete currentAudio;
\r
319 drawingThread->stop();
\r
320 ireader->shutdown();
\r
323 if (secondSurface()) {
\r
331 MediaPlayer::getInstance()->closeMediaChannel(1);
\r
332 MediaPlayer::getInstance()->closeMediaChannel(2);
\r
333 Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "done p=%p", this);
\r
336 void VMediaView::setPictureMode(bool act) {
\r
337 Log::getInstance()->log("VMediaView", Log::DEBUG, "set pictureMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);
\r
339 if ( ! pictureEnabled && ! audioEnabled) {
\r
342 showPictureBanner();
\r
343 parent->updateAll();
\r
345 if (! pictureEnabled) {
\r
346 //we newly enable the picture - so clear the screen
\r
348 BoxStack::getInstance()->update(this);
\r
349 if (slideshow) startSlideshow();
\r
354 if ( pictureEnabled) {
\r
355 destroyPictureBanner();
\r
356 stopSlideshow(false);
\r
357 #ifdef DRAWING_THREAD
\r
358 drawingThread->stop();
\r
360 if (! audioEnabled) {
\r
365 parent->updateAll();
\r
368 pictureShowing=false;
\r
370 pictureEnabled=act;
\r
373 //we can disable audio without automatically hiding us
\r
374 //this will become strange - we are visible but do not handle
\r
375 //keys - so if you call setAudioMode(false,false) be sure
\r
376 //to call setAudioMode(false,true) afterwards
\r
377 //it is only here to give the list a chance to start a new play
\r
378 //when we call directoryDone
\r
379 void VMediaView::setAudioMode(bool act,bool doHiding) {
\r
380 Log::getInstance()->log("VMediaView", Log::DEBUG, "setAudioMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);
\r
382 if (! audioEnabled) {
\r
383 if (! pictureEnabled) {
\r
386 draw(); //empty screen if no picture
\r
387 parent->updateAll();
\r
389 destroyPictureBanner();
\r
398 if ( audioEnabled) {
\r
399 audioEnabled=false;
\r
401 destroyAudioBanner();
\r
403 if (! pictureEnabled && doHiding) {
\r
407 parent->updateAll();
\r
410 //picture now on top
\r
411 if (havePictureBanner && ! pictureBanner) showPictureBanner();
\r
418 void VMediaView::draw()
\r
420 Log::getInstance()->log("VMediaView::draw", Log::DEBUG, "pictureError=%s,p=%p", pictureError,this);
\r
422 if (pictureShowing ) fillColour(pictureBack);
\r
423 else fillColour(DrawStyle::BLACK);
\r
424 if (pictureError) {
\r
425 drawText(pictureError,100,area.h/2,DrawStyle::LIGHTTEXT);
\r
431 int VMediaView::handleCommand(int command)
\r
433 Log::getInstance()->log("VMediaView::handleCommand", Log::DEBUG, "cmd=%d,p=%p", command,this);
\r
434 if ( !audioEnabled && ! pictureEnabled ) {
\r
435 //only handle YELLOW
\r
436 if (command == Remote::YELLOW) {
\r
437 setAudioMode(true);
\r
442 if ( ! audioEnabled) {
\r
443 //------------------------- command in mode PICTURE (i.e. picture is on top) ----------------
\r
448 case Remote::DF_UP:
\r
450 case Remote::SKIPBACK:
\r
451 rotate=WJpegComplex::ROT_0;
\r
452 showPicture(VMediaList::MV_PREV,slideshow,true);
\r
455 case Remote::FORWARD:
\r
456 if (showtime > 1) showtime--;
\r
457 updatePictureBanner(true);
\r
459 case Remote::DF_DOWN:
\r
461 case Remote::SKIPFORWARD:
\r
462 rotate=WJpegComplex::ROT_0;
\r
463 showPicture(VMediaList::MV_NEXT,slideshow,true);
\r
466 case Remote::REVERSE:
\r
467 if (showtime < 50 ) showtime++;
\r
468 updatePictureBanner(true);
\r
472 if (pictureBanner) {
\r
473 destroyPictureBanner();
\r
475 havePictureBanner=false;
\r
478 havePictureBanner=true;
\r
479 showPictureBanner(pictureLoading);
\r
487 rotate=WJpegComplex::ROT_0;
\r
488 showPicture(VMediaList::MV_NEXT,slideshow,true);
\r
492 case Remote::PAUSE:
\r
494 stopSlideshow(true);
\r
495 updatePictureBanner();
\r
499 rotate=WJpegComplex::ROT_0;
\r
500 showPicture(VMediaList::MV_NEXT,slideshow,true);
\r
505 stopSlideshow(true);
\r
506 showtime=INITIAL_SHOWTIME;
\r
507 updatePictureBanner();
\r
512 case WJpegComplex::ROT_0:
\r
513 rotate=WJpegComplex::ROT_90;
\r
515 case WJpegComplex::ROT_90:
\r
516 rotate=WJpegComplex::ROT_180;
\r
518 case WJpegComplex::ROT_180:
\r
519 rotate=WJpegComplex::ROT_270;
\r
521 case WJpegComplex::ROT_270:
\r
522 rotate=WJpegComplex::ROT_0;
\r
525 showPicture(VMediaList::MV_NONE,slideshow,true);
\r
528 case Remote::GREEN:
\r
529 if (info) destroyInfo();
\r
530 else showPictureInfo();
\r
534 switch (cropmode) {
\r
535 case WJpegComplex::CROP:
\r
536 cropmode=WJpegComplex::LETTER;
\r
538 case WJpegComplex::LETTER:
\r
539 cropmode=WJpegComplex::CROPPERCENT;
\r
542 cropmode=WJpegComplex::CROP;
\r
545 showPicture(VMediaList::MV_NONE,slideshow,true);
\r
548 case Remote::MENU: {
\r
549 stopSlideshow(true);
\r
550 destroyPictureBanner();
\r
552 VColourTuner *ct=new VColourTuner();
\r
553 BoxStack::getInstance()->add(ct);
\r
555 BoxStack::getInstance()->update(ct);
\r
561 setPictureMode(false);
\r
565 case Remote::YELLOW:
\r
567 setAudioMode(true);
\r
575 bool updateInfo=false;
\r
576 //------------------------- command in mode AUDIO (i.e. audio is on top) ----------------
\r
579 case Remote::YELLOW:
\r
580 setAudioMode(false);
\r
583 case Remote::DF_UP:
\r
585 play(playall,false,VMediaList::MV_PREV);
\r
588 case Remote::FORWARD:
\r
589 if (! audioError) getPlayer()->fastForward();
\r
593 case Remote::DF_DOWN:
\r
595 play(playall,false,VMediaList::MV_NEXT);
\r
598 case Remote::SKIPFORWARD:
\r
599 if (! audioError) getPlayer()->skipForward(10);
\r
602 case Remote::SKIPBACK:
\r
603 if (! audioError) getPlayer()->skipBackward(10);
\r
606 case Remote::REVERSE:
\r
610 if (! audioError) getPlayer()->jumpToPercent(0);
\r
614 if (! audioError) getPlayer()->jumpToPercent(10);
\r
618 if (! audioError) getPlayer()->jumpToPercent(20);
\r
621 case Remote::THREE:
\r
622 if (! audioError) getPlayer()->jumpToPercent(30);
\r
626 if (! audioError) getPlayer()->jumpToPercent(40);
\r
630 if (! audioError) getPlayer()->jumpToPercent(50);
\r
634 if (! audioError) getPlayer()->jumpToPercent(60);
\r
637 case Remote::SEVEN:
\r
638 if (! audioError) getPlayer()->jumpToPercent(70);
\r
641 case Remote::EIGHT:
\r
642 if (! audioError) getPlayer()->jumpToPercent(80);
\r
646 if (! audioError) getPlayer()->jumpToPercent(90);
\r
650 case Remote::GREEN:
\r
654 retriggerAudioInfo=false;
\r
657 retriggerAudioInfo=true;
\r
660 if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
\r
661 if (playall) play(playall,false,VMediaList::MV_NEXT);
\r
668 if (! audioError) getPlayer()->unpause();
\r
670 if (getPlayer()->getState() != AudioPlayer::S_ERROR) ;
\r
671 else if (playall) play(playall,false,VMediaList::MV_NEXT);
\r
675 case Remote::PAUSE:
\r
676 if (! audioError) getPlayer()->pause();
\r
681 getPlayer()->stop();
\r
688 getPlayer()->stop();
\r
690 retriggerAudioInfo=false;
\r
692 setAudioMode(false);
\r
693 if (! pictureShowing) setPictureMode(false); //could have been delayed
\r
698 if (audioEnabled && updateInfo) updateAudioInfo();
\r
703 void VMediaView::processMessage(Message* m)
\r
705 Log::getInstance()->log("VMediaView::processMessage", Log::DEBUG, "cmd=%lu,p=%lu", m->message,m->parameter);
\r
706 if (m->message == Message::MOUSE_MOVE)
\r
710 else if (m->message == Message::MOUSE_LBDOWN)
\r
713 //check if press is outside this view! then simulate cancel
\r
714 int x=(m->parameter>>16)-getScreenX();
\r
715 int y=(m->parameter&0xFFFF)-getScreenY();
\r
716 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
\r
718 BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press
\r
721 else if (m->message = Message::PLAYER_EVENT) {
\r
722 switch (m->parameter) {
\r
723 case EVENT_SLIDESHOW:
\r
724 if (! pictureEnabled) break; //old timer msg...
\r
725 //if (! audioEnabled) {
\r
728 rotate=WJpegComplex::ROT_0;
\r
729 showPicture(VMediaList::MV_NEXT,true,false);
\r
734 case EVENT_DRAWINGERROR:
\r
737 case EVENT_DRAWINGDONE:
\r
738 drawingDone(false);
\r
740 case EVENT_DIRECTORYDONE:
\r
741 //just disable audio without (poetntially) hiding us
\r
742 //this gives the list a chance to decide whether audio
\r
743 //should be on top afterwards without showing the list
\r
745 setAudioMode(false,false);
\r
746 parent->directoryDone();
\r
747 if (! pictureShowing) setPictureMode(false);
\r
748 if (! justPlaying) setAudioMode(false);
\r
750 case AudioPlayer::STREAM_ERR:
\r
751 case AudioPlayer::STREAM_END:
\r
752 if (playall) play(playall,false,VMediaList::MV_NEXT);
\r
754 setAudioMode(false);
\r
758 case AudioPlayer::SHORT_UPDATE:
\r
759 if (info && audioEnabled ) {
\r
761 BoxStack::getInstance()->update(info,&clocksRegion);
\r
762 BoxStack::getInstance()->update(info,&barRegion);
\r
763 Timers::getInstance()->setTimerD(this, 4, 1);
\r
766 case AudioPlayer::NEW_SONG:
\r
767 if (audioEnabled) updateAudioInfo();
\r
769 case AudioPlayer::CONNECTION_LOST:
\r
770 if (audioEnabled) destroyInfo();
\r
771 if (AudioPlayer *player=getPlayer(false)) {
\r
772 player->shutdown();
\r
775 Command::getInstance()->connectionLost();
\r
782 VMediaView * VMediaView::createViewer(VMediaList * mparent) {
\r
783 Log::getInstance()->log("VMediaView::createViewer", Log::DEBUG, "p=%p",
\r
785 VMediaView *vmn=new VMediaView(mparent);
\r
786 BoxStack::getInstance()->add(vmn);
\r
788 BoxStack::getInstance()->update(vmn);
\r
792 void VMediaView::startSlideshow() {
\r
794 if (! pictureLoading) Timers::getInstance()->setTimerD(this,1,showtime);
\r
797 void VMediaView::stopSlideshow(bool hard) {
\r
798 if (hard) slideshow=false;
\r
799 Timers::getInstance()->cancelTimer(this,1);
\r
803 void VMediaView::showPicture(ULONG move,bool bslideshow,bool activateBanner) {
\r
804 pictureShowing=true;
\r
805 setPictureMode(true);
\r
806 stopSlideshow(true);
\r
807 #ifdef DRAWING_THREAD
\r
808 drawingThread->stop();
\r
810 slideshow=bslideshow;
\r
811 Media *newPicture=parent->getMedia(MEDIA_TYPE_PICTURE,move);
\r
812 if ( ! newPicture) {
\r
813 pictureShowing=false;
\r
814 if (!audioEnabled) {
\r
815 //within the event handler first directoryDone is called
\r
816 //and afterwards everything is stopped if nothing new
\r
817 sendCommandMsg(EVENT_DIRECTORYDONE);
\r
823 pictureShowing=true;
\r
824 if (currentPicture) {
\r
825 delete currentPicture;
\r
826 currentPicture=NULL;
\r
828 currentPicture=newPicture;
\r
829 loadPicture(currentPicture,activateBanner);
\r
833 //the real picture drawing method
\r
834 //will start drawing on a new surface and will switch it wehn done
\r
836 int VMediaView::loadPicture(Media *md,bool activateBanner) {
\r
838 if (! md) return 1;
\r
839 if (! md->getURI()) {
\r
840 pictureError=tr("No media found");
\r
841 Log::getInstance()->log("VMediaView::load",Log::ERR,"no URI in media");
\r
844 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",
\r
845 md->getURI()->getName(),this);
\r
846 //do we have a pictureBanner?
\r
847 havePictureBanner=pictureBanner!=NULL || activateBanner;
\r
848 pictureLoading=true;
\r
849 #ifdef DRAWING_THREAD
\r
850 if (!audioEnabled && havePictureBanner ) showPictureBanner(true);
\r
851 drawingThread->stop();
\r
853 showPictureBanner(true);
\r
857 int rtok=reader->initRead(md->getURI(),&size,ctl.scaleAmount); //scaleAmount is the same for both...
\r
859 //now we can really draw
\r
860 //get the surface for drawing
\r
861 Surface * drawSurface=NULL;
\r
862 WJpegComplex::JpegControl *drawCtl=NULL;
\r
863 getDrawingParam(drawSurface,drawCtl);
\r
864 drawCtl->error[0]=0;
\r
865 drawCtl->rotation=rotate;
\r
866 drawCtl->mode=cropmode;
\r
867 drawCtl->compressedSize=size;
\r
868 #ifdef DRAWING_THREAD
\r
869 bool ok=drawingThread->start(drawCtl,drawSurface,reader,pictureBack);
\r
871 Log::getInstance()->log("VMediaView::load", Log::ERR, "unable to start drawing thread");
\r
872 pictureError=tr("JpegError");
\r
873 pictureLoading=false;
\r
874 destroyPictureBanner();
\r
879 //here we could hand this over to the drawing thread
\r
880 bool ok=WJpegComplex::drawJpeg(drawCtl,drawSurface,reader,pictureBack);
\r
885 pictureLoading=false;
\r
889 void VMediaView::drawingDone(bool hasError) {
\r
890 pictureLoading=false;
\r
891 destroyPictureBanner(); //disable loading indication (or real banner)
\r
894 Log::getInstance()->log("VMediaView::drawingDone", Log::DEBUG, "success: sfc now=%p",surface);
\r
896 //only show the pictureBanner if it was there before
\r
897 if (havePictureBanner && ! audioEnabled) showPictureBanner();
\r
898 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "success" );
\r
899 updatePictureInfo(); //will only do somethng if pictureEnabled
\r
903 pictureError=tr("JpegError");
\r
904 if (pictureEnabled) {
\r
909 MediaPlayer::getInstance()->closeMediaChannel(1);
\r
910 #ifndef DRAWING_THREAD
\r
911 if (audioEnabled) showAudioBanner();
\r
913 if (slideshow) startSlideshow();
\r
914 BoxStack::getInstance()->update(this);
\r
917 void VMediaView::showPictureBanner(bool loading) {
\r
918 //we are in the main thread - so we can (and must) safely hard destroy/create the banner
\r
919 Timers::getInstance()->cancelTimer(this,2);
\r
920 if (! currentPicture) {
\r
922 destroyPictureBanner(false);
\r
925 if (pictureBanner) destroyPictureBanner(false);
\r
926 if (! pictureEnabled || ! bannerEnabled) return;
\r
927 pictureBanner= new VPictureBanner(loading, slideshow);
\r
928 pictureBanner->fillColour(infoBack);
\r
930 int len=strlen(currentPicture->getFileName())+Media::TIMEBUFLEN+20;
\r
931 char *buf=new char[len];
\r
932 char tbuf[Media::TIMEBUFLEN];
\r
933 SNPRINTF(buf,len,"%c%02ds%c %s %s " ,
\r
937 currentPicture->getTimeString(tbuf),
\r
938 currentPicture->getFileName()
\r
940 pictureBanner->setText(buf);
\r
944 char *buf=new char[strlen(currentPicture->getDisplayName())+50];
\r
945 SNPRINTF(buf,50,"%s %s",tr("Loading"), currentPicture->getDisplayName());
\r
946 pictureBanner->setText(buf);
\r
949 pictureBanner->draw();
\r
950 if (! loading ) Timers::getInstance()->setTimerD(this,2,8);
\r
951 BoxStack::getInstance()->add(pictureBanner);
\r
952 BoxStack::getInstance()->update(pictureBanner);
\r
955 void VMediaView::destroyPictureBanner(bool fromTimer) {
\r
956 if (pictureBanner) {
\r
957 if (fromTimer) sendViewMsg(pictureBanner);
\r
958 else BoxStack::getInstance()->remove(pictureBanner);
\r
959 pictureBanner=NULL;
\r
960 if (! fromTimer) Timers::getInstance()->cancelTimer(this,2);
\r
963 void VMediaView::updatePictureBanner(bool loading) {
\r
964 if (pictureBanner ) {
\r
965 showPictureBanner(loading);
\r
968 void VMediaView::timercall(int clientref) {
\r
969 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "id=%d",clientref);
\r
974 sendCommandMsg(AudioPlayer::SHORT_UPDATE);
\r
979 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "infoEnd");
\r
981 if (audioEnabled) {
\r
982 //we only did show the audio error info if audio is enabled
\r
983 bool stillError=false;
\r
984 if (AudioPlayer * player=getPlayer(false)) {
\r
985 stillError=player->getState()==AudioPlayer::S_ERROR;
\r
988 sendCommandMsg(AudioPlayer::STREAM_END);
\r
994 if (! slideshow) return;
\r
995 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "slideshow");
\r
996 sendCommandMsg(EVENT_SLIDESHOW);
\r
999 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "pictureBannerEnd");
\r
1000 destroyPictureBanner(true);
\r
1006 #define INFOBUF 2000
\r
1007 void VMediaView::showPictureInfo(){
\r
1008 if (! pictureEnabled || audioEnabled) return;
\r
1009 if (info) destroyInfo();
\r
1010 if (! currentPicture) return;
\r
1013 info->setTitleText(currentPicture->getFileName());
\r
1014 info->setDropThrough();
\r
1015 info->setSize(500, 300);
\r
1016 info->createBuffer();
\r
1017 info->setBorderOn(1);
\r
1018 info->setTitleBarOn(1);
\r
1020 if (Video::getInstance()->getFormat() == Video::PAL)
\r
1021 info->setPosition(100, 180);
\r
1023 info->setPosition(100, 150);
\r
1024 char buf[INFOBUF];
\r
1025 char tbuf[Media::TIMEBUFLEN];
\r
1026 //modes should come from mediaoptions...
\r
1027 const char *mode=NULL;
\r
1028 switch (currentControl->mode) {
\r
1029 case WJpegComplex::CROPPERCENT:
\r
1030 mode="clipfactor";
\r
1032 case WJpegComplex::LETTER:
\r
1039 const char *dirname=parent->getDirname(MEDIA_TYPE_PICTURE);
\r
1041 const char *prfx="";
\r
1042 if (dirname) ldir=strlen(dirname);
\r
1044 dirname=dirname+ldir-35;
\r
1047 SNPRINTF(buf,INFOBUF,"%s= %s%s\n%s= %u x %u\n%s= %lu kBytes\n%s= %s\n%s= %u\n%s= %u%%\n%s= %s",
\r
1048 tr("Directory"), prfx,dirname,
\r
1049 tr("Format(px)"),currentControl->picw,currentControl->pich,
\r
1050 tr("Filesize"),currentControl->compressedSize/1000,
\r
1051 tr("Time"),currentPicture->getTimeString(tbuf),
\r
1052 tr("Rotation"),90*currentControl->finalRotation,
\r
1053 tr("Scale"),currentControl->scale,
\r
1054 tr("Picture Mode"),mode );
\r
1055 info->setMainText(buf);
\r
1057 BoxStack::getInstance()->add(info);
\r
1058 BoxStack::getInstance()->update(info);
\r
1059 Timers::getInstance()->setTimerD(this,3,8);
\r
1061 void VMediaView::updatePictureInfo(){
\r
1063 showPictureInfo();
\r
1066 void VMediaView::destroyInfo(bool fromTimer){
\r
1068 if (! fromTimer) {
\r
1069 Log::getInstance()->log("VMediaView",Log::DEBUG,"remove info %p",info);
\r
1070 BoxStack::getInstance()->remove(info);
\r
1073 Log::getInstance()->log("VMediaView",Log::DEBUG,"trigger remove info %p",info);
\r
1074 sendViewMsg(info);
\r
1078 if (! fromTimer) Timers::getInstance()->cancelTimer(this,3);
\r
1079 if (! fromTimer) Timers::getInstance()->cancelTimer(this,4);
\r
1082 void VMediaView::sendViewMsg(Boxx *v) {
\r
1083 Message* m = new Message();
\r
1084 m->message = Message::CLOSE_ME;
\r
1085 m->to = BoxStack::getInstance();
\r
1087 m->parameter=(ULONG)v;
\r
1088 Command::getInstance()->postMessageFromOuterSpace(m);
\r
1090 void VMediaView::sendCommandMsg(int command) {
\r
1091 Message* m = new Message();
\r
1092 //we misuse PLAYER_EVENT here
\r
1093 m->message = Message::PLAYER_EVENT;
\r
1096 m->parameter= command;
\r
1097 Command::getInstance()->postMessageFromOuterSpace(m);
\r
1100 void VMediaView::enableBanner(bool enable) {
\r
1101 bannerEnabled=false;
\r
1102 updatePictureBanner();
\r
1105 void VMediaView::getDrawingParam(Surface *&sfc,WJpegComplex::JpegControl *&c){
\r
1106 if (secondSurface()) {
\r
1107 //we currently display on sfc2
\r
1116 void VMediaView::switchSurface(){
\r
1117 if (secondSurface()) {
\r
1118 //now we switch to sfc1
\r
1120 currentControl=&ctl;
\r
1124 currentControl=&ctl2;
\r
1128 AudioPlayer * VMediaView::getPlayer(bool createIfNeeded)
\r
1130 AudioPlayer* rt=AudioPlayer::getInstance(this,false);
\r
1131 if (! createIfNeeded && rt == NULL) return NULL;
\r
1133 rt=AudioPlayer::getInstance(this);
\r
1139 bool VMediaView::isAudioPlaying() {
\r
1140 Log::getInstance()->log("VMediaView::isPlaying", Log::DEBUG, "rt=%s", justPlaying?"true":"false");
\r
1141 return justPlaying;
\r
1149 int VMediaView::play(bool all,bool activate,ULONG move,bool showInfo) {
\r
1151 if (getPlayer(false)) getPlayer(false)->stop();
\r
1152 justPlaying=false;
\r
1153 if (currentAudio) delete currentAudio;
\r
1154 currentAudio=NULL;
\r
1156 currentAudio=parent->getMedia(MEDIA_TYPE_AUDIO,move);
\r
1158 if ( ! currentAudio || ! currentAudio->getURI()) {
\r
1159 Log::getInstance()->log("VMediaView::load", Log::ERR, "no URI in media");
\r
1160 audioError=tr("no audio file");
\r
1161 if (audioEnabled) sendCommandMsg(EVENT_DIRECTORYDONE);
\r
1165 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",
\r
1166 currentAudio->getURI()->getName(),this);
\r
1167 int wseq=getPlayer()->play(currentAudio->getURI());
\r
1168 if (getPlayer()->waitForSequence(5,wseq)<0) {
\r
1169 audioError=tr("unable to open audio file");
\r
1176 Log::getInstance()->log("VMediaView", Log::DEBUG, "player started for %s",currentAudio->getURI()->getName());
\r
1178 if (activate && ! audioEnabled){
\r
1179 if (showInfo) retriggerAudioInfo=true;
\r
1180 setAudioMode(true);
\r
1183 //avoid duplicate creation of banner and info
\r
1184 showAudioBanner();
\r
1187 if (activate && (! currentPicture || ! pictureShowing)) draw();
\r
1188 BoxStack::getInstance()->update(this);
\r
1192 void VMediaView::showAudioInfo() {
\r
1193 if (! audioEnabled) return;
\r
1194 Timers::getInstance()->cancelTimer(this,4);
\r
1195 Timers::getInstance()->cancelTimer(this,3);
\r
1196 if (info) destroyInfo();
\r
1197 if (! retriggerAudioInfo) return;
\r
1199 bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR || audioError;
\r
1200 if (! playerError) Timers::getInstance()->setTimerD(this,3,AUDIOBANNER_TIME);
\r
1201 else Timers::getInstance()->setTimerD(this,3,AUDIOERROR_TIME);
\r
1202 if (! playerError) Timers::getInstance()->setTimerD(this,4, 1);
\r
1203 BoxStack::getInstance()->update(info);
\r
1206 void VMediaView::updateAudioInfo() {
\r
1212 void VMediaView::drawAudioInfo(){
\r
1213 Log::getInstance()->log("VMediaView",Log::DEBUG, "draw banner for %p",info);
\r
1214 const char *title=NULL;
\r
1215 char *playerTitle=NULL;
\r
1216 bool playerError=false;
\r
1220 const char *pl=tr("Playlist");
\r
1221 const char *first=NULL;
\r
1222 char *playerInfo=NULL;
\r
1224 if (getPlayer()->getState() == AudioPlayer::S_PLAY) audioError=NULL;
\r
1226 if (! currentAudio && ! audioError) audioError=tr("no audio file");
\r
1227 if (audioError ) {
\r
1228 title=tr("MediaError");
\r
1231 playerTitle=getPlayer()->getTitle();
\r
1232 if (playerTitle) title=playerTitle;
\r
1233 else title=currentAudio->getDisplayName();
\r
1234 num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentAudio->index);
\r
1235 playerError=getPlayer()->getState() == AudioPlayer::S_ERROR;
\r
1236 //1more line for long dirs
\r
1237 numlines=playall?5:4;
\r
1239 if (playerError) {
\r
1241 first=tr("Unable to play audio file");
\r
1242 len=strlen(first)+3;
\r
1244 else if (audioError) {
\r
1247 len=strlen(first)+3;
\r
1250 playerInfo=getPlayer()->getID3Info();drawText(tr("Loading"),5,3,DrawStyle::LIGHTTEXT);
\r
1251 len=strlen(currentAudio->getDisplayName())+strlen(pl)+30+strlen(parent->getDirname(MEDIA_TYPE_AUDIO))+Media::TIMEBUFLEN+110;
\r
1253 for (UINT i=0;i<strlen(playerInfo);i++)
\r
1254 if (playerInfo[i] == '\n') numlines++;
\r
1255 len+=strlen(playerInfo);
\r
1264 UINT height=numlines*30+60;
\r
1265 UINT vheight=Video::getInstance()->getScreenHeight();
\r
1266 UINT vwidth=Video::getInstance()->getScreenWidth();
\r
1267 if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN)
\r
1268 height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN;
\r
1269 info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height);
\r
1270 info->createBuffer();
\r
1271 if (Video::getInstance()->getFormat() == Video::PAL)
\r
1273 info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);
\r
1277 info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);
\r
1280 info->setTitleBarOn(0);
\r
1281 info->setDropThrough();
\r
1283 //set the regions for the closcks and bars on banner
\r
1284 barRegion.x = info->getWidth()-AUDIOBARLEN-20;
\r
1285 barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below?
\r
1286 barRegion.w = info->getWidth()-AUDIOBARLEN+10;
\r
1289 clocksRegion.x = 130;
\r
1290 clocksRegion.y = barRegion.y + 3;
\r
1291 clocksRegion.w = 190;
\r
1292 clocksRegion.h = getFontHeight();
\r
1293 Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info);
\r
1294 BoxStack::getInstance()->add(info);
\r
1295 char *buf=new char [len];
\r
1296 if (playerError || audioError) {
\r
1297 SNPRINTF(buf,len,"%s",first);
\r
1300 char tbuf[Media::TIMEBUFLEN];
\r
1302 SNPRINTF(buf,len,"%s\n"
\r
1308 tr("FileName"),currentAudio->getDisplayName(),
\r
1309 pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO),
\r
1310 tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),
\r
1311 tr("Time"),currentAudio->getTimeString(tbuf));
\r
1314 SNPRINTF(buf,len,"%s\n"
\r
1319 tr("FileName"),currentAudio->getDisplayName(),
\r
1320 tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),
\r
1321 tr("Time"),currentAudio->getTimeString(tbuf));
\r
1324 Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
\r
1325 //now the real drawing functions
\r
1328 info->rectangle(0, 0, info->getWidth(), 30, DrawStyle::TITLEBARBACKGROUND);
\r
1329 info->drawText(title, 5, 5, DrawStyle::LIGHTTEXT);
\r
1330 info->drawPara(buf,5,32,DrawStyle::LIGHTTEXT);
\r
1332 if (! audioError) {
\r
1334 info->TEMPADD(&w);
\r
1336 int ybottom=info->getHeight();
\r
1337 info->rectangle(0, ybottom - barRegion.h, info->getWidth(), barRegion.h, DrawStyle::TITLEBARBACKGROUND);
\r
1338 bool drawSymbol=true;
\r
1339 switch(getPlayer()->getState()) {
\r
1340 case AudioPlayer::S_PAUSE:
\r
1341 w.nextSymbol = WSymbol::PAUSE;
\r
1343 case AudioPlayer::S_PLAY:
\r
1344 w.nextSymbol = WSymbol::PLAY;
\r
1346 case AudioPlayer::S_DONE:
\r
1348 w.nextSymbol = WSymbol::PLAY;
\r
1352 case AudioPlayer::S_BACK:
\r
1353 w.nextSymbol = WSymbol::FBWD ;
\r
1355 case AudioPlayer::S_FF:
\r
1356 w.nextSymbol = WSymbol::FFWD ;
\r
1363 w.setPosition(x, ybottom-24);
\r
1368 info->rectangle(x, ybottom - 23, 18, 16, DrawStyle::LIGHTTEXT);
\r
1371 drawAudioClocks();
\r
1373 if (playerInfo) delete playerInfo;
\r
1374 if (playerTitle) delete playerTitle;
\r
1377 void VMediaView::drawAudioClocks() {
\r
1378 if (! info || ! audioEnabled) return;
\r
1379 Log::getInstance()->log("VMediaView::drawAudioClocks", Log::DEBUG, "");
\r
1380 //draw clocks and bar
\r
1381 info->rectangle(clocksRegion, DrawStyle::TITLEBARBACKGROUND);
\r
1383 time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
\r
1384 time_t lengthSec=(time_t)(getPlayer()->getSonglen());
\r
1387 /* gmtime_r(¤tSec,&cpos);
\r
1388 gmtime_r(&lengthSec,&slen);*/
\r
1389 cpos.tm_hour=currentSec/3600;
\r
1390 cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60;
\r
1391 cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60);
\r
1392 slen.tm_hour=lengthSec/3600;;
\r
1393 slen.tm_min=(lengthSec-slen.tm_hour*3600)/60;
\r
1394 slen.tm_sec=(lengthSec-slen.tm_hour*3600-slen.tm_min*60);
\r
1397 if (currentSec >= lengthSec)
\r
1399 SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000);
\r
1403 SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i %03dk", cpos.tm_hour, cpos.tm_min, cpos.tm_sec, slen.tm_hour, slen.tm_min, slen.tm_sec,
\r
1404 getPlayer()->getCurrentBitrate()/1000);
\r
1405 //Log::getInstance()->log("VMediaView", Log::DEBUG, buffer);
\r
1408 info->drawText(buffer, clocksRegion.x, clocksRegion.y, DrawStyle::LIGHTTEXT);
\r
1410 // Draw progress bar
\r
1411 int progBarXbase = 0;
\r
1414 info->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, DrawStyle::LIGHTTEXT);
\r
1415 info->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue);
\r
1417 if (currentSec > lengthSec) currentSec=lengthSec;
\r
1418 if (lengthSec == 0) return;
\r
1420 // Draw yellow portion
\r
1421 int progressWidth = (barlen+2) * currentSec / lengthSec;
\r
1422 info->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
\r
1426 void VMediaView::showAudioBanner() {
\r
1427 destroyAudioBanner();
\r
1428 if (! audioEnabled) return;
\r
1429 audioBanner=new VInfo();
\r
1430 Log::getInstance()->log("VMediaView",Log::DEBUG,"creating AudioBanner %p", audioBanner);
\r
1431 Video *v=Video::getInstance();
\r
1432 audioBanner->setSize(v->getScreenWidth()-100, 36);
\r
1433 audioBanner->createBuffer();
\r
1434 audioBanner->setPosition(50, v->getScreenHeight()-50);
\r
1435 audioBanner->fillColour(audioBannerBack);
\r
1436 audioBanner->setTitleBarOn(0);
\r
1437 audioBanner->setDropThrough();
\r
1438 if ( ! currentAudio || ! currentAudio->getDisplayName() || audioError) {
\r
1439 audioBanner->drawText(tr("AudioPlayer - not playing"),5,3,DrawStyle::LIGHTTEXT);
\r
1442 char * buf=new char[strlen(currentAudio->getDisplayName())+50];
\r
1443 SNPRINTF(buf,50,"%s %s",tr("AudioPlayer"),currentAudio->getDisplayName());
\r
1444 audioBanner->drawText(buf,5,3,DrawStyle::LIGHTTEXT);
\r
1447 BoxStack::getInstance()->add(audioBanner);
\r
1448 BoxStack::getInstance()->update(audioBanner);
\r
1451 void VMediaView::destroyAudioBanner() {
\r
1452 if (audioBanner) {
\r
1453 Log::getInstance()->log("VMediaView",Log::DEBUG,"deleting AudioBanner %p",audioBanner);
\r
1454 BoxStack::getInstance()->remove(audioBanner);
\r