summaryrefslogtreecommitdiff
path: root/core/multimedia/opieplayer/loopcontrol_threaded.cpp
Unidiff
Diffstat (limited to 'core/multimedia/opieplayer/loopcontrol_threaded.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/multimedia/opieplayer/loopcontrol_threaded.cpp626
1 files changed, 626 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/loopcontrol_threaded.cpp b/core/multimedia/opieplayer/loopcontrol_threaded.cpp
new file mode 100644
index 0000000..2ec4a48
--- a/dev/null
+++ b/core/multimedia/opieplayer/loopcontrol_threaded.cpp
@@ -0,0 +1,626 @@
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
21#define _REENTRANT
22
23#include <qpe/qpeapplication.h>
24#include <qimage.h>
25#include <qpainter.h>
26#ifdef Q_WS_QWS
27#include <qpe/qcopenvelope_qws.h>
28#endif
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <time.h>
33#include <unistd.h>
34#include <pthread.h>
35#include "loopcontrol.h"
36#include "audiodevice.h"
37#include "videowidget.h"
38#include "audiowidget.h"
39#include "mediaplayerplugininterface.h"
40#include "mediaplayerstate.h"
41
42
43#if defined(QT_QWS_CUSTOM) || defined(QT_QWS_IPAQ)
44#define USE_REALTIME_AUDIO_THREAD
45#endif
46
47
48extern VideoWidget *videoUI; // now only needed to tell it to play a frame
49extern MediaPlayerState *mediaPlayerState;
50
51
52#define DecodeLoopDebug(x) qDebug x
53//#define DecodeLoopDebug(x)
54
55
56 static char *audioBuffer = NULL;
57static AudioDevice *audioDevice = NULL;
58 static bool disabledSuspendScreenSaver = FALSE;
59
60
61 pthread_tvideo_tid;
62pthread_attr_t video_attr;
63 pthread_taudio_tid;
64pthread_attr_t audio_attr;
65
66
67bool emitPlayFinished = FALSE;
68bool emitChangePos = FALSE;
69
70
71class Mutex {
72public:
73 Mutex() {
74 pthread_mutexattr_t attr;
75 pthread_mutexattr_init( &attr );
76 pthread_mutex_init( &mutex, &attr );
77 pthread_mutexattr_destroy( &attr );
78 }
79
80 ~Mutex() {
81 pthread_mutex_destroy( &mutex );
82 }
83
84 void lock() {
85 pthread_mutex_lock( &mutex );
86 }
87
88 void unlock() {
89 pthread_mutex_unlock( &mutex );
90 }
91/*
92 bool locked() {
93 switch ( pthread_mutex_trylock( &mutex ) ) {
94 case EBUSY:
95 return TRUE;
96 case 0:
97 pthread_mutex_unlock( &mutex );
98 default:
99 return FALSE;
100 }
101 }
102*/
103private:
104 pthread_mutex_t mutex;
105};
106
107
108class currentFrameObj {
109public:
110 currentFrameObj() : value( 0 ) { }
111 void set( long f ) {
112 mutex.lock();
113 value = f;
114 mediaPlayerState->curDecoder()->videoSetFrame( f, 0 );
115 mutex.unlock();
116 }
117 long get() {
118 return value;
119 }
120private:
121 long value;
122 Mutex mutex;
123};
124
125
126Mutex *videoMutex;
127Mutex *audioMutex;
128Mutex *globalMutex;
129
130
131 clock_tbegin;
132
133
134LoopControl::LoopControl( QObject *parent, const char *name )
135 : QObject( parent, name ) {
136 isMuted = FALSE;
137 connect( qApp, SIGNAL( volumeChanged(bool) ), this, SLOT( setMute(bool) ) );
138 timerid = startTimer( 200 );
139 videoMutex = new Mutex;
140 audioMutex = new Mutex;
141 globalMutex = new Mutex;
142 //begin = clock();
143}
144
145
146LoopControl::~LoopControl() {
147 stop();
148 killTimer( timerid );
149}
150
151
152static bool sendingNewPos = FALSE;
153static long prev_frame = 0;
154static int currentSample = 0;
155
156
157void LoopControl::timerEvent( QTimerEvent* ) {
158 // We need to emit playFinished from the main thread, not one of the
159 // decoding threads else we'll have all kinds of yucky things happen (reentrance).
160 // playFinished will eventually call stop() which stops these threads.
161 if ( emitPlayFinished ) {
162 emitPlayFinished = FALSE;
163 mediaPlayerState->setPlaying( FALSE );
164 }
165
166 if ( emitChangePos ) {
167
168 emitChangePos = FALSE;
169
170 if ( hasVideoChannel && hasAudioChannel ) {
171 sendingNewPos = TRUE;
172 mediaPlayerState->setPosition( current_frame );
173 } else if ( hasVideoChannel ) {
174 sendingNewPos = TRUE;
175 mediaPlayerState->setPosition( current_frame );
176 } else if ( hasAudioChannel ) {
177 sendingNewPos = TRUE;
178 mediaPlayerState->setPosition( audioSampleCounter );
179 }
180
181 }
182}
183
184
185
186
187void LoopControl::setPosition( long pos ) {
188 if ( sendingNewPos ) {
189 sendingNewPos = FALSE;
190 return;
191 }
192
193 if ( hasVideoChannel && hasAudioChannel ) {
194 videoMutex->lock();
195 audioMutex->lock();
196qDebug("setting position");
197 playtime.restart();
198 playtime = playtime.addMSecs( -pos * 1000 / framerate );
199 //begin = clock() - (double)pos * CLOCKS_PER_SEC / framerate;
200 current_frame = pos + 1;
201 mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
202 prev_frame = current_frame - 1;
203 currentSample = (int)( current_frame * freq / framerate );
204 mediaPlayerState->curDecoder()->audioSetSample( currentSample, stream );
205 audioSampleCounter = currentSample - 1;
206 audioMutex->unlock();
207 videoMutex->unlock();
208 } else if ( hasVideoChannel ) {
209 videoMutex->lock();
210 playtime.restart();
211 playtime = playtime.addMSecs( -pos * 1000 / framerate );
212 //begin = clock() - (double)pos * CLOCKS_PER_SEC / framerate;
213 current_frame = pos + 1;
214 mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
215 videoMutex->unlock();
216 prev_frame = current_frame - 1;
217 } else if ( hasAudioChannel ) {
218 audioMutex->lock();
219 playtime.restart();
220 playtime = playtime.addMSecs( -pos * 1000 / freq );
221 //begin = clock() - (double)pos * CLOCKS_PER_SEC / freq;
222 currentSample = pos + 1; // (int)( current_frame * freq / framerate );
223 mediaPlayerState->curDecoder()->audioSetSample( currentSample, stream );
224 audioSampleCounter = currentSample - 1;
225 audioMutex->unlock();
226 }
227}
228
229
230void *startVideoThread( void *ptr ) {
231 LoopControl *mpegView = (LoopControl *)ptr;
232 mpegView->startVideo();
233 return 0;
234}
235
236void *startAudioThread( void *ptr ) {
237 LoopControl *mpegView = (LoopControl *)ptr;
238 mpegView->startAudio();
239 return 0;
240}
241
242void LoopControl::startVideo() {
243 moreVideo = TRUE;
244
245 while ( moreVideo ) {
246
247 if ( mediaPlayerState->curDecoder() && hasVideoChannel ) {
248
249 if ( hasAudioChannel && !isMuted ) {
250
251 bool done = FALSE;
252
253 do {
254
255
256/*
257 videoMutex->lock();
258 current_frame = int( (double)playtime.elapsed() * (double)framerate / 1000.0 );
259 //current_frame = ( clock() - begin ) * (double)framerate / CLOCKS_PER_SEC;
260
261 // Sync to Audio
262 // current_frame = (long)((double)(audioSampleCounter - 1000) * framerate / (double)freq);
263
264 long mSecsToNextFrame = 0;
265
266 if ( current_frame == prev_frame ) {
267 int nf = current_frame + 1;
268 if ( nf > 0 && nf != total_video_frames )
269 // mSecsToNextFrame = long(double(nf * CLOCKS_PER_SEC) / framerate) - ( clock() - begin );
270 mSecsToNextFrame = long(double(nf * 1000) / framerate) - ( playtime.elapsed() );
271 }
272 videoMutex->unlock();
273
274 if ( mSecsToNextFrame ) {
275 usleep( mSecsToNextFrame ); // wait a bit
276
277 videoMutex->lock();
278 // This should now be the next frame
279 current_frame = int( (double)playtime.elapsed() * (double)framerate / 1000.0 );
280 //current_frame = ( clock() - begin ) * (double)framerate / CLOCKS_PER_SEC;
281 videoMutex->unlock();
282 }
283
284 videoMutex->lock();
285 done = current_frame >= prev_frame;
286 videoMutex->unlock();
287*/
288 videoMutex->lock();
289 current_frame = int( (double)playtime.elapsed() * (double)framerate / 1000.0 );
290 done = current_frame >= prev_frame;
291 videoMutex->unlock();
292 if ( !done )
293 usleep( 1000 ); // wait a bit
294
295 } while ( !done );
296
297 // qDebug("elapsed: %i %i (%f)", int( playtime.elapsed() ), current_frame, framerate );
298
299 } else {
300 videoMutex->lock();
301 current_frame++;
302 videoMutex->unlock();
303 }
304
305 videoMutex->lock();
306 bool check = current_frame && current_frame > prev_frame;
307 videoMutex->unlock();
308
309 if ( check ) {
310 videoMutex->lock();
311 if ( current_frame > prev_frame + 1 ) {
312 qDebug("skipped a frame");
313 mediaPlayerState->curDecoder()->videoSetFrame( current_frame, stream );
314 }
315 prev_frame = current_frame;
316 if ( moreVideo = videoUI->playVideo() )
317 emitChangePos = TRUE;
318 videoMutex->unlock();
319 }
320
321 } else
322 moreVideo = FALSE;
323
324 }
325
326 if ( !moreVideo && !moreAudio )
327 emitPlayFinished = TRUE;
328
329 pthread_exit(NULL);
330}
331
332void LoopControl::startAudio() {
333 moreAudio = TRUE;
334
335 while ( moreAudio ) {
336
337 if ( !isMuted && mediaPlayerState->curDecoder() && hasAudioChannel ) {
338
339 audioMutex->lock();
340 currentSample = mediaPlayerState->curDecoder()->audioGetSample( stream );
341
342 if ( currentSample == 0 )
343 currentSample = audioSampleCounter + 1;
344
345 if ( currentSample != audioSampleCounter + 1 )
346 qDebug("out of sync with decoder %i %i", currentSample, audioSampleCounter);
347 audioMutex->unlock();
348
349/*
350 int sampleWeShouldBeAt = int( playtime.elapsed() ) * freq / 1000;
351
352 if ( sampleWeShouldBeAt - currentSample > 20000 ) {
353 mediaPlayerState->curDecoder()->audioSetSample( sampleWeShouldBeAt, stream );
354 currentSample = sampleWeShouldBeAt;
355 }
356*/
357 long samplesRead = 0;
358
359 const long samples = 1024;
360
361 moreAudio = !mediaPlayerState->curDecoder()->audioReadSamples( (short*)audioBuffer, channels, samples, samplesRead, stream );
362
363 audioMutex->lock();
364 long sampleWeShouldBeAt = long( playtime.elapsed() ) * freq / 1000;
365 //long sampleWeShouldBeAt = long( clock() - begin ) * (double) freq / CLOCKS_PER_SEC;
366 long sampleWaitTime = currentSample - sampleWeShouldBeAt;
367 audioMutex->unlock();
368
369 if ( sampleWaitTime >= 0 && sampleWaitTime <= 2000 ) {
370 //qDebug("sampleWaitTime: %i", sampleWaitTime);
371 usleep( ( sampleWaitTime * 1000000 ) / ( freq ) );
372 } else {
373 audioMutex->lock();
374 if ( sampleWaitTime <= -2000 ) {
375 qDebug("need to catch up by: %li (%i,%li)", -sampleWaitTime, currentSample, sampleWeShouldBeAt );
376 mediaPlayerState->curDecoder()->audioSetSample( sampleWeShouldBeAt, stream );
377 currentSample = sampleWeShouldBeAt;
378 }
379 audioMutex->unlock();
380 }
381
382 audioDevice->write( audioBuffer, samplesRead * 2 * channels );
383
384 audioMutex->lock();
385 // audioSampleCounter += samplesRead;
386 audioSampleCounter = currentSample + samplesRead - 1;
387 audioMutex->unlock();
388
389 if ( !hasVideoChannel )
390 emitChangePos = TRUE;
391
392 //qDebug("currentSample: %i audioSampleCounter: %i total_audio_samples: %i", currentSample, audioSampleCounter, total_audio_samples);
393 // qDebug("current: %i counter: %i total: %i", currentSample, audioSampleCounter, (int)total_audio_samples);
394 moreAudio = audioSampleCounter <= total_audio_samples;
395
396 } else {
397
398 if ( mediaPlayerState->curDecoder() && hasAudioChannel )
399 usleep( 100000 ); // Check every 1/10 sec to see if mute is off
400 else
401 moreAudio = FALSE;
402
403 }
404 }
405
406 qDebug( "End of file" );
407
408 if ( !moreVideo && !moreAudio )
409 emitPlayFinished = TRUE;
410
411 pthread_exit(NULL);
412}
413
414void LoopControl::killTimers() {
415 if ( hasVideoChannel ) {
416 if ( pthread_self() != video_tid ) {
417 if ( pthread_cancel(video_tid) == 0 ) {
418 void *thread_result = 0;
419 if ( pthread_join(video_tid,&thread_result) != 0 )
420 qDebug("thread join error 1");
421 pthread_attr_destroy(&video_attr);
422 }
423 }
424 }
425 if ( hasAudioChannel ) {
426 if ( pthread_self() != audio_tid ) {
427 if ( pthread_cancel(audio_tid) == 0 ) {
428 void *thread_result = 0;
429 if ( pthread_join(audio_tid,&thread_result) != 0 )
430 qDebug("thread join error 2");
431 pthread_attr_destroy(&audio_attr);
432 }
433 }
434 }
435}
436
437void LoopControl::startTimers() {
438 moreVideo = FALSE;
439 moreAudio = FALSE;
440
441 if ( hasVideoChannel ) {
442 moreVideo = TRUE;
443 pthread_attr_init(&video_attr);
444 pthread_create(&video_tid, &video_attr, (void * (*)(void *))startVideoThread, this);
445 }
446
447 if ( hasAudioChannel ) {
448 moreAudio = TRUE;
449 pthread_attr_init(&audio_attr);
450#ifdef USE_REALTIME_AUDIO_THREAD
451 pthread_attr_setschedpolicy(&audio_attr,SCHED_RR); // Real-time round robin
452 //qDebug("min: %i, max: %i", sched_get_priority_min( SCHED_RR ), sched_get_priority_max( SCHED_RR ) );
453 sched_param params;
454 params.sched_priority = 50;
455 pthread_attr_setschedparam(&audio_attr,&params);
456#endif
457 pthread_create(&audio_tid, &audio_attr, (void * (*)(void *))startAudioThread, this);
458 }
459}
460
461
462
463
464void LoopControl::setPaused( bool pause ) {
465 static int whenPaused = 0;
466
467 if ( !mediaPlayerState->curDecoder() || !mediaPlayerState->curDecoder()->isOpen() )
468 return;
469
470 if ( pause ) {
471 // Remember where we are
472 whenPaused = playtime.elapsed();
473 killTimers();
474 } else {
475 // Just like we never stopped
476 playtime.restart();
477 playtime = playtime.addMSecs( -whenPaused );
478 whenPaused = 0;
479 startTimers();
480 }
481}
482
483
484void LoopControl::stop( bool willPlayAgainShortly ) {
485
486#if defined(Q_WS_QWS) && !defined(QT_NO_COP)
487 if ( !willPlayAgainShortly && disabledSuspendScreenSaver ) {
488 disabledSuspendScreenSaver = FALSE;
489 // Re-enable the suspend mode
490 QCopEnvelope("QPE/System", "setScreenSaverMode(int)" ) << QPEApplication::Enable;
491 }
492#endif
493
494 if ( mediaPlayerState->curDecoder() && mediaPlayerState->curDecoder()->isOpen() ) {
495
496 killTimers();
497
498 mediaPlayerState->curDecoder()->close();
499
500 if ( audioDevice ) {
501 delete audioDevice;
502 delete audioBuffer;
503 audioDevice = 0;
504 audioBuffer = 0;
505 }
506
507 }
508}
509
510
511bool LoopControl::init( const QString& filename ) {
512 stop();
513 fileName = filename;
514 stream = 0; // only play stream 0 for now
515 current_frame = total_video_frames = total_audio_samples = 0;
516
517 qDebug( "Using the %s decoder", mediaPlayerState->curDecoder()->pluginName() );
518
519 // ### Hack to use libmpeg3plugin to get the number of audio samples if we are using the libmad plugin
520 if ( mediaPlayerState->curDecoder()->pluginName() == QString("LibMadPlugin") ) {
521 if ( mediaPlayerState->libMpeg3Decoder() && mediaPlayerState->libMpeg3Decoder()->open( filename ) ) {
522 total_audio_samples = mediaPlayerState->libMpeg3Decoder()->audioSamples( 0 );
523 mediaPlayerState->libMpeg3Decoder()->close();
524 }
525 }
526
527 if ( !mediaPlayerState->curDecoder()|| !mediaPlayerState->curDecoder()->open( filename ) )
528 return FALSE;
529
530 hasAudioChannel = mediaPlayerState->curDecoder()->audioStreams() > 0;
531 hasVideoChannel = mediaPlayerState->curDecoder()->videoStreams() > 0;
532
533 if ( hasAudioChannel ) {
534 int astream = 0;
535
536 channels = mediaPlayerState->curDecoder()->audioChannels( astream );
537 DecodeLoopDebug(( "channels = %d\n", channels ));
538
539 if ( !total_audio_samples )
540 total_audio_samples = mediaPlayerState->curDecoder()->audioSamples( astream );
541
542 mediaPlayerState->setLength( total_audio_samples );
543
544 freq = mediaPlayerState->curDecoder()->audioFrequency( astream );
545 DecodeLoopDebug(( "frequency = %d\n", freq ));
546
547 audioSampleCounter = 0;
548
549 static const int bytes_per_sample = 2; //16 bit
550
551 audioDevice = new AudioDevice( freq, channels, bytes_per_sample );
552 audioBuffer = new char[ audioDevice->bufferSize() ];
553 channels = audioDevice->channels();
554
555 //### must check which frequency is actually used.
556 static const int size = 1;
557 short int buf[size];
558 long samplesRead = 0;
559 mediaPlayerState->curDecoder()->audioReadSamples( buf, channels, size, samplesRead, stream );
560 }
561
562 if ( hasVideoChannel ) {
563 total_video_frames = mediaPlayerState->curDecoder()->videoFrames( stream );
564
565 mediaPlayerState->setLength( total_video_frames );
566
567 framerate = mediaPlayerState->curDecoder()->videoFrameRate( stream );
568 DecodeLoopDebug(( "Frame rate %g total %ld", framerate, total_video_frames ));
569
570 if ( framerate <= 1.0 ) {
571 DecodeLoopDebug(( "Crazy frame rate, resetting to sensible" ));
572 framerate = 25;
573 }
574
575 if ( total_video_frames == 1 ) {
576 DecodeLoopDebug(( "Cannot seek to frame" ));
577 }
578
579 }
580
581 videoMutex->lock();
582 current_frame = 0;
583 prev_frame = -1;
584 videoMutex->unlock();
585
586 connect( mediaPlayerState, SIGNAL( positionChanged( long ) ), this, SLOT( setPosition( long ) ) );
587 connect( mediaPlayerState, SIGNAL( pausedToggled( bool ) ), this, SLOT( setPaused( bool ) ) );
588
589 //setBackgroundColor( black );
590 return TRUE;
591}
592
593
594void LoopControl::play() {
595
596#if defined(Q_WS_QWS) && !defined(QT_NO_COP)
597 if ( !disabledSuspendScreenSaver ) {
598 disabledSuspendScreenSaver = TRUE;
599 // Stop the screen from blanking and power saving state
600 QCopEnvelope("QPE/System", "setScreenSaverMode(int)" )
601 << ( hasVideoChannel ? QPEApplication::Disable : QPEApplication::DisableSuspend );
602 }
603#endif
604
605 //begin = clock();
606 playtime.start();
607 startTimers();
608 //updateGeometry();
609}
610
611
612void LoopControl::setMute( bool on ) {
613 if ( isMuted != on ) {
614 isMuted = on;
615 if ( isMuted ) {
616 } else {
617 int frame = current_frame; // mediaPlayerState->curDecoder()->videoGetFrame( stream );
618 playtime.restart();
619 playtime = playtime.addMSecs( -frame * 1000 / framerate );
620 //begin = clock() - (double)frame * CLOCKS_PER_SEC / framerate;
621 mediaPlayerState->curDecoder()->audioSetSample( frame*freq/framerate, stream );
622 }
623 }
624}
625
626