author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (side-by-side diff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /core/multimedia/opieplayer/loopcontrol.cpp | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
Diffstat (limited to 'core/multimedia/opieplayer/loopcontrol.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/multimedia/opieplayer/loopcontrol.cpp | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/loopcontrol.cpp b/core/multimedia/opieplayer/loopcontrol.cpp new file mode 100644 index 0000000..93a6e3f --- a/dev/null +++ b/core/multimedia/opieplayer/loopcontrol.cpp @@ -0,0 +1,464 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#include <qpe/qpeapplication.h> +#ifdef Q_WS_QWS +#include <qpe/qcopenvelope_qws.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <errno.h> +#include <unistd.h> +#include "loopcontrol.h" +#include "videowidget.h" +#include "audiodevice.h" +#include "mediaplayerplugininterface.h" +#include "mediaplayerstate.h" + + +extern VideoWidget *videoUI; // now only needed to tell it to play a frame +extern MediaPlayerState *mediaPlayerState; + + +//#define DecodeLoopDebug(x) qDebug x +#define DecodeLoopDebug(x) + + +static char *audioBuffer = NULL; +static AudioDevice *audioDevice = NULL; +static bool disabledSuspendScreenSaver = FALSE; +static bool previousSuspendMode = FALSE; + + +pthread_t audio_tid; +pthread_attr_t audio_attr; +bool threadOkToGo = FALSE; + + +class Mutex { +public: + Mutex() { + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutex_init( &mutex, &attr ); + pthread_mutexattr_destroy( &attr ); + } + + ~Mutex() { + pthread_mutex_destroy( &mutex ); + } + + void lock() { + pthread_mutex_lock( &mutex ); + } + + void unlock() { + pthread_mutex_unlock( &mutex ); + } +private: + pthread_mutex_t mutex; +}; + + +void *startAudioThread( void *ptr ) { + LoopControl *mpegView = (LoopControl *)ptr; + while ( TRUE ) { + if ( threadOkToGo && mpegView->moreAudio ) + mpegView->startAudio(); + else + usleep( 10000 ); // Semi-buzy-wait till we are playing again + } + return 0; +} + + +Mutex *audioMutex; + + +LoopControl::LoopControl( QObject *parent, const char *name ) + : QObject( parent, name ) { + isMuted = FALSE; + connect( qApp, SIGNAL( volumeChanged(bool) ), this, SLOT( setMute(bool) ) ); + + audioMutex = new Mutex; + + pthread_attr_init(&audio_attr); +#define USE_REALTIME_AUDIO_THREAD +#ifdef USE_REALTIME_AUDIO_THREAD + // Attempt to set it to real-time round robin + if ( pthread_attr_setschedpolicy( &audio_attr, SCHED_RR ) == 0 ) { + sched_param params; + params.sched_priority = 50; + pthread_attr_setschedparam(&audio_attr,¶ms); + } else { + qDebug( "Error setting up a realtime thread, reverting to using a normal thread." ); + pthread_attr_destroy(&audio_attr); + pthread_attr_init(&audio_attr); + } +#endif + pthread_create(&audio_tid, &audio_attr, (void * (*)(void *))startAudioThread, this); +} + + +LoopControl::~LoopControl() { + stop(); +} + + +static long prev_frame = 0; +static int currentSample = 0; + + +void LoopControl::timerEvent( QTimerEvent *te ) { + + if ( te->timerId() == videoId ) + startVideo(); + + if ( te->timerId() == sliderId ) { + if ( hasAudioChannel && !hasVideoChannel && moreAudio ) { + mediaPlayerState->updatePosition( audioSampleCounter ); + } else if ( hasVideoChannel && moreVideo ) { + mediaPlayerState->updatePosition( current_frame ); + } + } + + if ( !moreVideo && !moreAudio ) { + mediaPlayerState->setPlaying( FALSE ); + mediaPlayerState->setNext(); + } +} + + +void LoopControl::setPosition( long pos ) { + audioMutex->lock(); + + if ( hasVideoChannel && hasAudioChannel ) { + playtime.restart(); + playtime = playtime.addMSecs( long((double)-pos * 1000.0 / framerate) ); + current_frame = pos + 1; + mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream ); + prev_frame = current_frame - 1; + currentSample = (int)( (double)current_frame * freq / framerate ); + mediaPlayerState->curDecoder()->audioSetSample( currentSample, stream ); + audioSampleCounter = currentSample - 1; + } else if ( hasVideoChannel ) { + playtime.restart(); + playtime = playtime.addMSecs( long((double)-pos * 1000.0 / framerate) ); + current_frame = pos + 1; + mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream ); + prev_frame = current_frame - 1; + } else if ( hasAudioChannel ) { + playtime.restart(); + playtime = playtime.addMSecs( long((double)-pos * 1000.0 / freq) ); + currentSample = pos + 1; + mediaPlayerState->curDecoder()->audioSetSample( currentSample, stream ); + audioSampleCounter = currentSample - 1; + } + + audioMutex->unlock(); +} + + +void LoopControl::startVideo() { + + if ( moreVideo ) { + + if ( mediaPlayerState->curDecoder() ) { + + if ( hasAudioChannel && !isMuted ) { + + current_frame = long( playtime.elapsed() * framerate / 1000 ); + + if ( prev_frame != -1 && current_frame <= prev_frame ) + return; + + } else { + // Don't skip + current_frame++; + } + + if ( prev_frame == -1 || current_frame > prev_frame ) { + if ( current_frame > prev_frame + 1 ) { + mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream ); + } + moreVideo = videoUI->playVideo(); + prev_frame = current_frame; + } + + } else { + + moreVideo = FALSE; + killTimer( videoId ); + + } + + } +} + + +void LoopControl::startAudio() { + + audioMutex->lock(); + + if ( moreAudio ) { + + if ( !isMuted && mediaPlayerState->curDecoder() ) { + + currentSample = audioSampleCounter + 1; + + if ( currentSample != audioSampleCounter + 1 ) + qDebug("out of sync with decoder %i %i", currentSample, audioSampleCounter); + + long samplesRead = 0; + mediaPlayerState->curDecoder()->audioReadSamples( (short*)audioBuffer, channels, 1024, samplesRead, stream ); + long sampleWeShouldBeAt = long( playtime.elapsed() ) * freq / 1000; + long sampleWaitTime = currentSample - sampleWeShouldBeAt; + + if ( ( sampleWaitTime > 2000 ) && ( sampleWaitTime < 20000 ) ) { + usleep( (long)((double)sampleWaitTime * 1000000.0 / freq) ); + } else if ( sampleWaitTime <= -5000 ) { + qDebug("need to catch up by: %li (%i,%li)", -sampleWaitTime, currentSample, sampleWeShouldBeAt ); + //mediaPlayerState->curDecoder()->audioSetSample( sampleWeShouldBeAt, stream ); + currentSample = sampleWeShouldBeAt; + } + + audioDevice->write( audioBuffer, samplesRead * 2 * channels ); + audioSampleCounter = currentSample + samplesRead - 1; + + moreAudio = audioSampleCounter <= total_audio_samples; + + } else { + + moreAudio = FALSE; + + } + + } + + audioMutex->unlock(); +} + + +void LoopControl::killTimers() { + + audioMutex->lock(); + + if ( hasVideoChannel ) + killTimer( videoId ); + killTimer( sliderId ); + threadOkToGo = FALSE; + + audioMutex->unlock(); +} + + +void LoopControl::startTimers() { + + audioMutex->lock(); + + moreVideo = FALSE; + moreAudio = FALSE; + + if ( hasVideoChannel ) { + moreVideo = TRUE; + int mSecsBetweenFrames = (int)(100 / framerate); // 10% of the real value + videoId = startTimer( mSecsBetweenFrames ); + } + + if ( hasAudioChannel ) { + moreAudio = TRUE; + threadOkToGo = TRUE; + } + + sliderId = startTimer( 300 ); // update slider every 1/3 second + + audioMutex->unlock(); +} + + +void LoopControl::setPaused( bool pause ) { + + if ( !mediaPlayerState->curDecoder() || !mediaPlayerState->curDecoder()->isOpen() ) + return; + + if ( pause ) { + killTimers(); + } else { + // Force an update of the position + mediaPlayerState->setPosition( mediaPlayerState->position() + 1 ); + mediaPlayerState->setPosition( mediaPlayerState->position() - 1 ); + // Just like we never stopped + startTimers(); + } +} + + +void LoopControl::stop( bool willPlayAgainShortly ) { + +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + if ( !willPlayAgainShortly && disabledSuspendScreenSaver ) { + disabledSuspendScreenSaver = FALSE; + // Re-enable the suspend mode + QCopEnvelope("QPE/System", "setScreenSaverMode(int)" ) << QPEApplication::Enable; + } +#endif + + if ( mediaPlayerState->curDecoder() && mediaPlayerState->curDecoder()->isOpen() ) { + + killTimers(); + + audioMutex->lock(); + + mediaPlayerState->curDecoder()->close(); + + if ( audioDevice ) { + delete audioDevice; + delete audioBuffer; + audioDevice = 0; + audioBuffer = 0; + } + + audioMutex->unlock(); + + } +} + + +bool LoopControl::init( const QString& filename ) { + stop(); + + audioMutex->lock(); + + fileName = filename; + stream = 0; // only play stream 0 for now + current_frame = total_video_frames = total_audio_samples = 0; + + qDebug( "Using the %s decoder", mediaPlayerState->curDecoder()->pluginName() ); + + // ### Hack to use libmpeg3plugin to get the number of audio samples if we are using the libmad plugin + if ( mediaPlayerState->curDecoder()->pluginName() == QString("LibMadPlugin") ) { + if ( mediaPlayerState->libMpeg3Decoder() && mediaPlayerState->libMpeg3Decoder()->open( filename ) ) { + total_audio_samples = mediaPlayerState->libMpeg3Decoder()->audioSamples( 0 ); + mediaPlayerState->libMpeg3Decoder()->close(); + } + } + + if ( !mediaPlayerState->curDecoder()|| !mediaPlayerState->curDecoder()->open( filename ) ) { + audioMutex->unlock(); + return FALSE; + } + + hasAudioChannel = mediaPlayerState->curDecoder()->audioStreams() > 0; + hasVideoChannel = mediaPlayerState->curDecoder()->videoStreams() > 0; + + if ( hasAudioChannel ) { + int astream = 0; + + channels = mediaPlayerState->curDecoder()->audioChannels( astream ); + DecodeLoopDebug(( "channels = %d\n", channels )); + + if ( !total_audio_samples ) + total_audio_samples = mediaPlayerState->curDecoder()->audioSamples( astream ); + + total_audio_samples += 1000; + + mediaPlayerState->setLength( total_audio_samples ); + + freq = mediaPlayerState->curDecoder()->audioFrequency( astream ); + DecodeLoopDebug(( "frequency = %d\n", freq )); + + audioSampleCounter = 0; + + static const int bytes_per_sample = 2; //16 bit + + audioDevice = new AudioDevice( freq, channels, bytes_per_sample ); + audioBuffer = new char[ audioDevice->bufferSize() ]; + channels = audioDevice->channels(); + + //### must check which frequency is actually used. + static const int size = 1; + short int buf[size]; + long samplesRead = 0; + mediaPlayerState->curDecoder()->audioReadSamples( buf, channels, size, samplesRead, stream ); + } + + if ( hasVideoChannel ) { + total_video_frames = mediaPlayerState->curDecoder()->videoFrames( stream ); + + mediaPlayerState->setLength( total_video_frames ); + + framerate = mediaPlayerState->curDecoder()->videoFrameRate( stream ); + DecodeLoopDebug(( "Frame rate %g total %ld", framerate, total_video_frames )); + + if ( framerate <= 1.0 ) { + DecodeLoopDebug(( "Crazy frame rate, resetting to sensible" )); + framerate = 25; + } + + if ( total_video_frames == 1 ) { + DecodeLoopDebug(( "Cannot seek to frame" )); + } + + } + + current_frame = 0; + prev_frame = -1; + + connect( mediaPlayerState, SIGNAL( positionChanged( long ) ), this, SLOT( setPosition( long ) ) ); + connect( mediaPlayerState, SIGNAL( pausedToggled( bool ) ), this, SLOT( setPaused( bool ) ) ); + + audioMutex->unlock(); + + return TRUE; +} + + +void LoopControl::play() { + +#if defined(Q_WS_QWS) && !defined(QT_NO_COP) + if ( !disabledSuspendScreenSaver || previousSuspendMode != hasVideoChannel ) { + disabledSuspendScreenSaver = TRUE; + previousSuspendMode = hasVideoChannel; + // Stop the screen from blanking and power saving state + QCopEnvelope("QPE/System", "setScreenSaverMode(int)" ) + << ( hasVideoChannel ? QPEApplication::Disable : QPEApplication::DisableSuspend ); + } +#endif + + playtime.start(); + startTimers(); +} + + +void LoopControl::setMute( bool on ) { + if ( on != isMuted ) { + isMuted = on; + if ( !on ) { + // Force an update of the position + mediaPlayerState->setPosition( mediaPlayerState->position() + 1 ); + mediaPlayerState->setPosition( mediaPlayerState->position() - 1 ); + // Resume playing audio + moreAudio = TRUE; + } + } +} + + |