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
19
#include "audiooutput.h"
20
#include "audioplayer.h"
21
#include "defs.h"
22
#include "dummyplayer.h"
23
#include "utils.h"
24
#include "utils.h"
25
26
#ifdef PHONON_MMF_VIDEO_SURFACES
27
#include "videoplayer_surface.h"
28
#else
29
#include "videoplayer_dsa.h"
30
#endif
31
32
#include "videowidget.h"
33
34
#include "mediaobject.h"
35
36
#include <QDir>
37
#include <QResource>
38
#include <QUrl>
39
#include <cdbcols.h>
40
#include <cdblen.h>
41
#include <commdb.h>
42
#include <mmf/common/mmfcontrollerframeworkbase.h>
43
44
QT_BEGIN_NAMESPACE
45
46
using namespace Phonon;
47
using namespace Phonon::MMF;
48
49
/*! \class MMF::MediaObject
50
  \internal
51
*/
52
53
//-----------------------------------------------------------------------------
54
// Constructor / destructor
55
//-----------------------------------------------------------------------------
56
57
MMF::MediaObject::MediaObject(QObject *parent) : MMF::MediaNode::MediaNode(parent)
58
                                               , m_recognizerOpened(false)
59
                                               , m_nextSourceSet(false)
60
                                               , m_file(0)
61
                                               , m_resource(0)
