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