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