Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

loopcontrol_threaded.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002 ** Copyright (C) 2000 Trolltech AS.  All rights reserved.
00003 **
00004 ** This file is part of Qtopia Environment.
00005 **
00006 ** This file may be distributed and/or modified under the terms of the
00007 ** GNU General Public License version 2 as published by the Free Software
00008 ** Foundation and appearing in the file LICENSE.GPL included in the
00009 ** packaging of this file.
00010 **
00011 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00012 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00013 **
00014 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00015 **
00016 ** Contact info@trolltech.com if any conditions of this licensing are
00017 ** not clear to you.
00018 **
00019 **********************************************************************/
00020 #define _REENTRANT
00021 
00022 #include "mediaplayerplugininterface.h"
00023 #include "loopcontrol.h"
00024 #include "audiodevice.h"
00025 #include "videowidget.h"
00026 #include "audiowidget.h"
00027 #include "mediaplayerstate.h"
00028 
00029 /* OPIE */
00030 #include <qpe/qpeapplication.h>
00031 #include <qpe/custom.h>
00032 
00033 #if !defined(QT_NO_COP)
00034 #include <qpe/qcopenvelope_qws.h>
00035 #endif
00036 
00037 #include <opie2/odebug.h>
00038 
00039 /* QT */
00040 #include <qimage.h>
00041 #include <qpainter.h>
00042 
00043 /* STD */
00044 #include <stdio.h>
00045 #include <stdlib.h>
00046 #include <string.h>
00047 #include <time.h>
00048 #include <unistd.h>
00049 #include <pthread.h>
00050 
00051 
00052 extern VideoWidget *videoUI; // now only needed to tell it to play a frame
00053 extern MediaPlayerState *mediaPlayerState;
00054 
00055 
00056 #define DecodeLoopDebug(x)  qDebug x
00057 //#define DecodeLoopDebug(x)
00058 
00059 
00060 static char     *audioBuffer = NULL;
00061 static AudioDevice  *audioDevice = NULL;
00062 static bool     disabledSuspendScreenSaver = FALSE;
00063 
00064 
00065 pthread_t video_tid;
00066 pthread_attr_t  video_attr;
00067 pthread_t audio_tid;
00068 pthread_attr_t  audio_attr;
00069 
00070 
00071 bool emitPlayFinished = FALSE;
00072 bool emitChangePos = FALSE;
00073 
00074 
00075 class Mutex {
00076 public:
00077     Mutex() {
00078   pthread_mutexattr_t attr;
00079   pthread_mutexattr_init( &attr );
00080   pthread_mutex_init( &mutex, &attr );
00081   pthread_mutexattr_destroy( &attr );
00082     }
00083 
00084     ~Mutex() {
00085   pthread_mutex_destroy( &mutex );
00086     }
00087 
00088     void lock() {
00089   pthread_mutex_lock( &mutex );
00090     }
00091 
00092     void unlock() {
00093   pthread_mutex_unlock( &mutex );
00094     }
00095 /*
00096     bool locked() {
00097   switch ( pthread_mutex_trylock( &mutex ) ) {
00098       case EBUSY:
00099     return TRUE;
00100       case 0:
00101     pthread_mutex_unlock( &mutex );
00102       default:
00103     return FALSE;
00104   }
00105     }
00106 */
00107 private:
00108     pthread_mutex_t mutex;
00109 };
00110 
00111 
00112 class currentFrameObj {
00113 public:
00114     currentFrameObj() : value( 0 ) { }
00115     void set( long f ) {
00116   mutex.lock();
00117   value = f;
00118   mediaPlayerState->curDecoder()->videoSetFrame( f, 0 );
00119   mutex.unlock();
00120     }
00121     long get() {
00122   return value;
00123     }
00124 private:
00125     long value;
00126     Mutex mutex;
00127 };
00128 
00129 
00130 Mutex *videoMutex;
00131 Mutex *audioMutex;
00132 Mutex *globalMutex;
00133 
00134 
00135 clock_t begin;
00136 
00137 
00138 LoopControl::LoopControl( QObject *parent, const char *name )
00139     : QObject( parent, name ) {
00140     isMuted = FALSE;
00141     connect( qApp, SIGNAL( volumeChanged(bool) ), this, SLOT( setMute(bool) ) );
00142     timerid = startTimer( 200 );
00143     videoMutex = new Mutex;
00144     audioMutex = new Mutex;
00145     globalMutex = new Mutex;
00146     //begin = clock();
00147 }
00148 
00149 
00150 LoopControl::~LoopControl() {
00151     stop();
00152     killTimer( timerid );
00153 }
00154 
00155 
00156 static bool sendingNewPos = FALSE;
00157 static long prev_frame = 0;
00158 static int currentSample = 0;
00159 
00160 
00161 void LoopControl::timerEvent( QTimerEvent* ) {
00162     // We need to emit playFinished from the main thread, not one of the
00163     // decoding threads else we'll have all kinds of yucky things happen (reentrance).
00164     // playFinished will eventually call stop() which stops these threads.
00165     if ( emitPlayFinished ) {
00166   emitPlayFinished = FALSE;
00167         mediaPlayerState->setPlaying( FALSE );
00168     }
00169 
00170     if ( emitChangePos ) {
00171 
00172   emitChangePos = FALSE;
00173 
00174   if ( hasVideoChannel && hasAudioChannel ) {
00175       sendingNewPos = TRUE;
00176       mediaPlayerState->setPosition( current_frame );
00177   } else if ( hasVideoChannel ) {
00178       sendingNewPos = TRUE;
00179       mediaPlayerState->setPosition( current_frame );
00180   } else if ( hasAudioChannel ) {
00181       sendingNewPos = TRUE;
00182       mediaPlayerState->setPosition( audioSampleCounter );
00183   }
00184 
00185     }
00186 }
00187 
00188 
00189 
00190 
00191 void LoopControl::setPosition( long pos ) {
00192     if ( sendingNewPos ) {
00193   sendingNewPos = FALSE;
00194   return;
00195     }
00196 
00197     if ( hasVideoChannel && hasAudioChannel ) {
00198   videoMutex->lock();
00199   audioMutex->lock();
00200   //odebug << "setting position" << oendl;
00201   playtime.restart();
00202   playtime = playtime.addMSecs( -pos * 1000 / framerate );
00203   //begin = clock() - (double)pos * CLOCKS_PER_SEC / framerate;
00204   current_frame = pos + 1;
00205   mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
00206   prev_frame = current_frame - 1;
00207   currentSample = (int)( current_frame * freq / framerate );
00208   mediaPlayerState->curDecoder()->audioSetSample( currentSample, stream );
00209   audioSampleCounter = currentSample - 1;
00210   audioMutex->unlock();
00211   videoMutex->unlock();
00212     } else if ( hasVideoChannel ) {
00213   videoMutex->lock();
00214   playtime.restart();
00215   playtime = playtime.addMSecs( -pos * 1000 / framerate );
00216   //begin = clock() - (double)pos * CLOCKS_PER_SEC / framerate;
00217   current_frame = pos + 1;
00218   mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
00219   videoMutex->unlock();
00220   prev_frame = current_frame - 1;
00221     } else if ( hasAudioChannel ) {
00222   audioMutex->lock();
00223   playtime.restart();
00224   playtime = playtime.addMSecs( -pos * 1000 / freq );
00225   //begin = clock() - (double)pos * CLOCKS_PER_SEC / freq;
00226   currentSample = pos + 1; // (int)( current_frame * freq / framerate );
00227   mediaPlayerState->curDecoder()->audioSetSample( currentSample, stream );
00228   audioSampleCounter = currentSample - 1;
00229   audioMutex->unlock();
00230     }
00231 }
00232 
00233 
00234 void *startVideoThread( void *ptr ) {
00235     LoopControl *mpegView = (LoopControl *)ptr;
00236     mpegView->startVideo();
00237     return 0;
00238 }
00239 
00240 void *startAudioThread( void *ptr ) {
00241     LoopControl *mpegView = (LoopControl *)ptr;
00242     mpegView->startAudio();
00243     return 0;
00244 }
00245 
00246 void LoopControl::startVideo() {
00247     moreVideo = TRUE;
00248 
00249     while ( moreVideo ) {
00250 
00251         if ( mediaPlayerState->curDecoder() && hasVideoChannel ) {
00252 
00253       if ( hasAudioChannel && !isMuted ) {
00254 
00255     bool done = FALSE;
00256 
00257     do {
00258 
00259 
00260 /*
00261         videoMutex->lock();
00262         current_frame = int( (double)playtime.elapsed() * (double)framerate / 1000.0 );
00263         //current_frame = ( clock() - begin ) * (double)framerate / CLOCKS_PER_SEC;
00264 
00265         // Sync to Audio
00266 //        current_frame = (long)((double)(audioSampleCounter - 1000) * framerate / (double)freq);
00267 
00268         long mSecsToNextFrame = 0;
00269 
00270         if ( current_frame == prev_frame ) {
00271       int nf = current_frame + 1;
00272       if ( nf > 0 && nf != total_video_frames )
00273           // mSecsToNextFrame = long(double(nf * CLOCKS_PER_SEC) / framerate) - ( clock() - begin );
00274           mSecsToNextFrame = long(double(nf * 1000) / framerate) - ( playtime.elapsed() );
00275         }
00276         videoMutex->unlock();
00277 
00278         if ( mSecsToNextFrame ) {
00279       usleep( mSecsToNextFrame ); // wait a bit
00280 
00281       videoMutex->lock();
00282       // This should now be the next frame
00283       current_frame = int( (double)playtime.elapsed() * (double)framerate / 1000.0 );
00284       //current_frame = ( clock() - begin ) * (double)framerate / CLOCKS_PER_SEC;
00285       videoMutex->unlock();
00286         }
00287 
00288         videoMutex->lock();
00289         done = current_frame >= prev_frame;
00290         videoMutex->unlock();
00291 */
00292         videoMutex->lock();
00293         current_frame = int( (double)playtime.elapsed() * (double)framerate / 1000.0 );
00294         done = current_frame >= prev_frame;
00295         videoMutex->unlock();
00296         if ( !done )
00297       usleep( 1000 ); // wait a bit
00298 
00299     } while ( !done );
00300 
00301 //    odebug << "elapsed: " << int( playtime.elapsed() ) << " " << current_frame << " (" << framerate << ")" << oendl;
00302 
00303       } else {
00304     videoMutex->lock();
00305     current_frame++;
00306     videoMutex->unlock();
00307       }
00308 
00309       videoMutex->lock();
00310       bool check = current_frame && current_frame > prev_frame;
00311       videoMutex->unlock();
00312 
00313       if ( check ) {
00314     videoMutex->lock();
00315     if ( current_frame > prev_frame + 1 ) {
00316        //       odebug << "skipped a frame" << oendl;
00317         mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
00318     }
00319     prev_frame = current_frame;
00320     if ( moreVideo = videoUI->playVideo() )
00321         emitChangePos = TRUE;
00322     videoMutex->unlock();
00323       }
00324 
00325   } else
00326       moreVideo = FALSE;
00327 
00328     }
00329 
00330     if ( !moreVideo && !moreAudio )
00331   emitPlayFinished = TRUE;
00332 
00333     pthread_exit(NULL);
00334 }
00335 
00336 void LoopControl::startAudio() {
00337     moreAudio = TRUE;
00338 
00339     while ( moreAudio ) {
00340 
00341   if ( !isMuted && mediaPlayerState->curDecoder() && hasAudioChannel ) {
00342 
00343       audioMutex->lock();
00344       currentSample = mediaPlayerState->curDecoder()->audioGetSample( stream );
00345 
00346       if ( currentSample == 0 )
00347     currentSample = audioSampleCounter + 1;
00348 
00349 //        if ( currentSample != audioSampleCounter + 1 )
00350 //      odebug << "out of sync with decoder " << currentSample << " " << audioSampleCounter << "" << oendl;
00351       audioMutex->unlock();
00352 
00353 /*
00354       int sampleWeShouldBeAt = int( playtime.elapsed() ) * freq / 1000;
00355 
00356       if ( sampleWeShouldBeAt - currentSample > 20000 ) {
00357     mediaPlayerState->curDecoder()->audioSetSample( sampleWeShouldBeAt, stream );
00358     currentSample = sampleWeShouldBeAt;
00359       }
00360 */
00361       long samplesRead = 0;
00362 
00363       const long samples = 1024;
00364 
00365       moreAudio = !mediaPlayerState->curDecoder()->audioReadSamples( (short*)audioBuffer, channels, samples, samplesRead, stream );
00366 
00367       audioMutex->lock();
00368       long sampleWeShouldBeAt = long( playtime.elapsed() ) * freq / 1000;
00369       //long sampleWeShouldBeAt = long( clock() - begin ) * (double) freq / CLOCKS_PER_SEC;
00370       long sampleWaitTime = currentSample - sampleWeShouldBeAt;
00371       audioMutex->unlock();
00372 
00373       if ( sampleWaitTime >= 0 && sampleWaitTime <= 2000 ) {
00374     //odebug << "sampleWaitTime: " << sampleWaitTime << "" << oendl;
00375     usleep( ( sampleWaitTime * 1000000 ) / ( freq ) );
00376       } else {
00377     audioMutex->lock();
00378     if ( sampleWaitTime <= -2000 ) {
00379        //        odebug << "need to catch up by: " << -sampleWaitTime << " (" << currentSample << "," << sampleWeShouldBeAt << ")" << oendl;
00380         mediaPlayerState->curDecoder()->audioSetSample( sampleWeShouldBeAt, stream );
00381         currentSample = sampleWeShouldBeAt;
00382     }
00383     audioMutex->unlock();
00384       }
00385 
00386       audioDevice->write( audioBuffer, samplesRead * 2 * channels );
00387 
00388       audioMutex->lock();
00389 //      audioSampleCounter += samplesRead;
00390       audioSampleCounter = currentSample + samplesRead - 1;
00391       audioMutex->unlock();
00392 
00393       if ( !hasVideoChannel )
00394     emitChangePos = TRUE;
00395 
00396       //odebug << "currentSample: " << currentSample << " audioSampleCounter: " << audioSampleCounter << " total_audio_samples: " << total_audio_samples << "" << oendl;
00397 //      odebug << "current: " << currentSample << " counter: " << audioSampleCounter << " total: " << (int)total_audio_samples << "" << oendl;
00398       moreAudio = audioSampleCounter <= total_audio_samples;
00399 
00400   } else {
00401 
00402       if ( mediaPlayerState->curDecoder() && hasAudioChannel )
00403           usleep( 100000 ); // Check every 1/10 sec to see if mute is off
00404       else
00405     moreAudio = FALSE;
00406 
00407   }
00408     }
00409 
00410     //    odebug << "End of file" << oendl;
00411 
00412     if ( !moreVideo && !moreAudio )
00413   emitPlayFinished = TRUE;
00414 
00415     pthread_exit(NULL);
00416 }
00417 
00418 void LoopControl::killTimers() {
00419     if ( hasVideoChannel ) {
00420   if ( pthread_self() != video_tid ) {
00421       if ( pthread_cancel(video_tid) == 0 ) {
00422     void *thread_result = 0;
00423     if ( pthread_join(video_tid,&thread_result) != 0 )
00424        //        odebug << "thread join error 1" << oendl;
00425     pthread_attr_destroy(&video_attr);
00426       }
00427   }
00428     }
00429     if ( hasAudioChannel ) {
00430   if ( pthread_self() != audio_tid ) {
00431       if ( pthread_cancel(audio_tid) == 0 ) {
00432     void *thread_result = 0;
00433     if ( pthread_join(audio_tid,&thread_result) != 0 )
00434        //        odebug << "thread join error 2" << oendl;
00435     pthread_attr_destroy(&audio_attr);
00436       }
00437   }
00438     }
00439 }
00440 
00441 void LoopControl::startTimers() {
00442     moreVideo = FALSE;
00443     moreAudio = FALSE;
00444 
00445     if ( hasVideoChannel ) {
00446   moreVideo = TRUE;
00447   pthread_attr_init(&video_attr);
00448   pthread_create(&video_tid, &video_attr, (void * (*)(void *))startVideoThread, this);
00449     }
00450 
00451     if ( hasAudioChannel ) {
00452   moreAudio = TRUE;
00453   pthread_attr_init(&audio_attr);
00454 #ifdef USE_REALTIME_AUDIO_THREAD
00455   pthread_attr_setschedpolicy(&audio_attr,SCHED_RR); // Real-time round robin
00456   //odebug << "min: " << sched_get_priority_min( SCHED_RR ) << ", max: " << sched_get_priority_max( SCHED_RR ) << "" << oendl;
00457   sched_param params;
00458   params.sched_priority = 50;
00459   pthread_attr_setschedparam(&audio_attr,&params);
00460 #endif
00461   pthread_create(&audio_tid, &audio_attr, (void * (*)(void *))startAudioThread, this);
00462     }
00463 }
00464 
00465 
00466 
00467 
00468 void LoopControl::setPaused( bool pause ) {
00469     static int whenPaused = 0;
00470 
00471     if ( !mediaPlayerState->curDecoder() || !mediaPlayerState->curDecoder()->isOpen() )
00472   return;
00473 
00474     if ( pause ) {
00475   // Remember where we are
00476   whenPaused = playtime.elapsed();
00477   killTimers();
00478     } else {
00479   // Just like we never stopped
00480   playtime.restart();
00481   playtime = playtime.addMSecs( -whenPaused );
00482   whenPaused = 0;
00483   startTimers();
00484     }
00485 }
00486 
00487 
00488 void LoopControl::stop( bool willPlayAgainShortly ) {
00489 
00490 #if defined(Q_WS_QWS) && !defined(QT_NO_COP)
00491     if ( !willPlayAgainShortly && disabledSuspendScreenSaver ) {
00492   disabledSuspendScreenSaver = FALSE;
00493   // Re-enable the suspend mode
00494   QCopEnvelope("QPE/System", "setScreenSaverMode(int)" ) << QPEApplication::Enable;
00495     }
00496 #endif
00497 
00498     if ( mediaPlayerState->curDecoder() && mediaPlayerState->curDecoder()->isOpen() ) {
00499 
00500   killTimers();
00501 
00502   mediaPlayerState->curDecoder()->close();
00503 
00504   if ( audioDevice ) {
00505       delete audioDevice;
00506       delete audioBuffer;
00507       audioDevice = 0;
00508       audioBuffer = 0;
00509   }
00510 
00511     }
00512 }
00513 
00514 
00515 bool LoopControl::init( const QString& filename ) {
00516     stop();
00517     fileName = filename;
00518     stream = 0; // only play stream 0 for now
00519     current_frame = total_video_frames = total_audio_samples = 0;
00520 
00521     //    odebug << "Using the " << mediaPlayerState->curDecoder()->pluginName() << " decoder" << oendl;
00522 
00523     // ### Hack to use libmpeg3plugin to get the number of audio samples if we are using the libmad plugin
00524     if ( mediaPlayerState->curDecoder()->pluginName() == QString("LibMadPlugin") ) {
00525   if ( mediaPlayerState->libMpeg3Decoder() && mediaPlayerState->libMpeg3Decoder()->open( filename ) ) {
00526       total_audio_samples = mediaPlayerState->libMpeg3Decoder()->audioSamples( 0 );
00527       mediaPlayerState->libMpeg3Decoder()->close();
00528   }
00529     }
00530 
00531     if ( !mediaPlayerState->curDecoder()|| !mediaPlayerState->curDecoder()->open( filename ) )
00532   return FALSE;
00533 
00534     hasAudioChannel = mediaPlayerState->curDecoder()->audioStreams() > 0;
00535     hasVideoChannel = mediaPlayerState->curDecoder()->videoStreams() > 0;
00536 
00537     if ( hasAudioChannel ) {
00538   int astream = 0;
00539 
00540   channels = mediaPlayerState->curDecoder()->audioChannels( astream );
00541   DecodeLoopDebug(( "channels = %d\n", channels ));
00542 
00543   if ( !total_audio_samples )
00544       total_audio_samples = mediaPlayerState->curDecoder()->audioSamples( astream );
00545 
00546   mediaPlayerState->setLength( total_audio_samples );
00547 
00548   freq = mediaPlayerState->curDecoder()->audioFrequency( astream );
00549   DecodeLoopDebug(( "frequency = %d\n", freq ));
00550 
00551   audioSampleCounter = 0;
00552 
00553   static const int bytes_per_sample = 2; //16 bit
00554 
00555   audioDevice = new AudioDevice( freq, channels, bytes_per_sample );
00556   audioBuffer = new char[ audioDevice->bufferSize() ];
00557   channels = audioDevice->channels();
00558 
00559   //### must check which frequency is actually used.
00560   static const int size = 1;
00561   short int buf[size];
00562   long samplesRead = 0;
00563   mediaPlayerState->curDecoder()->audioReadSamples( buf, channels, size, samplesRead, stream );
00564     }
00565 
00566     if ( hasVideoChannel ) {
00567   total_video_frames = mediaPlayerState->curDecoder()->videoFrames( stream );
00568 
00569   mediaPlayerState->setLength( total_video_frames );
00570 
00571   framerate = mediaPlayerState->curDecoder()->videoFrameRate( stream );
00572   DecodeLoopDebug(( "Frame rate %g total %ld", framerate, total_video_frames ));
00573 
00574   if ( framerate <= 1.0 ) {
00575       DecodeLoopDebug(( "Crazy frame rate, resetting to sensible" ));
00576       framerate = 25;
00577   }
00578 
00579   if ( total_video_frames == 1 ) {
00580       DecodeLoopDebug(( "Cannot seek to frame" ));
00581   }
00582 
00583     }
00584 
00585     videoMutex->lock();
00586     current_frame = 0;
00587     prev_frame = -1;
00588     videoMutex->unlock();
00589 
00590     connect( mediaPlayerState, SIGNAL( positionChanged(long) ), this, SLOT( setPosition(long) ) );
00591     connect( mediaPlayerState, SIGNAL( pausedToggled(bool) ), this, SLOT( setPaused(bool) ) );
00592 
00593     //setBackgroundColor( black );
00594     return TRUE;
00595 }
00596 
00597 
00598 void LoopControl::play() {
00599 
00600 #if defined(Q_WS_QWS) && !defined(QT_NO_COP)
00601     if ( !disabledSuspendScreenSaver ) {
00602   disabledSuspendScreenSaver = TRUE;
00603         // Stop the screen from blanking and power saving state
00604   QCopEnvelope("QPE/System", "setScreenSaverMode(int)" )
00605       << ( hasVideoChannel ? QPEApplication::Disable : QPEApplication::DisableSuspend );
00606     }
00607 #endif
00608 
00609     //begin = clock();
00610     playtime.start();
00611     startTimers();
00612     //updateGeometry();
00613 }
00614 
00615 
00616 void LoopControl::setMute( bool on ) {
00617     if ( isMuted != on ) {
00618   isMuted = on;
00619   if ( isMuted ) {
00620   } else {
00621       int frame = current_frame; // mediaPlayerState->curDecoder()->videoGetFrame( stream );
00622       playtime.restart();
00623       playtime = playtime.addMSecs( -frame * 1000 / framerate );
00624       //begin = clock() - (double)frame * CLOCKS_PER_SEC / framerate;
00625       mediaPlayerState->curDecoder()->audioSetSample( frame*freq/framerate, stream );
00626   }
00627     }
00628 }
00629 
00630 

Generated on Sat Nov 5 16:15:37 2005 for OPIE by  doxygen 1.4.2