summaryrefslogtreecommitdiff
path: root/core/multimedia/opieplayer/loopcontrol.cpp
Unidiff
Diffstat (limited to 'core/multimedia/opieplayer/loopcontrol.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/multimedia/opieplayer/loopcontrol.cpp464
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 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20#include <qpe/qpeapplication.h>
21#ifdef Q_WS_QWS
22#include <qpe/qcopenvelope_qws.h>
23#endif
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <pthread.h>
28#include <errno.h>
29#include <unistd.h>
30#include "loopcontrol.h"
31#include "videowidget.h"
32#include "audiodevice.h"
33#include "mediaplayerplugininterface.h"
34#include "mediaplayerstate.h"
35
36
37extern VideoWidget *videoUI; // now only needed to tell it to play a frame
38extern MediaPlayerState *mediaPlayerState;
39
40
41//#define DecodeLoopDebug(x) qDebug x
42#define DecodeLoopDebug(x)
43
44
45 static char *audioBuffer = NULL;
46static AudioDevice *audioDevice = NULL;
47 static bool disabledSuspendScreenSaver = FALSE;
48 static bool previousSuspendMode = FALSE;
49
50
51 pthread_taudio_tid;
52pthread_attr_t audio_attr;
53bool threadOkToGo = FALSE;
54
55
56class Mutex {
57public:
58 Mutex() {
59 pthread_mutexattr_t attr;
60 pthread_mutexattr_init( &attr );
61 pthread_mutex_init( &mutex, &attr );
62 pthread_mutexattr_destroy( &attr );
63 }
64
65 ~Mutex() {
66 pthread_mutex_destroy( &mutex );
67 }
68
69 void lock() {
70 pthread_mutex_lock( &mutex );
71 }
72
73 void unlock() {
74 pthread_mutex_unlock( &mutex );
75 }
76private:
77 pthread_mutex_t mutex;
78};
79
80
81void *startAudioThread( void *ptr ) {
82 LoopControl *mpegView = (LoopControl *)ptr;
83 while ( TRUE ) {
84 if ( threadOkToGo && mpegView->moreAudio )
85 mpegView->startAudio();
86 else
87 usleep( 10000 ); // Semi-buzy-wait till we are playing again
88 }
89 return 0;
90}
91
92
93Mutex *audioMutex;
94
95
96LoopControl::LoopControl( QObject *parent, const char *name )
97 : QObject( parent, name ) {
98 isMuted = FALSE;
99 connect( qApp, SIGNAL( volumeChanged(bool) ), this, SLOT( setMute(bool) ) );
100
101 audioMutex = new Mutex;
102
103 pthread_attr_init(&audio_attr);
104#define USE_REALTIME_AUDIO_THREAD
105#ifdef USE_REALTIME_AUDIO_THREAD
106 // Attempt to set it to real-time round robin
107 if ( pthread_attr_setschedpolicy( &audio_attr, SCHED_RR ) == 0 ) {
108 sched_param params;
109 params.sched_priority = 50;
110 pthread_attr_setschedparam(&audio_attr,&params);
111 } else {
112 qDebug( "Error setting up a realtime thread, reverting to using a normal thread." );
113 pthread_attr_destroy(&audio_attr);
114 pthread_attr_init(&audio_attr);
115 }
116#endif
117 pthread_create(&audio_tid, &audio_attr, (void * (*)(void *))startAudioThread, this);
118}
119
120
121LoopControl::~LoopControl() {
122 stop();
123}
124
125
126static long prev_frame = 0;
127static int currentSample = 0;
128
129
130void LoopControl::timerEvent( QTimerEvent *te ) {
131
132 if ( te->timerId() == videoId )
133 startVideo();
134
135 if ( te->timerId() == sliderId ) {
136 if ( hasAudioChannel && !hasVideoChannel && moreAudio ) {
137 mediaPlayerState->updatePosition( audioSampleCounter );
138 } else if ( hasVideoChannel && moreVideo ) {
139 mediaPlayerState->updatePosition( current_frame );
140 }
141 }
142
143 if ( !moreVideo && !moreAudio ) {
144 mediaPlayerState->setPlaying( FALSE );
145 mediaPlayerState->setNext();
146 }
147}
148
149
150void LoopControl::setPosition( long pos ) {
151 audioMutex->lock();
152
153 if ( hasVideoChannel && hasAudioChannel ) {
154 playtime.restart();
155 playtime = playtime.addMSecs( long((double)-pos * 1000.0 / framerate) );
156 current_frame = pos + 1;
157 mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
158 prev_frame = current_frame - 1;
159 currentSample = (int)( (double)current_frame * freq / framerate );
160 mediaPlayerState->curDecoder()->audioSetSample( currentSample, stream );
161 audioSampleCounter = currentSample - 1;
162 } else if ( hasVideoChannel ) {
163 playtime.restart();
164 playtime = playtime.addMSecs( long((double)-pos * 1000.0 / framerate) );
165 current_frame = pos + 1;
166 mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
167 prev_frame = current_frame - 1;
168 } else if ( hasAudioChannel ) {
169 playtime.restart();
170 playtime = playtime.addMSecs( long((double)-pos * 1000.0 / freq) );
171 currentSample = pos + 1;
172 mediaPlayerState->curDecoder()->audioSetSample( currentSample, stream );
173 audioSampleCounter = currentSample - 1;
174 }
175
176 audioMutex->unlock();
177}
178
179
180void LoopControl::startVideo() {
181
182 if ( moreVideo ) {
183
184 if ( mediaPlayerState->curDecoder() ) {
185
186 if ( hasAudioChannel && !isMuted ) {
187
188 current_frame = long( playtime.elapsed() * framerate / 1000 );
189
190 if ( prev_frame != -1 && current_frame <= prev_frame )
191 return;
192
193 } else {
194 // Don't skip
195 current_frame++;
196 }
197
198 if ( prev_frame == -1 || current_frame > prev_frame ) {
199 if ( current_frame > prev_frame + 1 ) {
200 mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
201 }
202 moreVideo = videoUI->playVideo();
203 prev_frame = current_frame;
204 }
205
206 } else {
207
208 moreVideo = FALSE;
209 killTimer( videoId );
210
211 }
212
213 }
214}
215
216
217void LoopControl::startAudio() {
218
219 audioMutex->lock();
220
221 if ( moreAudio ) {
222
223 if ( !isMuted && mediaPlayerState->curDecoder() ) {
224
225 currentSample = audioSampleCounter + 1;
226
227 if ( currentSample != audioSampleCounter + 1 )
228 qDebug("out of sync with decoder %i %i", currentSample, audioSampleCounter);
229
230 long samplesRead = 0;
231 mediaPlayerState->curDecoder()->audioReadSamples( (short*)audioBuffer, channels, 1024, samplesRead, stream );
232 long sampleWeShouldBeAt = long( playtime.elapsed() ) * freq / 1000;
233 long sampleWaitTime = currentSample - sampleWeShouldBeAt;
234
235 if ( ( sampleWaitTime > 2000 ) && ( sampleWaitTime < 20000 ) ) {
236 usleep( (long)((double)sampleWaitTime * 1000000.0 / freq) );
237 } else if ( sampleWaitTime <= -5000 ) {
238 qDebug("need to catch up by: %li (%i,%li)", -sampleWaitTime, currentSample, sampleWeShouldBeAt );
239 //mediaPlayerState->curDecoder()->audioSetSample( sampleWeShouldBeAt, stream );
240 currentSample = sampleWeShouldBeAt;
241 }
242
243 audioDevice->write( audioBuffer, samplesRead * 2 * channels );
244 audioSampleCounter = currentSample + samplesRead - 1;
245
246 moreAudio = audioSampleCounter <= total_audio_samples;
247
248 } else {
249
250 moreAudio = FALSE;
251
252 }
253
254 }
255
256 audioMutex->unlock();
257}
258
259
260void LoopControl::killTimers() {
261
262 audioMutex->lock();
263
264 if ( hasVideoChannel )
265 killTimer( videoId );
266 killTimer( sliderId );
267 threadOkToGo = FALSE;
268
269 audioMutex->unlock();
270}
271
272
273void LoopControl::startTimers() {
274
275 audioMutex->lock();
276
277 moreVideo = FALSE;
278 moreAudio = FALSE;
279
280 if ( hasVideoChannel ) {
281 moreVideo = TRUE;
282 int mSecsBetweenFrames = (int)(100 / framerate); // 10% of the real value
283 videoId = startTimer( mSecsBetweenFrames );
284 }
285
286 if ( hasAudioChannel ) {
287 moreAudio = TRUE;
288 threadOkToGo = TRUE;
289 }
290
291 sliderId = startTimer( 300 ); // update slider every 1/3 second
292
293 audioMutex->unlock();
294}
295
296
297void LoopControl::setPaused( bool pause ) {
298
299 if ( !mediaPlayerState->curDecoder() || !mediaPlayerState->curDecoder()->isOpen() )
300 return;
301
302 if ( pause ) {
303 killTimers();
304 } else {
305 // Force an update of the position
306 mediaPlayerState->setPosition( mediaPlayerState->position() + 1 );
307 mediaPlayerState->setPosition( mediaPlayerState->position() - 1 );
308 // Just like we never stopped
309 startTimers();
310 }
311}
312
313
314void LoopControl::stop( bool willPlayAgainShortly ) {
315
316#if defined(Q_WS_QWS) && !defined(QT_NO_COP)
317 if ( !willPlayAgainShortly && disabledSuspendScreenSaver ) {
318 disabledSuspendScreenSaver = FALSE;
319 // Re-enable the suspend mode
320 QCopEnvelope("QPE/System", "setScreenSaverMode(int)" ) << QPEApplication::Enable;
321 }
322#endif
323
324 if ( mediaPlayerState->curDecoder() && mediaPlayerState->curDecoder()->isOpen() ) {
325
326 killTimers();
327
328 audioMutex->lock();
329
330 mediaPlayerState->curDecoder()->close();
331
332 if ( audioDevice ) {
333 delete audioDevice;
334 delete audioBuffer;
335 audioDevice = 0;
336 audioBuffer = 0;
337 }
338
339 audioMutex->unlock();
340
341 }
342}
343
344
345bool LoopControl::init( const QString& filename ) {
346 stop();
347
348 audioMutex->lock();
349
350 fileName = filename;
351 stream = 0; // only play stream 0 for now
352 current_frame = total_video_frames = total_audio_samples = 0;
353
354 qDebug( "Using the %s decoder", mediaPlayerState->curDecoder()->pluginName() );
355
356 // ### Hack to use libmpeg3plugin to get the number of audio samples if we are using the libmad plugin
357 if ( mediaPlayerState->curDecoder()->pluginName() == QString("LibMadPlugin") ) {
358 if ( mediaPlayerState->libMpeg3Decoder() && mediaPlayerState->libMpeg3Decoder()->open( filename ) ) {
359 total_audio_samples = mediaPlayerState->libMpeg3Decoder()->audioSamples( 0 );
360 mediaPlayerState->libMpeg3Decoder()->close();
361 }
362 }
363
364 if ( !mediaPlayerState->curDecoder()|| !mediaPlayerState->curDecoder()->open( filename ) ) {
365 audioMutex->unlock();
366 return FALSE;
367 }
368
369 hasAudioChannel = mediaPlayerState->curDecoder()->audioStreams() > 0;
370 hasVideoChannel = mediaPlayerState->curDecoder()->videoStreams() > 0;
371
372 if ( hasAudioChannel ) {
373 int astream = 0;
374
375 channels = mediaPlayerState->curDecoder()->audioChannels( astream );
376 DecodeLoopDebug(( "channels = %d\n", channels ));
377
378 if ( !total_audio_samples )
379 total_audio_samples = mediaPlayerState->curDecoder()->audioSamples( astream );
380
381 total_audio_samples += 1000;
382
383 mediaPlayerState->setLength( total_audio_samples );
384
385 freq = mediaPlayerState->curDecoder()->audioFrequency( astream );
386 DecodeLoopDebug(( "frequency = %d\n", freq ));
387
388 audioSampleCounter = 0;
389
390 static const int bytes_per_sample = 2; //16 bit
391
392 audioDevice = new AudioDevice( freq, channels, bytes_per_sample );
393 audioBuffer = new char[ audioDevice->bufferSize() ];
394 channels = audioDevice->channels();
395
396 //### must check which frequency is actually used.
397 static const int size = 1;
398 short int buf[size];
399 long samplesRead = 0;
400 mediaPlayerState->curDecoder()->audioReadSamples( buf, channels, size, samplesRead, stream );
401 }
402
403 if ( hasVideoChannel ) {
404 total_video_frames = mediaPlayerState->curDecoder()->videoFrames( stream );
405
406 mediaPlayerState->setLength( total_video_frames );
407
408 framerate = mediaPlayerState->curDecoder()->videoFrameRate( stream );
409 DecodeLoopDebug(( "Frame rate %g total %ld", framerate, total_video_frames ));
410
411 if ( framerate <= 1.0 ) {
412 DecodeLoopDebug(( "Crazy frame rate, resetting to sensible" ));
413 framerate = 25;
414 }
415
416 if ( total_video_frames == 1 ) {
417 DecodeLoopDebug(( "Cannot seek to frame" ));
418 }
419
420 }
421
422 current_frame = 0;
423 prev_frame = -1;
424
425 connect( mediaPlayerState, SIGNAL( positionChanged( long ) ), this, SLOT( setPosition( long ) ) );
426 connect( mediaPlayerState, SIGNAL( pausedToggled( bool ) ), this, SLOT( setPaused( bool ) ) );
427
428 audioMutex->unlock();
429
430 return TRUE;
431}
432
433
434void LoopControl::play() {
435
436#if defined(Q_WS_QWS) && !defined(QT_NO_COP)
437 if ( !disabledSuspendScreenSaver || previousSuspendMode != hasVideoChannel ) {
438 disabledSuspendScreenSaver = TRUE;
439 previousSuspendMode = hasVideoChannel;
440 // Stop the screen from blanking and power saving state
441 QCopEnvelope("QPE/System", "setScreenSaverMode(int)" )
442 << ( hasVideoChannel ? QPEApplication::Disable : QPEApplication::DisableSuspend );
443 }
444#endif
445
446 playtime.start();
447 startTimers();
448}
449
450
451void LoopControl::setMute( bool on ) {
452 if ( on != isMuted ) {
453 isMuted = on;
454 if ( !on ) {
455 // Force an update of the position
456 mediaPlayerState->setPosition( mediaPlayerState->position() + 1 );
457 mediaPlayerState->setPosition( mediaPlayerState->position() - 1 );
458 // Resume playing audio
459 moreAudio = TRUE;
460 }
461 }
462}
463
464