1
/*  This file is part of the KDE project.
2
3
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
5
This library is free software: you can redistribute it and/or modify
6
it under the terms of the GNU Lesser General Public License as published by
7
the Free Software Foundation, either version 2.1 or 3 of the License.
8
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU Lesser General Public License for more details.
13
14
You should have received a copy of the GNU Lesser General Public License
15
along with this library.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
18
#include "mediaobject.h"
19
#include "audiooutput.h"
20
21
#include <QtCore/QVector>
22
#include <QtCore/QTimerEvent>
23
#include <QtCore/QTimer>
24
#include <QtCore/QTime>
25
#include <QtCore/QLibrary>
26
#include <QtCore/QUrl>
27
#include <QtCore/QWriteLocker>
28
29
#include <phonon/streaminterface.h>
30
31
32
#define WAVEHEADER_OFFSET_FORMATTAG        20
33
#define WAVEHEADER_OFFSET_CHANNELS         22
34
#define WAVEHEADER_OFFSET_SAMPLESPERSEC    24
35
#define WAVEHEADER_OFFSET_AVGBYTESPERSEC   28
36
#define WAVEHEADER_OFFSET_BLOCKALIGN       32
37
#define WAVEHEADER_OFFSET_BITSPERSAMPLE    34
38
#define WAVEHEADER_OFFSET_DATA             44
39
#define WAVEHEADER_SIZE                    WAVEHEADER_OFFSET_DATA
40
41
QT_BEGIN_NAMESPACE
42
43
namespace Phonon
44
{
45
    namespace WaveOut
46
    {        
47
        static unsigned int buffer_size = (16 * 1024 * 4);
48
49
        QString getErrorText(MMRESULT error)
50
        {
51
            ushort b[256];
52
            waveOutGetErrorText(error, (LPWSTR)b, 256);
53
            return QString((const QChar *)b);
54
        }
55
56
        class WorkerThread : public QThread
57
        {
58
         Q_OBJECT
59
         public slots:
60
              void stream(QIODevice *file, QByteArray *buffer, bool *finished);
61
        };
62
63
        void WorkerThread::stream(QIODevice *ioStream, QByteArray *buffer, bool *finished)
64
        {
65
            (*finished) = false;
66
            memset((void*) buffer->data(), 0, buffer->size());
67
            qint64 i = ioStream->read(buffer->data(), buffer_size);
68
            buffer->resize(i);
69
            (*finished) = true;
70
        }
71
72
73
        void QT_WIN_CALLBACK MediaObject::WaveOutCallBack(HWAVEOUT m_hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
74
        {
75
            Q_UNUSED(m_hWaveOut);
76
            Q_UNUSED(dwInstance);
77
            Q_UNUSED(dwParam2);
78
79
            switch(uMsg)
80
            {
81
            case WOM_OPEN:
82
                break;
83
            case WOM_DONE:
84
                {
85
                    WAVEHDR *waveHeader = (WAVEHDR*)dwParam1;
86
                    MediaObject* mediaObject = reinterpret_cast<MediaObject *>(waveHeader->dwUser);
87
                    if (mediaObject) {
88
                        mediaObject->swapBuffers();
89
                    }
90
                }
91
                break;
92
            case WOM_CLOSE:
93
                break;
94
            }
95
        }
96
97
        class StreamReader : public Phonon::StreamInterface
98
        {
99
        public:
100
              StreamReader(QObject *parent, const Phonon::MediaSource &source) :
101
                  m_seekable(false), m_pos(0), m_size(-1)
102
              {
103
                  Q_UNUSED(parent);
104
                  connectToSource(source);
105
              }
106
107
              //for Phonon::StreamInterface
108
              void writeData(const QByteArray &data)
109
              {
110
                  QWriteLocker locker(&m_lock);
111
                  m_pos += data.size();
112
                  m_buffer += data;
113
              }
114
115
              void endOfData()
116
              {
117
              }
118
119
              void setStreamSize(qint64 newSize)
120
              {
121
                  QWriteLocker locker(&m_lock);
122
                  m_size = newSize;
123
              }
124
125
              qint64 streamSize() const
126
              {
127
                  QReadLocker locker(&m_lock);
128
                  return m_size;
129
              }
130
131
              void setStreamSeekable(bool s)
132
              {
133
                  QWriteLocker locker(&m_lock);
134
                  m_seekable = s;
135
              }
136
137
              bool streamSeekable() const
138
              {
139
                  QReadLocker locker(&m_lock);
140
                  return m_seekable;
141
              }
142
143
              void setCurrentPos(qint64 pos)
144
              {
145
                  QWriteLocker locker(&m_lock);
146
                  m_pos = pos;
147
                  seekStream(pos);
148
                  m_buffer.clear();
149
              }
150
151
              qint64 currentPos() const
152
              {
153
                  QReadLocker locker(&m_lock);
154
                  return m_pos;
155
              }
156
157
              int currentBufferSize() const
158
              {
159
                  QReadLocker locker(&m_lock);
160
                  return m_buffer.size();
161
              }
162
163
            //for Phonon::StreamInterface
164
            QByteArray m_buffer;
165
            bool m_seekable;
166
            qint64 m_pos;
167
            qint64 m_size;
168
            mutable QReadWriteLock m_lock;
169
        };
170
171
        class IOWrapper : public QIODevice {
172
        public:
173
            IOWrapper(QObject *parent, const Phonon::MediaSource &source) : m_streamReader(this, source)
174
            {
175
                Q_UNUSED(parent);
176
                setOpenMode(QIODevice::ReadOnly);
177
            }
178
            bool seek(qint64 pos);
179
            qint64 size() const;
180
            qint64 pos();
181
            bool isReadable() const;
182
         protected:
183
            qint64 readData (char * data, qint64 maxSize);
184
            qint64 writeData(const char *,qint64);
185
        private:
186
            StreamReader m_streamReader;
187
        };
188
189
        bool IOWrapper::isReadable () const
190
        {
191
            return true;
192
        }
193
194
        qint64 IOWrapper::pos()
195
        {
196
            return (m_streamReader.streamSeekable() ? m_streamReader.currentPos() : 0);
197
        }
198
199
        bool IOWrapper::seek( qint64 pos)
200
        {
201
            if (!m_streamReader.streamSeekable())
202
                return false;
203
            m_streamReader.setCurrentPos(pos);
204
            return true;
205
        }
206
207
        qint64 IOWrapper::size() const
208
        {
209
            return m_streamReader.streamSize();
210
        }
211
212
        qint64 IOWrapper::readData(char * data, qint64 maxSize)
213
        {
214
            int oldSize = m_streamReader.currentBufferSize();
215
            while (m_streamReader.currentBufferSize() < maxSize) {
216
                m_streamReader.needData();
217
                if (oldSize == m_streamReader.currentBufferSize()) {
218
                    break; //we didn't get any data
219
                }
220
                oldSize = m_streamReader.currentBufferSize();
221
            }
222
223
            qint64 bytesRead = qMin(qint64(m_streamReader.currentBufferSize()), maxSize);
224
            {
225
                QWriteLocker locker(&m_streamReader.m_lock);
226
                qMemCopy(data, m_streamReader.m_buffer.data(), bytesRead);
227
                //truncate the buffer
228
                m_streamReader.m_buffer = m_streamReader.m_buffer.mid(bytesRead);
229
            }
230
            return bytesRead;
231
        }
232
233
        qint64 IOWrapper::writeData(const char *,qint64)
234
        {
235
            return 0;
236
        }
237
238
        MediaObject::MediaObject(QObject *parent) : m_file(0), m_stream(0),
239
                                                    m_hWaveOut(0), m_nextBufferIndex(1), 
240
                                                    m_mediaSize(-1), m_bufferingFinished(0),
241
                                                    m_paused(0), m_tickInterval(0),
242
                                                    m_hasNextSource(0), m_hasSource(0),
243
                                                    m_sourceIsValid(0), m_errorType(Phonon::NoError),
244
                                                    m_currentTime(0), m_transitionTime(0),
245
                                                    m_tick(0), m_volume(100), m_prefinishMark(0),
246
                                                    m_tickIntervalResolution(0), m_bufferPrepared(0),
247
                                                    m_stopped(0)
248
        {
249
            m_thread = new WorkerThread();
250
            connect(this, SIGNAL(outOfData(QIODevice*,QByteArray*,bool*)), m_thread, SLOT(stream(QIODevice*,QByteArray*,bool*)));
251
            m_thread->start();
252
            m_soundBuffer1.waveHeader = new WAVEHDR;
253
            m_soundBuffer2.waveHeader = new WAVEHDR;
254
            setParent(parent);
255
            setState(Phonon::LoadingState);            
256
        }
257
258
        MediaObject::~MediaObject()
259
        {
260
            stop();
261
            disconnect(this, SIGNAL(outOfData(QIODevice*,QByteArray*,bool*)), m_thread, SLOT(stream(QIODevice*,QByteArray*,bool*)));
262
            do { //The event loop of m_thread might not be started, yet
263
                m_thread->quit(); //If the event loop is not started yet quit() does nothing
264
                m_thread->wait(100);
265
            } while (m_thread->isRunning());
266
            delete m_thread;
267
            deleteValidWaveOutDevice();
268
            delete m_soundBuffer1.waveHeader;
269
            delete m_soundBuffer2.waveHeader;
270
        }
271
272
        Phonon::State MediaObject::state() const
273
        {
274
           return m_state;
275
        }
276
277
        bool MediaObject::hasVideo() const
278
        {
279
            return false;
280
        }
281
282
        bool MediaObject::isSeekable() const
283
        {
284
            if (!m_stream) 
285
              return false; 
286
            return !m_stream->isSequential();
287
        }
288
289
        qint64 MediaObject::totalTime() const
290
        {
291
            return m_totalTime;
292
        }
293
294
        qint64 MediaObject::currentTime() const
295
        {
296
            //this handles inaccuracy when stopping on a title
297
            return m_currentTime;
298
        }
299
300
        qint32 MediaObject::tickInterval() const
301
        {
302
            return m_tickInterval * m_tickIntervalResolution;
303
        }
304
305
        void MediaObject::setTickInterval(qint32 newTickInterval)
306
        {
307
            if ((m_tickIntervalResolution == 0) || (newTickInterval == 0))
308
                return;
309
            m_tickInterval = newTickInterval / m_tickIntervalResolution;
310
            if ((newTickInterval > 0) && (m_tickInterval == 0))
311
                m_tickInterval = 1;
312
        }
313
314
        void MediaObject::pause()
315
        {
316
            if (!m_paused) {
317
                m_paused = true;
318
                setState(Phonon::PausedState);
319
                if (!(waveOutPause(m_hWaveOut) == MMSYSERR_NOERROR))
320
                {
321
                    setError(Phonon::NormalError, QLatin1String("cannot pause (system error)"));
322
                }
323
            }
324
        }
325
326
        void MediaObject::stop()
327
        {
328
            setState(Phonon::StoppedState);
329
            m_stopped = true;
330
            m_paused = false;
331
            seek(0);
332
            if (!(waveOutReset(m_hWaveOut) == MMSYSERR_NOERROR))
333
                setError(Phonon::NormalError, QLatin1String("cannot stop (system error)"));
334
        }
335
336
        void MediaObject::play()
337
        {
338
            if ((m_state == Phonon::PlayingState) && !m_paused && !m_stopped)
339
                return;
340
            if  ((m_state == Phonon::LoadingState) ||
341
                 (m_state == Phonon::BufferingState) ||
342
                 (m_state == Phonon::ErrorState)) {
343
                    setError(Phonon::FatalError, QLatin1String("illegale state for playback"));
344
                    return;
345
            }
346
347
            if (m_state == Phonon::StoppedState)
348
                stop();
349
            if (m_sourceIsValid) {
350
                setState(Phonon::PlayingState);
351
                if (!m_paused) {
352
                    m_nextBufferIndex = true;
353
                    m_stopped = false;
354
                    playBuffer(m_soundBuffer1.waveHeader);
355
                    playBuffer(m_soundBuffer2.waveHeader);
356
                } else {
357
                    if (!(waveOutRestart(m_hWaveOut) == MMSYSERR_NOERROR))
358
                        setError(Phonon::NormalError, QLatin1String("cannot resume (system)"));
359
                }
360
            } else {
361
                setError(Phonon::FatalError, QLatin1String("cannot playback invalid source"));
362
            }
363
            m_paused = false;
364
        }
365
366
        QString MediaObject::errorString() const
367
        {
368
            
369
            return m_errorString;
370
        }
371
372
        Phonon::ErrorType MediaObject::errorType() const
373
        {
374
            return Phonon::ErrorType();
375
        }
376
377
        qint32 MediaObject::prefinishMark() const
378
        {
379
            return m_prefinishMark;
380
        }
381
382
        void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
383
        {
384
            m_prefinishMark = newPrefinishMark;
385
        }
386
387
        qint32 MediaObject::transitionTime() const
388
        {
389
            return m_transitionTime;
390
        }
391
392
        void MediaObject::setTransitionTime(qint32 time)
393
        {
394
           m_transitionTime = time;
395
        }
396
397
        qint64 MediaObject::remainingTime() const
398
        {
399
            return m_totalTime - m_currentTime;
400
        }
401
402
        Phonon::MediaSource MediaObject::source() const
403
        {
404
            return Phonon::MediaSource();
405
        }
406
407
        void MediaObject::setNextSource(const Phonon::MediaSource &source)
408
        {
409
            m_nextSource = source;
410
            m_hasNextSource = true;
411
        }
412
413
        void MediaObject::setSource(const Phonon::MediaSource &source)
414
        {
415
            if (m_state == Phonon::PlayingState)
416
            {
417
                setError(Phonon::NormalError, QLatin1String("source changed while playing"));
418
                stop();
419
            }
420
421
            m_source = source;
422
            m_hasSource = true;
423
            m_sourceIsValid = false;
424
425
            emit currentSourceChanged(source);
426
427
            if (source.type() == Phonon::MediaSource::LocalFile) {
428
                if (!openWaveFile(source.fileName())) {
429
                  setError(Phonon::FatalError, QLatin1String("cannot open media file"));
430
                  return ;
431
                }
432
            } else if (source.type() == Phonon::MediaSource::Stream) {
433
                if (m_stream)
434
                   delete m_stream;
435
                m_stream = new IOWrapper(this, source);
436
                m_mediaSize = m_stream->size();
437
            } else if (source.type() == Phonon::MediaSource::Url) {
438
                if (!openWaveFile(source.url().toLocalFile())) {
439
                    setError(Phonon::FatalError, QLatin1String("cannot open media file"));
440
                    return ;
441
                }
442
            } else {
443
                setError(Phonon::FatalError, QLatin1String("type of source not supported"));
444
                return ;
445
            }
446
            setState(Phonon::LoadingState);
447
448
            if (!readHeader())
449
                setError(Phonon::FatalError, QLatin1String("invalid header"));
450
            else if (!getWaveOutDevice())
451
                setError(Phonon::FatalError, QLatin1String("No waveOut device available"));
452
            else if (!fillBuffers())
453
                setError(Phonon::FatalError, QLatin1String("no data for buffering"));
454
            else if (!prepareBuffers())
455
                setError(Phonon::FatalError, QLatin1String("cannot prepare buffers"));
456
            else
457
                m_sourceIsValid = true;
458
459
            if (m_sourceIsValid)
460
                setState(Phonon::StoppedState);
461
        }
462
463
        void MediaObject::seek(qint64 time)
464
        {
465
            if (!m_sourceIsValid) {
466
                setError(Phonon::NormalError, QLatin1String("source is not valid"));
467
                return;
468
            }
469
            if ((time >= 0) && (time < m_totalTime)) {
470
                int counter = 0;
471
                while (!m_bufferingFinished && (counter < 200)) {
472
                  Sleep(20);
473
                  counter ++;
474
                }
475
                if (counter >= 200) {
476
                   setError(Phonon::NormalError, QLatin1String("buffering timed out"));
477
                   return;
478
                }
479
480
                m_stream->seek(WAVEHEADER_SIZE + time * m_waveFormatEx.nSamplesPerSec * m_waveFormatEx.wBitsPerSample * m_waveFormatEx.nChannels / 8 / 1000);
481
                m_currentTime = time;
482
                if (m_state == Phonon::PlayingState)
483
                  play();
484
            } else {
485
                setError(Phonon::NormalError, QLatin1String("seeking out of range"));
486
            }
487
        }
488
489
        void MediaObject::unPrepareBuffers()
490
        {
491
            if (m_bufferPrepared) {
492
            DWORD err1 = waveOutUnprepareHeader(m_hWaveOut, m_soundBuffer1.waveHeader, sizeof(WAVEHDR));
493
            DWORD err2 = waveOutUnprepareHeader(m_hWaveOut, m_soundBuffer2.waveHeader, sizeof(WAVEHDR));
494
            if (!(err1 == MMSYSERR_NOERROR) || !(err2 == MMSYSERR_NOERROR))
495
                setError(Phonon::NormalError, QLatin1String("cannot unprepare buffer") + getErrorText(err1) + getErrorText(err2));
496
            }
497
            m_bufferPrepared = false;
498
        }
499
500
        bool MediaObject::prepareBuffers()
501
        {
502
            memset((void*)m_soundBuffer1.waveHeader, 0, sizeof(WAVEHDR));
503
            m_soundBuffer1.waveHeader->lpData = m_soundBuffer1.data.data();
504
            m_soundBuffer1.waveHeader->dwBufferLength = m_soundBuffer1.data.size();
505
            m_soundBuffer1.waveHeader->dwUser = (DWORD_PTR) this;
506
507
            ZeroMemory((void*)m_soundBuffer2.waveHeader, sizeof(WAVEHDR));
508
            m_soundBuffer2.waveHeader->lpData = m_soundBuffer2.data.data();
509
            m_soundBuffer2.waveHeader->dwBufferLength = m_soundBuffer1.data.size();
510
            m_soundBuffer2.waveHeader->dwUser = (DWORD_PTR) this;
511
512
            m_bufferPrepared = (waveOutPrepareHeader(m_hWaveOut, m_soundBuffer1.waveHeader, sizeof(WAVEHDR)) == MMSYSERR_NOERROR)
513
                && (waveOutPrepareHeader(m_hWaveOut, m_soundBuffer2.waveHeader, sizeof(WAVEHDR)) == MMSYSERR_NOERROR);
514
             return m_bufferPrepared;
515
        }
516
517
        void MediaObject::deleteValidWaveOutDevice()
518
        {
519
            if (m_hWaveOut) {
520
                unPrepareBuffers();
521
                if (!(waveOutClose(m_hWaveOut)  == MMSYSERR_NOERROR))
522
                    setError(Phonon::NormalError, QLatin1String("cannot close wave device"));
523
            }
524
        }
525
526
        bool MediaObject::getWaveOutDevice()
527
        {
528
            deleteValidWaveOutDevice();
529
530
            for(UINT deviceId = 0; deviceId < waveOutGetNumDevs(); deviceId++)
531
            {
532
                if(deviceId == waveOutGetNumDevs())
533
                    return false;
534
                if(waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &m_waveFormatEx, (DWORD)WaveOutCallBack, 0, CALLBACK_FUNCTION) == MMSYSERR_NOERROR)
535
                    return m_hWaveOut; //m_hWaveOut !=0;
536
            }
537
            return false;
538
        }
539
540
        bool  MediaObject::openWaveFile(QString fileName)
541
        {
542
            if (m_file)
543
                delete m_file;
544
            m_file = new QFile(fileName);
545
            m_file->setParent(this);
546
            m_stream = m_file;
547
            m_mediaSize = m_file->size();
548
            return (m_file->open(QIODevice::ReadOnly));
549
        }
550
551
        bool MediaObject::readHeader()
552
        {
553
            QByteArray header = m_stream->read(WAVEHEADER_SIZE);
554
555
            if (header.size() == WAVEHEADER_SIZE) {
556
557
                m_waveFormatEx.wFormatTag         = *((WORD* )(header.data() + WAVEHEADER_OFFSET_FORMATTAG     ));
558
                m_waveFormatEx.nChannels          = *((WORD* )(header.data() + WAVEHEADER_OFFSET_CHANNELS      ));
559
                m_waveFormatEx.nSamplesPerSec     = *((DWORD*)(header.data() + WAVEHEADER_OFFSET_SAMPLESPERSEC ));
560
                m_waveFormatEx.nAvgBytesPerSec    = *((DWORD*)(header.data() + WAVEHEADER_OFFSET_AVGBYTESPERSEC));
561
                m_waveFormatEx.nBlockAlign        = *((WORD* )(header.data() + WAVEHEADER_OFFSET_BLOCKALIGN    ));
562
                m_waveFormatEx.wBitsPerSample     = *((WORD* )(header.data() + WAVEHEADER_OFFSET_BITSPERSAMPLE ));
563
564
                m_tickIntervalResolution = (qint64(buffer_size) * 8 * 1000) / m_waveFormatEx.nSamplesPerSec / m_waveFormatEx.wBitsPerSample / m_waveFormatEx.nChannels;
565
                if (m_mediaSize > 0)
566
                   m_totalTime = ((m_mediaSize - WAVEHEADER_SIZE) * 8 * 1000) / m_waveFormatEx.nSamplesPerSec / m_waveFormatEx.wBitsPerSample / m_waveFormatEx.nChannels;
567
                else
568
                  m_totalTime = -1;
569
                  emit totalTimeChanged(m_totalTime);
570
                return true;
571
            } else {
572
                return false;
573
            }
574
        }
575
        
576
        bool MediaObject::fillBuffers()
577
        {
578
            
579
            m_soundBuffer1.data = m_stream->read(buffer_size);
580
            m_soundBuffer2.data = m_stream->read(buffer_size);
581
582
            m_bufferingFinished = true;
583
584
            if (!(m_soundBuffer1.data.size() > 0))
585
                setError(Phonon::NormalError, QLatin1String("cannot read source"));
586
            return true;
587
        }
588
589
        void MediaObject::setState(Phonon::State newState)
590
        {
591
            if (m_state == newState)
592
                return;
593
            emit stateChanged(newState, m_state);
594
            m_state = newState;
595
        }
596
597
        void MediaObject::setError(ErrorType errorType, QString errorMessage)
598
        {
599
            m_errorType = errorType;
600
            setState(Phonon::ErrorState);
601
            m_errorString = errorMessage;
602
        }
603
604
        void MediaObject::setAudioOutput(QObject *audioOutput)
605
        {
606
            m_audioOutput = qobject_cast<AudioOutput*>(audioOutput);
607
608
            if (m_audioOutput) {
609
                m_volume = m_audioOutput->volume();
610
                connect(m_audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(setVolume(qreal)));
611
            }
612
        }
613
614
        void MediaObject::setVolume(qreal newVolume)
615
        {
616
            m_volume = newVolume;
617
        }
618
619
        void MediaObject::swapBuffers()
620
        {
621
            if (m_stopped || m_paused)
622
                return;
623
624
            m_currentTime += m_tickIntervalResolution;
625
            if (m_tickInterval) {
626
                m_tick ++;
627
                if (m_tick > (m_tickInterval - 1)) {
628
                    emit tick(m_currentTime);
629
                    m_tick = 0;
630
                }
631
            }
632
            if ((m_prefinishMark > 0)&& (m_prefinishMark < m_currentTime))
633
                emit prefinishMarkReached(m_totalTime - m_currentTime);
634
635
            while (!m_bufferingFinished) {
636
                setState(Phonon::BufferingState);
637
                qWarning() << QLatin1String("buffer underun");
638
                Sleep(20);
639
            }
640
641
            setState(Phonon::PlayingState);
642
643
            //if size == o then stop...
644
            if (m_nextBufferIndex) {
645
                int size = m_soundBuffer1.waveHeader->dwBufferLength = m_soundBuffer1.data.size();
646
                if (size == buffer_size) {
647
                    playBuffer(m_soundBuffer1.waveHeader);
648
                    emit outOfData(m_stream, &m_soundBuffer1.data, &m_bufferingFinished);
649
                } else {
650
                    playBuffer(m_soundBuffer1.waveHeader);
651
                    m_stopped = true;
652
                    setState(Phonon::StoppedState);
653
                    emit finished();
654
                    seek(0);
655
                }
656
            } else {
657
                int size = m_soundBuffer2.waveHeader->dwBufferLength = m_soundBuffer2.data.size();
658
                if (size == buffer_size) {
659
                    playBuffer(m_soundBuffer2.waveHeader);
660
                    emit outOfData(m_stream, &m_soundBuffer2.data, &m_bufferingFinished);
661
                } else {
662
                    playBuffer(m_soundBuffer2.waveHeader);
663
                    m_stopped = true;
664
                    setState(Phonon::StoppedState);
665
                    emit finished();
666
                    seek(0);
667
                }
668
            }
669
            m_nextBufferIndex =! m_nextBufferIndex;
670
        }
671
672
673
        void MediaObject::playBuffer(WAVEHDR *waveHeader)
674
        {
675
            DWORD err = waveOutWrite(m_hWaveOut, waveHeader, sizeof(WAVEHDR));
676
            if (!err == MMSYSERR_NOERROR) {
677
                setError(Phonon::FatalError, QLatin1String("cannot play sound buffer (system) ") + getErrorText(err));
678
                m_stopped = true;
679
            }
680
        }
681
    }
682
}
683
684
QT_END_NAMESPACE
685
686
#include "mediaobject.moc"