62
{
63
    m_player.reset(new DummyPlayer());
64
65
    TRACE_CONTEXT(MediaObject::MediaObject, EAudioApi);
66
    TRACE_ENTRY_0();
67
68
    const int err = m_fileServer.Connect();
69
    QT_TRAP_THROWING(User::LeaveIfError(err));
70
71
    parent->installEventFilter(this);
72
    m_iap = KUseDefaultIap;
73
74
    TRACE_EXIT_0();
75
}
76
77
MMF::MediaObject::~MediaObject()
78
{
79
    TRACE_CONTEXT(MediaObject::~MediaObject, EAudioApi);
80
    TRACE_ENTRY_0();
81
82
    parent()->removeEventFilter(this);
83
    delete m_resource;
84
85
    if (m_file)
86
        m_file->Close();
87
    delete m_file;
88
89
    m_fileServer.Close();
90
    m_recognizer.Close();
91
92
    TRACE_EXIT_0();
93
}
94
95
96
//-----------------------------------------------------------------------------
97
// Recognizer
98
//-----------------------------------------------------------------------------
99
100
bool MMF::MediaObject::openRecognizer()
101
{
102
    TRACE_CONTEXT(MediaObject::openRecognizer, EAudioInternal);
103
104
    if (!m_recognizerOpened) {
105
        TInt err = m_recognizer.Connect();
106
        if (KErrNone != err) {
107
            TRACE("RApaLsSession::Connect error %d", err);
108
            return false;
109
        }
110
111
        // This must be called in order to be able to share file handles with
112
        // the recognizer server (see fileMediaType function).
113
        err = m_fileServer.ShareProtected();
114
        if (KErrNone != err) {
115
            TRACE("RFs::ShareProtected error %d", err);
116
            return false;
117
        }
118
119
        m_recognizerOpened = true;
120
    }
121
122
    return true;
123
}
124
125
MMF::MediaType MMF::MediaObject::fileMediaType
126
(const QString& fileName)
127
{
128
    TRACE_CONTEXT(MediaObject::fileMediaType, EAudioInternal);
129
130
    MediaType result = MediaTypeUnknown;
131
132
    if (openRecognizer()) {
133
        TInt err = openFileHandle(fileName);
134
        const QHBufC nativeFileName(QDir::toNativeSeparators(fileName));
135
        if (KErrNone == err) {
136
            TDataRecognitionResult recognizerResult;
137
            err = m_recognizer.RecognizeData(*m_file, recognizerResult);
138
            if (KErrNone == err) {
139
                const TPtrC mimeType = recognizerResult.iDataType.Des();
140
                result = Utils::mimeTypeToMediaType(mimeType);
141
            } else {
142
                TRACE("RApaLsSession::RecognizeData filename %S error %d", nativeFileName.data(), err);
143
            }
144
        } else {
145
            TRACE("RFile::Open filename %S error %d", nativeFileName.data(), err);
146
        }
147
    }
148
149
    return result;
150
}
151
152
int MMF::MediaObject::openFileHandle(const QString &fileName)
153
{
154
    TRACE_CONTEXT(MediaObject::openFileHandle, EAudioInternal);
155
    const QHBufC nativeFileName(QDir::toNativeSeparators(fileName));
156
    TRACE_ENTRY("filename %S", nativeFileName.data());
157
    if (m_file)
158
        m_file->Close();
159
    delete m_file;
160
    m_file = 0;
161
    m_file = new RFile;
162
    TInt err = m_file->Open(m_fileServer, *nativeFileName, EFileRead | EFileShareReadersOrWriters);
163
    return err;
164
}
165
166
MMF::MediaType MMF::MediaObject::bufferMediaType(const uchar *data, qint64 size)
167
{
168
    TRACE_CONTEXT(MediaObject::bufferMediaType, EAudioInternal);
169
    MediaType result = MediaTypeUnknown;
170
    if (openRecognizer()) {
171
        TDataRecognitionResult recognizerResult;
172
        const TPtrC8 des(data, size);
173
        const TInt err = m_recognizer.RecognizeData(KNullDesC, des, recognizerResult);
174
        if (KErrNone == err) {
175
            const TPtrC mimeType = recognizerResult.iDataType.Des();
176
            result = Utils::mimeTypeToMediaType(mimeType);
177
        } else {
178
            TRACE("RApaLsSession::RecognizeData error %d", err);
179
        }
180
    }
181
    return result;
182
}
183
184
//-----------------------------------------------------------------------------
185
// MediaObjectInterface
186
//-----------------------------------------------------------------------------
187
188
void MMF::MediaObject::play()
189
{
190
    m_player->play();
191
}
192
193
void MMF::MediaObject::pause()
194
{
195
    m_player->pause();
196
}
197
198
void MMF::MediaObject::stop()
199
{
200
    m_player->stop();
201
}
202
203
void MMF::MediaObject::seek(qint64 ms)
204
{
205
    m_player->seek(ms);
206
207
    if (state() == PausedState or state() == PlayingState) {
208
        emit tick(currentTime());
209
    }
210
}
211
212
qint32 MMF::MediaObject::tickInterval() const
213
{
214
    return m_player->tickInterval();
215
}
216
217
void MMF::MediaObject::setTickInterval(qint32 interval)
218
{
219
    m_player->setTickInterval(interval);
220
}
221
222
bool MMF::MediaObject::hasVideo() const
223
{
224
    return m_player->hasVideo();
225
}
226
227
bool MMF::MediaObject::isSeekable() const
228
{
229
    return m_player->isSeekable();
230
}
231
232
Phonon::State MMF::MediaObject::state() const
233
{
234
    return m_player->state();
235
}
236
237
qint64 MMF::MediaObject::currentTime() const
238
{
239
    return m_player->currentTime();
240
}
241
242
QString MMF::MediaObject::errorString() const
243
{
244
    return m_player->errorString();
245
}
246
247
Phonon::ErrorType MMF::MediaObject::errorType() const
248
{
249
    return m_player->errorType();
250
}
251
252
qint64 MMF::MediaObject::totalTime() const
253
{
254
    return m_player->totalTime();
255
}
256
257
MediaSource MMF::MediaObject::source() const
258
{
259
    return m_source;
260
}
261
262
void MMF::MediaObject::setSource(const MediaSource &source)
263
{
264
    switchToSource(source);
265
}
266
267
void MMF::MediaObject::switchToSource(const MediaSource &source)
268
{
269
    if (m_file)
270
        m_file->Close();
271
    delete m_file;
272
    m_file = 0;
273
274
    delete m_resource;
275
    m_resource = 0;
276
277
    createPlayer(source);
278
    m_source = source;
279
    m_player->open();
280
    emit currentSourceChanged(m_source);
281
}
282
283
void MMF::MediaObject::createPlayer(const MediaSource &source)
284
{
285
    TRACE_CONTEXT(MediaObject::createPlayer, EAudioApi);
286
    TRACE_ENTRY("state %d source.type %d", state(), source.type());
287
    TRACE_ENTRY("source.type %d", source.type());
288
289
    MediaType mediaType = MediaTypeUnknown;
290
291
    AbstractPlayer* oldPlayer = m_player.data();
292
293
    const bool oldPlayerHasVideo = oldPlayer->hasVideo();
294
    const bool oldPlayerSeekable = oldPlayer->isSeekable();
295
296
    QString errorMessage;
297
298
    // Determine media type
299
    switch (source.type()) {
300
    case MediaSource::LocalFile:
301
        mediaType = fileMediaType(source.fileName());
302
        break;
303
304
    case MediaSource::Url:
305
        {
306
            const QUrl url(source.url());
307
            if (url.scheme() == QLatin1String("file")) {
308
                mediaType = fileMediaType(url.toLocalFile());
309
            }
310
            else {
311
                // Streaming playback is generally not supported by the implementation
312
                // of the audio player API, so we use CVideoPlayerUtility for both
313
                // audio and video streaming.
314
                mediaType = MediaTypeVideo;
315
            }
316
        }
317
        break;
318
319
    case MediaSource::Invalid:
320
    case MediaSource::Disc:
321
        errorMessage = tr("Error opening source: type not supported");
322
        break;
323
324
    case MediaSource::Stream:
325
        {
326
            const QString fileName = source.url().toLocalFile();
327
            if (fileName.startsWith(QLatin1String(":/")) || fileName.startsWith(QLatin1String("qrc://"))) {
328
                Q_ASSERT(!m_resource);
329
                m_resource = new QResource(fileName);
330
                if (m_resource->isValid()) {
331
                    if (m_resource->isCompressed())
332
                        errorMessage = tr("Error opening source: resource is compressed");
333
                    else
334
		        mediaType = bufferMediaType(m_resource->data(), m_resource->size());
335
		} else {
336
                    errorMessage = tr("Error opening source: resource not valid");
337
                }
338
            } else {
339
                errorMessage = tr("Error opening source: type not supported");
340
            }
341
        }
342
        break;
343
344
    case MediaSource::Empty:
345
        TRACE_0("Empty media source");
346
        break;
347
    }
348
349
    if (oldPlayer)
350
        oldPlayer->close();
351
352
    AbstractPlayer* newPlayer = 0;
353
354
    // Construct newPlayer using oldPlayer (if not 0) in order to copy
355
    // parameters (volume, prefinishMark, transitionTime) which may have
356
    // been set on oldPlayer.
357
358
    switch (mediaType) {
359
    case MediaTypeUnknown:
360
        TRACE_0("Media type could not be determined");
361
        newPlayer = new DummyPlayer(oldPlayer);
362
        errorMessage = tr("Error opening source: media type could not be determined");
363
        break;
364
365
    case MediaTypeAudio:
366
        newPlayer = new AudioPlayer(this, oldPlayer);
367
        break;
368
369
    case MediaTypeVideo:
370
#ifdef PHONON_MMF_VIDEO_SURFACES
371
        newPlayer = SurfaceVideoPlayer::create(this, oldPlayer);
372
#else
373
        newPlayer = DsaVideoPlayer::create(this, oldPlayer);
374
#endif
375
        break;
376
    }
377
378
    if (oldPlayer)
379
        emit abstractPlayerChanged(0);
380
    m_player.reset(newPlayer);
381
    emit abstractPlayerChanged(newPlayer);
382
383
    if (oldPlayerHasVideo != hasVideo()) {
384
        emit hasVideoChanged(hasVideo());
385
    }
386
387
    if (oldPlayerSeekable != isSeekable()) {
388
        emit seekableChanged(isSeekable());
389
    }
390
391
    connect(m_player.data(), SIGNAL(totalTimeChanged(qint64)), SIGNAL(totalTimeChanged(qint64)));
392
    connect(m_player.data(), SIGNAL(stateChanged(Phonon::State,Phonon::State)), SIGNAL(stateChanged(Phonon::State,Phonon::State)));
393
    connect(m_player.data(), SIGNAL(finished()), SIGNAL(finished()));
394
    connect(m_player.data(), SIGNAL(bufferStatus(int)), SIGNAL(bufferStatus(int)));
395
    connect(m_player.data(), SIGNAL(metaDataChanged(QMultiMap<QString,QString>)), SIGNAL(metaDataChanged(QMultiMap<QString,QString>)));
396
    connect(m_player.data(), SIGNAL(aboutToFinish()), SIGNAL(aboutToFinish()));
397
    connect(m_player.data(), SIGNAL(prefinishMarkReached(qint32)), SIGNAL(prefinishMarkReached(qint32)));
398
    connect(m_player.data(), SIGNAL(prefinishMarkReached(qint32)), SLOT(handlePrefinishMarkReached(qint32)));
399
    connect(m_player.data(), SIGNAL(tick(qint64)), SIGNAL(tick(qint64)));
400
401
    // We need to call setError() after doing the connects, otherwise the
402
    // error won't be received.
403
    if (!errorMessage.isEmpty()) {
404
        Q_ASSERT(m_player);
405
        m_player->setError(errorMessage);
406
    }
407
408
    TRACE_EXIT_0();
409
}
410
411
void MMF::MediaObject::setNextSource(const MediaSource &source)
412
{
413
    m_nextSource = source;
414
    m_nextSourceSet = true;
415
}
416
417
qint32 MMF::MediaObject::prefinishMark() const
418
{
419
    return m_player->prefinishMark();
420
}
421
422
void MMF::MediaObject::setPrefinishMark(qint32 mark)
423
{
424
    m_player->setPrefinishMark(mark);
425
}
426
427
qint32 MMF::MediaObject::transitionTime() const
428
{
429
    return m_player->transitionTime();
430
}
431
432
void MMF::MediaObject::setTransitionTime(qint32 time)
433
{
434
    m_player->setTransitionTime(time);
435
}
436
437
void MMF::MediaObject::volumeChanged(qreal volume)
438
{
439
    m_player->volumeChanged(volume);
440
}
441
442
RFile* MMF::MediaObject::file() const
443
{
444
    return m_file;
445
}
446
447
QResource* MMF::MediaObject::resource() const
448
{
449
    return m_resource;
450
}
451
452
//-----------------------------------------------------------------------------
453
// MediaNode
454
//-----------------------------------------------------------------------------
455
456
void MMF::MediaObject::connectMediaObject(MediaObject * /*mediaObject*/)
457
{
458
    // This function should never be called - see MediaNode::setMediaObject()
459
    Q_ASSERT_X(false, Q_FUNC_INFO,
460
        "Connection of MediaObject to MediaObject");
461
}
462
463
void MMF::MediaObject::disconnectMediaObject(MediaObject * /*mediaObject*/)
464
{
465
    // This function should never be called - see MediaNode::setMediaObject()
466
    Q_ASSERT_X(false, Q_FUNC_INFO,
467
        "Disconnection of MediaObject from MediaObject");
468
}
469
470
471
//-----------------------------------------------------------------------------
472
// Video output
473
//-----------------------------------------------------------------------------
474
475
void MMF::MediaObject::setVideoOutput(AbstractVideoOutput* videoOutput)
476
{
477
    m_player->setVideoOutput(videoOutput);
478
}
479
480
481
AbstractPlayer *MMF::MediaObject::abstractPlayer() const
482
{
483
    return m_player.data();
484
}
485
486
//-----------------------------------------------------------------------------
487
// Playlist support
488
//-----------------------------------------------------------------------------
489
490
void MMF::MediaObject::switchToNextSource()
491
{
492
    if (m_nextSourceSet) {
493
        m_nextSourceSet = false;
494
        switchToSource(m_nextSource);
495
        play();
496
    } else {
497
        emit finished();
498
    }
499
}
500
501
//-----------------------------------------------------------------------------
502
// IAP support
503
//-----------------------------------------------------------------------------
504
505
int MMF::MediaObject::currentIAP() const
506
{
507
    return m_iap;
508
}
509
510
bool MMF::MediaObject::eventFilter(QObject *watched, QEvent *event)
511
{
512
    if (event->type() == QEvent::DynamicPropertyChange ) {
513
        QDynamicPropertyChangeEvent* dynamicEvent = static_cast<QDynamicPropertyChangeEvent*>(event);
514
        if (dynamicEvent->propertyName() == "InternetAccessPointName") {
515
            QVariant value = watched->property("InternetAccessPointName");
516
            if (value.isValid()) {
517
                QString iapName = value.toString();
518
                TRAPD(err, setIAPIdFromNameL(iapName));
519
                if (err)
520
                    m_player->setError(tr("Failed to set requested IAP"), err);
521
            }
522
        }
523
    }
524
    return false;
525
}
526
527
void MMF::MediaObject::setIAPIdFromNameL(const QString& iapString)
528
{
529
    TRACE_CONTEXT(MediaObject::getIapIdFromName, EVideoInternal);
530
    TBuf<KCommsDbSvrMaxColumnNameLength> iapDes = qt_QString2TPtrC(iapString);
531
    CCommsDatabase *commsDb = CCommsDatabase::NewL(EDatabaseTypeIAP);
532
    CleanupStack::PushL(commsDb);
533
    commsDb->ShowHiddenRecords();
534
    CCommsDbTableView *view = commsDb->OpenTableLC(TPtrC(IAP));
535
    for (TInt l = view->GotoFirstRecord(); l != KErrNotFound; l = view->GotoNextRecord()) {
536
        TBuf<KCommsDbSvrMaxColumnNameLength> iapName;
537
        view->ReadTextL(TPtrC(COMMDB_NAME), iapName);
538
        TRACE("found IAP %S", &iapName);
539
        if (iapName.CompareF(iapDes) == 0) {
540
            TUint32 uiap;
541
            view->ReadUintL(TPtrC(COMMDB_ID), uiap);
542
            TRACE("matched IAP %S, setting m_iap %d", &iapName, uiap);
543
            m_iap = uiap;
544
            break;
545
        }
546
    }
547
    CleanupStack::PopAndDestroy(2); // commsDb, view
548
}
549
550
//-----------------------------------------------------------------------------
551
// Other private functions
552
//-----------------------------------------------------------------------------
553
554
void MMF::MediaObject::handlePrefinishMarkReached(qint32 time)
555
{
556
    emit tick(time);
557
}
558
559
560
QT_END_NAMESPACE