]> git.vomp.tv Git - vompclient.git/blob - playermedia.cc
7cd314b504c44d74b7292446e8066b1c7af5107b
[vompclient.git] / playermedia.cc
1 /*
2     Copyright 2004-2006 Chris Tallon, Andreas Vogel
3
4     This file is part of VOMP.
5
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.
10
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.
15
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.
19 */
20
21 #include "playermedia.h"
22 //we most probably need a new demux...
23 #include "demuxermedia.h"
24 #include "timers.h"
25 #include "video.h"
26 #include "command.h"
27 #include "i18n.h"
28 #include "boxx.h"
29 #include "log.h"
30 #include "media.h"
31 #include "mediaplayer.h"
32
33 //how long to wait for new data (100ms units)
34 #define MAXTRY 300
35
36
37 PlayerMedia::PlayerMedia(Boxx *parent) : afeed(this), vfeed(this)
38 {
39         frontend=parent;
40   audio = Audio::getInstance();
41   video = Video::getInstance();
42   logger = Log::getInstance();
43   vdr = VDR::getInstance();
44   logger->log("PlayerMedia", Log::DEBUG, "ctorI");
45   running=true;
46         playerRunnig=false;
47   streampos = 0;
48         bytesWritten=0;
49   state = S_STOP;
50         requestState=S_STOP;
51         feederState=FEEDER_STOP;
52
53   threadBuffer = NULL;
54   
55         Video::getInstance()->turnVideoOn();
56         requestedSequence=0;
57         sequence=0;
58         demuxer=new DemuxerMedia();
59   logger->log("PlayerMedia", Log::DEBUG, "ctorII");
60         if (!demuxer->init(this, audio, video, NULL, 2097152, 524288, 0))
61   {
62     logger->log("PlayerMedia", Log::ERR, "Demuxer failed to init");
63     shutdown();
64     return ;
65   }
66   demuxer->setFrameNum(0);
67   logger->log("PlayerMedia", Log::DEBUG, " ctorIII");
68   afeed.init();
69   vfeed.init();
70         audio->reset();
71   video->reset();
72   video->stop();
73   video->blank();
74   logger->log("PlayerMedia", Log::DEBUG, "player created");
75   lengthFrames=0;
76   lengthBytes=0;
77   mediaChannel=0;
78   onStartup=0;
79   playingStarted=false;
80   appDestinationPTS=0;
81   canPosition=false;
82 }
83
84 PlayerMedia::~PlayerMedia()
85 {
86   if (threadBuffer) free(threadBuffer);
87         Timers::getInstance()->cancelTimer(this,1);
88         controlFeeder(FEEDER_STOP);
89         audio->reset();
90   video->reset();
91         audio->setStreamType(Audio::MPEG2_PES);
92         delete demuxer;
93         demuxer=NULL;
94   MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
95 }
96
97 void PlayerMedia::controlFeeder(int feederAction) {
98         logger->log("PlayerMedia",Log::DEBUG,"control feeder old=%d, new=%d",feederState,feederAction);
99         switch (feederAction) {
100                 case FEEDER_START:
101                 case FEEDER_UNPAUSE:
102                         if (feederState == FEEDER_STOP) {
103                                 afeed.start();
104         vfeed.start();
105       }
106                         afeed.enable();
107                         break;
108                 case FEEDER_STOP:
109                         if (feederState != FEEDER_STOP) {
110                                 afeed.stop();
111         vfeed.stop();
112       }
113                         break;
114                 case FEEDER_PAUSE:
115                         afeed.disable();
116                         break;
117         }
118         feederState=feederAction;
119 }
120
121
122 void PlayerMedia::run() {
123         if (playerRunnig) return;
124         playerRunnig=true;
125         threadStart();
126 }
127
128
129 void PlayerMedia::shutdown()
130 {
131         running=false;
132         if (playerRunnig) {
133                 threadSignalNoLock();
134                 //wait for the player thread to stop
135                 logger->log("PlayerMedia",Log::DEBUG,"shutdown - warting for player thread to stop");
136                 //at most wait 10s
137                 for (int loopcount=200;playerRunnig && loopcount > 0;loopcount--) {
138                         MILLISLEEP(50);
139                 }
140                 if (playerRunnig) {
141                   logger->log("PlayerMedia",Log::ERR,"shutdown - unable to stop player within 10s");
142                 }
143         }
144 }
145
146 bool PlayerMedia::isPlayerRunning() {
147         return playerRunnig;
148 }
149
150 int PlayerMedia::setRequestedState(UCHAR rstate) {
151         int rt=0;
152         threadLock();
153         requestState=rstate;
154         requestedSequence++;
155         rt=requestedSequence;
156         threadUnlock();
157         return rt;
158 }
159
160
161 UCHAR PlayerMedia::getState() {
162         UCHAR rt=0;
163         threadLock();
164         rt=state;
165         threadUnlock();
166         return rt;
167 }
168
169 void PlayerMedia::setState(UCHAR s) {
170         threadLock();
171         state=s;
172         threadUnlock();
173 }
174
175 //------------------- externally called functions --------------------------
176
177 //we leave all the open and close handling to the frontend
178 //so we only need the channel
179 int PlayerMedia::playNew(ULONG mChannel,ULLONG lBytes,ULLONG lFrames)
180 {
181         logger->log("PlayerMedia", Log::DEBUG, "play request for channel %lu", mChannel);
182   if (getState() != S_STOP) {
183     return -1;
184   }
185   MediaInfo mi;
186   int rt=MediaPlayer::getInstance()->getMediaInfo(mChannel,&mi);
187   canPosition=mi.canPosition;
188   if (rt == 0 && mi.canPosition) {
189     //try to get the len
190     UCHAR buf[STARTBUFLEN];
191     UCHAR *bpointer=buf;
192     ULONG rtlen=0;
193           logger->log("PlayerMedia", Log::DEBUG, "try to find last PTS with %lu bytes", STARTBUFLEN);
194     int rb=MediaPlayer::getInstance()->getMediaBlock(mChannel,lBytes-STARTBUFLEN,STARTBUFLEN,&rtlen,&bpointer);
195     if (rb == 0) {
196       demuxer->findLastPTS(bpointer,rtlen);
197     }
198     if (bpointer != buf) free(bpointer);
199   }
200   mediaChannel=mChannel;
201   lengthBytes=lBytes;
202   lengthFrames=lFrames;
203         rt=0;
204         threadLock();
205         logger->log("PlayerMedia", Log::DEBUG, "play request setState");
206         requestState=S_SEEK;
207         requestedSequence++;
208         rt=requestedSequence;
209   playingStarted=true;
210         threadUnlock();
211         threadSignalNoLock();
212         logger->log("PlayerMedia", Log::DEBUG, "play request rt=%d",rt);
213         return rt;
214 }
215
216 int PlayerMedia::stop()
217 {
218         int rt= setRequestedState(S_STOP);
219         threadSignalNoLock();
220         return rt;
221 }
222
223 int PlayerMedia::play(){
224   if (! playingStarted) return -1;
225   int rt=setRequestedState(S_PLAY);
226   threadSignalNoLock();
227   return rt;
228 }
229
230 int PlayerMedia::pause()
231 {
232         int rt= setRequestedState(S_PAUSE);
233         threadSignalNoLock();
234         return rt;
235 }
236 int PlayerMedia::unpause()
237 {
238         int rt= setRequestedState(S_PLAY);
239         threadSignalNoLock();
240         return rt;
241 }
242
243 int PlayerMedia::fastForward(){
244   if (! canPosition) return -1;
245         int rt=setRequestedState(S_FF);
246         threadSignalNoLock();
247         return rt;
248 }
249
250 int PlayerMedia::fastBackward(){
251   if (! canPosition) return -1;
252         int rt=setRequestedState(S_BACK);
253         threadSignalNoLock();
254         return rt;
255 }
256 int PlayerMedia::jumpToPercent(double percent){
257   if (! canPosition) return -1;
258         logger->log("PlayerMedia",Log::DEBUG,"jump to %i%% ",percent);
259         threadLock();
260         ULLONG npos=streampos;
261   //TODO: where to go - currently we simply take bytes...
262   npos=(ULLONG)(percent*lengthBytes/100);
263         if (npos > lengthBytes) npos=lengthBytes-1;
264         requestedStreampos=npos;
265         requestState=S_POSITION;
266         requestedSequence++;
267         threadUnlock();
268         //no need to wait here...
269         return 0;
270 }
271
272 int PlayerMedia::skipForward(int seconds) {
273   if (! canPosition) return -1;
274         logger->log("PlayerMedia",Log::DEBUG,"skip fw %ds",seconds);
275         threadLock();
276   ULLONG bps=getBytesPerSecond();
277   requestedStreampos=streampos+ (ULLONG)seconds*bps;
278         requestState=S_POSITION;
279         requestedSequence++;
280         threadUnlock();
281         logger->log("PlayerMedia",Log::DEBUG,"skip fw to %llu, %llu bps",requestedStreampos,bps);
282         return 0;
283 }
284 int PlayerMedia::skipBackward(int seconds) {
285   if (! canPosition) return -1;
286         logger->log("PlayerMedia",Log::DEBUG,"skip back %ds",seconds);
287         threadLock();
288   ULLONG bps=getBytesPerSecond();
289   if (streampos > (ULLONG)seconds*bps)
290           requestedStreampos=streampos- (ULLONG)seconds*bps;
291   else
292     requestedStreampos=0;
293         requestState=S_POSITION;
294         requestedSequence++;
295         threadUnlock();
296         logger->log("PlayerMedia",Log::DEBUG,"skip bw to %llu, %llu bps",requestedStreampos,bps);
297         return 0;
298 }
299
300
301
302 // ----------------------------------- Internal functions
303
304 void PlayerMedia::gotoSeek() {
305   controlFeeder(FEEDER_STOP);
306   video->stop();
307   video->reset();
308   audio->reset();
309   audio->setStreamType(Audio::MPEG2_PES);
310   demuxer->flush();
311   demuxer->seek();
312   controlFeeder(FEEDER_START);
313   audio->play();
314   video->sync();
315   audio->sync();
316   audio->systemMuteOff();
317   audio->doMuting();
318   video->play();
319   video->pause();
320   onStartup=true;  //search for Audio again
321                    //TODO: should be done only at startup
322 }
323
324
325
326 void PlayerMedia::sendFrontendMessage(ULONG para)
327 {
328   logger->log("PlayerMedia", Log::DEBUG, "sending frontend message %ld",para);
329   Message* m = new Message();
330         threadLock();
331   m->to = frontend;
332         threadUnlock();
333   m->from = this;
334   m->message = Message::PLAYER_EVENT;
335   m->parameter = para;
336         Command::getInstance()->postMessageFromOuterSpace(m);
337 }
338
339 //method called by the playing thread to handle 
340 //"commands" by the frontend
341 UCHAR PlayerMedia::checkState()
342 {
343         threadLock();
344         UCHAR rstate=requestState;
345         UCHAR cstate=state;
346         int rseq=requestedSequence;
347         int cseq=sequence;
348         threadUnlock();
349
350         if ( rseq > cseq) {
351                 logger->log("PlayerMedia", Log::DEBUG, "Switch state from %u to %u", cstate, rstate);
352     switch(rstate)
353     {
354       case S_PLAY: // to S_PLAY
355       {
356         if (cstate != S_SEEK && cstate != S_PAUSE) break;
357         appDestinationPTS=0;
358         if (cstate == S_PAUSE) {
359           audio->unPause();
360           video->unPause();
361           audio->unMute();
362           break;
363         }
364         controlFeeder(FEEDER_START);
365         video->reset();
366         video->play();
367         video->sync();
368         vfeed.release();
369         audio->unMute();
370         break;
371       }
372       case S_SEEK: //INITIAL_PLAY
373       {
374         //we are starting
375         gotoSeek();
376         break;
377       }
378       case S_PAUSE: // to S_PAUSE
379       {
380         if (cstate == S_PAUSE) {
381           rstate=S_PLAY;
382           video->unPause();
383           audio->unPause();
384           audio->unMute();
385           break;
386         }
387         if (cstate != S_PLAY) {
388           rstate=cstate;
389           break;
390         }
391         video->pause();
392         audio->pause();
393         break;
394       }
395       case S_FF: // to S_FF
396       {
397         gotoSeek();
398         rstate=S_SEEK;
399         break;
400       }
401       case S_BACK: // to S_BACK
402       {
403         gotoSeek();
404         rstate=S_SEEK;
405         break;
406       }
407       case S_STOP: // to S_STOP
408       {
409         if (cstate == S_STOP) {
410           break;
411         }
412         controlFeeder(FEEDER_STOP);
413         video->stop();
414         video->blank();
415         audio->stop();
416         audio->unPause();
417         audio->reset();
418         video->reset();
419         demuxer->reset();
420         demuxer->setFrameNum(0);//trigger framecounting
421         break;
422       }
423       case S_POSITION: // to S_JUMP
424       {
425         audio->mute();
426         audio->unPause();
427         video->unPause();
428         controlFeeder(FEEDER_STOP);
429         thisRead=0;
430         thisWrite=0;
431         if (threadBuffer) free(threadBuffer);
432         threadBuffer=NULL;
433         ULLONG bpts=0;
434         ULLONG curPts=getCurrentPTS();
435         if (curPts != 0) bpts=streampos/curPts;
436         if (bpts != 0) appDestinationPTS=requestedStreampos/bpts;
437         else appDestinationPTS=0;
438         streampos=requestedStreampos;
439         bytesWritten=streampos;
440         Log::getInstance()->log("PlayerMedia",Log::DEBUG,"position to %llu",streampos);
441         gotoSeek();
442         rstate=S_SEEK;
443         break;
444       }
445       case S_DONE:
446       Timers::getInstance()->setTimerD(this,1,15);
447       break;
448     }
449                 threadLock();
450                 state=rstate;
451                 sequence=rseq;
452                 threadUnlock();
453                 if (cstate != rstate && rstate != S_DONE ) {
454                         sendFrontendMessage(STATUS_CHANGE);
455                         //any change after done cancels the "done" timer
456                         Timers::getInstance()->cancelTimer(this,1);
457                 }
458                 logger->log("PlayerMedia", Log::DEBUG, "Switch state from %u to %u completed", cstate, rstate);
459                 //we return the newly set state
460                 return rstate;
461         }
462         //rstate could be different but no new request - so return cstate
463         return cstate;
464 }
465
466
467
468
469 // ----------------------------------- Feed thread
470
471 void PlayerMedia::waitTimed(int ms) {
472         threadLock();
473         struct timespec nt;
474         int sec=ms/1000;
475         int us=1000*(ms - 1000*sec);
476 #ifndef WIN32
477         struct timeval ct;
478         gettimeofday(&ct,NULL);
479         nt.tv_sec=ct.tv_sec+sec;
480         nt.tv_nsec=1000*us+1000*ct.tv_usec;
481 #else 
482     DWORD ct=timeGetTime();
483     nt.tv_sec=ct/1000+sec;
484     nt.tv_nsec=1000*us+1000*ct*1000;
485 #endif
486         threadWaitForSignalTimed(&nt);
487         threadUnlock();
488 }
489
490 void PlayerMedia::threadMethod()
491 {
492         logger->log("PlayerMedia", Log::DEBUG, "player thread started");
493         thisWrite=0;
494         thisRead=0;
495   int retrycount=0;
496   while(1)
497   {
498                 UCHAR cstate=checkState();
499     if (! running) {
500                         break;
501                 }
502                 if (cstate != S_PLAY && cstate != S_FF && cstate != S_BACK && cstate != S_SEEK) {
503                         waitTimed(500);
504                         continue;
505                 }
506     threadCheckExit();
507
508                 if (thisWrite == thisRead) {
509                         thisRead=0;
510       thisWrite=0;
511       threadBuffer=NULL;
512       ULONG rd=BUFLEN;
513       if (onStartup) rd=STARTBUFLEN;
514       int rt=MediaPlayer::getInstance()->getMediaBlock(mediaChannel,streampos,rd,&thisRead,&threadBuffer);
515       if ( thisRead == 0 && threadBuffer) {
516         free(threadBuffer);
517         threadBuffer=NULL;
518       }
519                         if ((!threadBuffer || thisRead == 0) && rt == 0) {
520         retrycount++;
521         if (retrycount > MAXTRY) rt=-1;
522         else {
523                                   logger->log("PlayerMedia", Log::DEBUG, "no data - retrying");
524           retrycount++;
525           continue;
526         }
527       }
528
529                         if (!threadBuffer || thisRead == 0 || rt != 0) {
530                                 //OK we count this as end of stream
531                                 logger->log("PlayerMedia", Log::DEBUG, "stream end");
532                                 setRequestedState(S_DONE);
533         MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
534                                 continue;
535                         }
536                         //logger->log("PlayerMedia", Log::DEBUG, "read %ld bytes at pos %ld",thisRead,streampos);
537                         streampos+=thisRead;
538       retrycount=0;
539                 }
540     
541     threadCheckExit();  
542     if (onStartup)
543     {
544       if (demuxer->getselAudioChannel() < Demuxer::PESTYPE_AUD0) {
545         int a_stream = demuxer->scan(threadBuffer, thisRead);
546         if (a_stream != 0){
547           demuxer->setAudioStream(a_stream);
548           logger->log("PlayerMedia", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
549         }
550         else {
551           logger->log("PlayerMedia", Log::DEBUG, "Startup Audio stream not choosen - leave to demux");
552         }
553       }
554       onStartup = false;
555       setRequestedState(S_PLAY); //OK - start playing even if we have no audio
556     }
557
558                 /*
559     MediaPacket p;
560                 memset(&p,sizeof(p),0);
561                 p.pos_buffer=0;
562                 p.length=thisRead;
563     MediaPacketList pl;
564                 pl.push_back(p);
565                 audio->PrepareMediaSample(pl,0);
566                 UINT bytesWritten=0;
567                 UINT rt=audio->DeliverMediaSample(threadBuffer,&bytesWritten);
568                 ULONG written=thisRead;
569                 if (rt == 0)
570                    written=bytesWritten;
571                 */
572     ULONG written= demuxer->put(threadBuffer + thisWrite, thisRead - thisWrite);
573     thisWrite+=written;
574                 bytesWritten+=written;
575                 if (thisWrite < thisRead) {
576                         if (written == 0) {
577         //logger->log("PlayerMedia", Log::DEBUG, "demuxer full, waiting ");
578         // demuxer is full and can't take anymore
579                                 waitTimed(200);
580                         }
581       }
582                 else {
583       //logger->log("PlayerMedia", Log::DEBUG, "block written %d", thisWrite);
584                         thisWrite=0;
585                         thisRead=0;
586       free(threadBuffer);
587       threadBuffer = NULL;
588                 }
589
590   }
591
592   logger->log("PlayerMedia", Log::DEBUG, "finished");
593   MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
594         playerRunnig=false;
595   setRequestedState(S_STOP);
596         return;
597 }
598
599 int PlayerMedia::waitForSequence(int timeout, int seq) {
600         time_t starttime=time(NULL)+timeout;
601         time_t curtime=0;
602   logger->log("PlayerMedia", Log::DEBUG, "waiting for sequence %d",seq);
603         while ((curtime=time(NULL)) < starttime) {
604                 int cseq=getSequence();
605                 if (cseq >= seq) return cseq;
606                 MILLISLEEP(10);
607         }
608         return -1;
609 }
610
611 int PlayerMedia::getSequence() {
612         int rt=0;
613         threadLock();
614         rt=sequence;
615         threadUnlock();
616         return rt;
617 }
618
619
620
621 void PlayerMedia::threadPostStopCleanup()
622 {
623   if (threadBuffer)
624   {
625     delete(threadBuffer);
626     threadBuffer = NULL;
627   }
628         playerRunnig=false;
629 }
630
631
632 void PlayerMedia::timercall(int ref) {
633         if (ref == 1) {
634                 logger->log("PlayerMedia", Log::DEBUG, "stream end - informing frontend");
635                 sendFrontendMessage(STREAM_END);
636         }
637 }
638
639
640 void PlayerMedia::call(void* caller)
641 {
642   if (caller == demuxer)
643   {
644     logger->log("PlayerMedia", Log::DEBUG, "Callback from demuxer");
645     //set position
646     if (demuxer->getHorizontalSize() > 0 && demuxer->getVerticalSize() > 0) {
647       int xpos=(video->getScreenWidth()-demuxer->getHorizontalSize())/2;
648       if (xpos <0 ) xpos=0;
649       int ypos=(video->getScreenHeight()-demuxer->getVerticalSize())/2;
650       if (ypos < 0) ypos=0;
651       logger->log("PlayerMedia", Log::DEBUG, "setting pos x=%d,y=%d",xpos,ypos);
652       video->setPosition(xpos,ypos);
653     }
654
655     if (video->getTVsize() == Video::ASPECT4X3)
656     {
657       logger->log("PlayerMedia", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
658       return;
659     }
660
661     int parx,pary;
662     int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
663     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
664     {
665       logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
666       video->setAspectRatio(Video::ASPECT4X3,parx,pary);
667
668       Message* m = new Message();
669       m->from = this;
670       m->to = frontend;
671       m->message = Message::PLAYER_EVENT;
672       m->parameter = PlayerMedia::ASPECT43;
673       Command::getInstance()->postMessageFromOuterSpace(m);
674     }
675     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
676     {
677       logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
678       video->setAspectRatio(Video::ASPECT16X9,parx,pary);
679
680       Message* m = new Message();
681       m->from = this;
682       m->to = frontend;
683       m->message = Message::PLAYER_EVENT;
684       m->parameter = PlayerMedia::ASPECT169;
685       Command::getInstance()->postMessageFromOuterSpace(m);
686     }
687     else
688     {
689       logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is something else... switch anayway");
690       video->setAspectRatio(dxCurrentAspect,parx,pary);
691     }
692
693   }
694   else
695   {
696     if (getState() == S_SEEK)
697     {
698       logger->log("PlayerMedia", Log::DEBUG, "feeder started writing");
699       setRequestedState(S_PLAY);
700     }
701
702     threadSignalNoLock();
703   }
704 }
705
706 void PlayerMedia::setAudioChannel(int newChannel)
707 {
708   demuxer->setAudioStream(newChannel);
709 }
710
711 bool* PlayerMedia::getDemuxerMpegAudioChannels()
712 {
713   return demuxer->getmpAudioChannels();
714 }
715
716 bool* PlayerMedia::getDemuxerAc3AudioChannels()
717 {
718   return demuxer->getac3AudioChannels();
719 }
720
721 int PlayerMedia::getCurrentAudioChannel()
722 {
723   return demuxer->getselAudioChannel();
724 }
725
726 ULLONG PlayerMedia::getLengthBytes() {
727   return lengthBytes;
728 }
729 ULLONG PlayerMedia::getLengthFrames() {
730   return lengthFrames;
731 }
732
733 //PTS is always relative to first
734 ULLONG PlayerMedia::getCurrentPTS()
735 {
736   if (appDestinationPTS != 0) {
737     ULLONG lenPts=demuxer->getLenPTS();
738     if (appDestinationPTS < lenPts) return appDestinationPTS;
739     return lenPts;
740   }
741   return demuxer->getCurrentPTS();
742 }
743 ULLONG PlayerMedia::getLenPTS()
744 {
745   return demuxer->getLenPTS();
746 }
747
748 ULLONG PlayerMedia::getBytesPerSecond() {
749   ULLONG currsec=getCurrentPTS()/90000;
750   if (currsec == 0) return 0;
751   return streampos/currsec;
752 }
753
754 char * PlayerMedia::getInfo() {
755   char * ibuf=new char[500];
756   int br=demuxer->getBitRate();
757   char brbuf[100];
758   if (br == 0x3ffff ) {
759     //VBR - use estimated
760     sprintf(brbuf,"VBR, cur %llu kBit/s",getBytesPerSecond()*8/1000);
761   }
762   else {
763     sprintf(brbuf,"CBR, %d kBit/s",br*4/10);
764   }
765   int parx,pary;
766   SNPRINTF(ibuf,500,"%s: %dx%d\n"
767                     "%s: %s\n"
768                     "%s: %s\n",
769                     tr("Format"),
770                     demuxer->getHorizontalSize(),
771                     demuxer->getVerticalSize(),
772                     tr("Aspect"),
773                     demuxer->getAspectRatio(&parx,&pary)==2?"4:3":"16:9",
774                     tr("BitRate"),brbuf
775                     );
776                     
777   return ibuf;
778 }
779
780
781
782   
783