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 <QDir>
20
#include <QUrl>
21
#include <QTimer>
22
#include <QWidget>
23
24
#include <coemain.h>    // for CCoeEnv
25
26
#include "abstractvideoplayer.h"
27
#include "utils.h"
28
29
#ifndef QT_NO_DEBUG
30
#include "objectdump.h"
31
#endif
32
33
QT_BEGIN_NAMESPACE
34
35
using namespace Phonon;
36
using namespace Phonon::MMF;
37
38
/*! \class MMF::AbstractVideoPlayer
39
  \internal
40
*/
41
42
//-----------------------------------------------------------------------------
43
// Constructor / destructor
44
//-----------------------------------------------------------------------------
45
46
MMF::AbstractVideoPlayer::AbstractVideoPlayer(MediaObject *parent, const AbstractPlayer *player)
47
    :   AbstractMediaPlayer(parent, player)
48
    ,   m_wsSession(CCoeEnv::Static()->WsSession())
49
    ,   m_screenDevice(*CCoeEnv::Static()->ScreenDevice())
50
    ,   m_window(0)
51
    ,   m_scaleWidth(1.0)
52
    ,   m_scaleHeight(1.0)
53
    ,   m_totalTime(0)
54
{
55
56
}
57
58
void MMF::AbstractVideoPlayer::construct()
59
{
60
    TRACE_CONTEXT(AbstractVideoPlayer::AbstractVideoPlayer, EVideoApi);
61
    TRACE_ENTRY_0();
62
63
    if (m_videoOutput) {
64
        initVideoOutput();
65
        m_window = m_videoOutput->videoWindow();
66
    }
67
68
    createPlayer();
69
70
    m_player->RegisterForVideoLoadingNotification(*this);
71
72
    TRACE_EXIT_0();
73
}
74
75
MMF::AbstractVideoPlayer::~AbstractVideoPlayer()
76
{
77
    TRACE_CONTEXT(AbstractVideoPlayer::~AbstractVideoPlayer, EVideoApi);
78
    TRACE_ENTRY_0();
79
80
    // QObject destructor removes all signal-slot connections involving this
81
    // object, so we do not need to disconnect from m_videoOutput here.
82
83
    TRACE_EXIT_0();
84
}
85
86
CVideoPlayerUtility* MMF::AbstractVideoPlayer::nativePlayer() const
87
{
88
    return m_player.data();
89
}
90
91
//-----------------------------------------------------------------------------
92
// Public API
93
//-----------------------------------------------------------------------------
94
95
void MMF::AbstractVideoPlayer::doPlay()
96
{
97
    TRACE_CONTEXT(AbstractVideoPlayer::doPlay, EVideoApi);
98
99
    handlePendingParametersChanged();
100
101
    m_player->Play();
102
}
103
104
void MMF::AbstractVideoPlayer::doPause()
105
{
106
    TRACE_CONTEXT(AbstractVideoPlayer::doPause, EVideoApi);
107
108
    TRAPD(err, m_player->PauseL());
109
    if (KErrNone != err && state() != ErrorState) {
110
        TRACE("PauseL error %d", err);
111
        setError(tr("Pause failed"), err);
112
    }
113
}
114
115
void MMF::AbstractVideoPlayer::doStop()
116
{
117
    m_player->Stop();
118
}
119
120
void MMF::AbstractVideoPlayer::doSeek(qint64 ms)
121
{
122
    TRACE_CONTEXT(AbstractVideoPlayer::doSeek, EVideoApi);
123
124
    TRAPD(err, m_player->SetPositionL(TTimeIntervalMicroSeconds(ms * 1000)));
125
126
    if (KErrNone != err)
127
        setError(tr("Seek failed"), err);
128
}
129
130
int MMF::AbstractVideoPlayer::setDeviceVolume(int mmfVolume)
131
{
132
    TRAPD(err, m_player->SetVolumeL(mmfVolume));
133
    return err;
134
}
135
136
int MMF::AbstractVideoPlayer::openFile(const QString &fileName)
137
{
138
    const QHBufC nativeFileName(QDir::toNativeSeparators(fileName));
139
    TRAPD(err, m_player->OpenFileL(*nativeFileName));
140
    return err;
141
}
142
143
int MMF::AbstractVideoPlayer::openFile(RFile &file)
144
{
145
    TRAPD(err, m_player->OpenFileL(file));
146
    return err;
147
}
148
149
int MMF::AbstractVideoPlayer::openUrl(const QString &url, int iap)
150
{
151
    TRAPD(err, m_player->OpenUrlL(qt_QString2TPtrC(url), iap));
152
    return err;
153
}
154
155
int MMF::AbstractVideoPlayer::openDescriptor(const TDesC8 &des)
156
{
157
    TRAPD(err, m_player->OpenDesL(des));
158
    return err;
159
}
160
161
int MMF::AbstractVideoPlayer::bufferStatus() const
162
{
163
    int result = 0;
164
    TRAP_IGNORE(m_player->GetVideoLoadingProgressL(result));
165
    return result;
166
}
167
168
void MMF::AbstractVideoPlayer::doClose()
169
{
170
    m_player->Close();
171
}
172
173
bool MMF::AbstractVideoPlayer::hasVideo() const
174
{
175
    return true;
176
}
177
178
qint64 MMF::AbstractVideoPlayer::getCurrentTime() const
179
{
180
    TRACE_CONTEXT(AbstractVideoPlayer::getCurrentTime, EVideoApi);
181
182
    TTimeIntervalMicroSeconds us;
183
    TRAPD(err, us = m_player->PositionL())
184
185
    qint64 result = 0;
186
187
    if (KErrNone == err) {
188
        result = toMilliSeconds(us);
189
    } else {
190
        TRACE("PositionL error %d", err);
191
192
        // If we don't cast away constness here, we simply have to ignore
193
        // the error.
194
        const_cast<AbstractVideoPlayer*>(this)->setError(tr("Getting position failed"), err);
195
    }
196
197
    return result;
198
}
199
200
qint64 MMF::AbstractVideoPlayer::totalTime() const
201
{
202
    return m_totalTime;
203
}
204
205
206
//-----------------------------------------------------------------------------
207
// Public slots
208
//-----------------------------------------------------------------------------
209
210
void MMF::AbstractVideoPlayer::videoWindowChanged()
211
{
212
    TRACE_CONTEXT(AbstractVideoPlayer::videoWindowChanged, EVideoInternal);
213
    TRACE_ENTRY("state %d", state());
214
215
    m_window = m_videoOutput ? m_videoOutput->videoWindow() : 0;
216
217
    if (m_videoOutput)
218
        m_videoOutput->dump();
219
220
    handleVideoWindowChanged();
221
222
    TRACE_EXIT_0();
223
}
224
225
void MMF::AbstractVideoPlayer::aspectRatioChanged()
226
{
227
    TRACE_CONTEXT(AbstractVideoPlayer::aspectRatioChanged, EVideoInternal);
228
    TRACE_ENTRY("state %d aspectRatio %d", state());
229
230
    if (m_videoOutput)
231
        updateScaleFactors(m_videoOutput->videoWindowSize());
232
233
    TRACE_EXIT_0();
234
}
235
236
void MMF::AbstractVideoPlayer::scaleModeChanged()
237
{
238
    TRACE_CONTEXT(AbstractVideoPlayer::scaleModeChanged, EVideoInternal);
239
    TRACE_ENTRY("state %d", state());
240
241
    if (m_videoOutput)
242
        updateScaleFactors(m_videoOutput->videoWindowSize());
243
244
    TRACE_EXIT_0();
245
}
246
247
248
//-----------------------------------------------------------------------------
249
// MVideoPlayerUtilityObserver callbacks
250
//-----------------------------------------------------------------------------
251
252
void MMF::AbstractVideoPlayer::MvpuoOpenComplete(TInt aError)
253
{
254
    TRACE_CONTEXT(AbstractVideoPlayer::MvpuoOpenComplete, EVideoApi);
255
    TRACE_ENTRY("state %d error %d", state(), aError);
256
257
    __ASSERT_ALWAYS(LoadingState == state() ||
258
                    progressiveDownloadStalled() && BufferingState == state(),
259
                    Utils::panic(InvalidStatePanic));
260
261
    if (KErrNone == aError)
262
        m_player->Prepare();
263
    else
264
        setError(tr("Opening clip failed"), aError);
265
266
    TRACE_EXIT_0();
267
}
268
269
void MMF::AbstractVideoPlayer::MvpuoPrepareComplete(TInt aError)
270
{
271
    TRACE_CONTEXT(AbstractVideoPlayer::MvpuoPrepareComplete, EVideoApi);
272
    TRACE_ENTRY("state %d error %d", state(), aError);
273
274
    __ASSERT_ALWAYS(LoadingState == state() ||
275
                    progressiveDownloadStalled() && BufferingState == state(),
276
                    Utils::panic(InvalidStatePanic));
277
278
    TRAPD(err, getVideoClipParametersL(aError));
279
280
    if (KErrNone == err) {
281
        if (m_videoOutput)
282
            m_videoOutput->dump();
283
284
        maxVolumeChanged(m_player->MaxVolume());
285
286
        if (m_videoOutput)
287
            m_videoOutput->setVideoSize(m_videoFrameSize);
288
289
        prepareCompleted();
290
        handlePendingParametersChanged();
291
292
        emit totalTimeChanged(totalTime());
293
    }
294
295
    loadingComplete(aError);
296
297
    TRACE_EXIT_0();
298
}
299
300
void MMF::AbstractVideoPlayer::getVideoClipParametersL(TInt aError)
301
{
302
    User::LeaveIfError(aError);
303
304
    // Get frame size
305
    TSize size;
306
    m_player->VideoFrameSizeL(size);
307
    m_videoFrameSize = QSize(size.iWidth, size.iHeight);
308
309
    // Get duration
310
    m_totalTime = toMilliSeconds(m_player->DurationL());
311
}
312
313
314
void MMF::AbstractVideoPlayer::MvpuoFrameReady(CFbsBitmap &aFrame, TInt aError)
315
{
316
    TRACE_CONTEXT(AbstractVideoPlayer::MvpuoFrameReady, EVideoApi);
317
    TRACE_ENTRY("state %d error %d", state(), aError);
318
319
    Q_UNUSED(aFrame);
320
    Q_UNUSED(aError);   // suppress warnings in release builds
321
322
    TRACE_EXIT_0();
323
}
324
325
void MMF::AbstractVideoPlayer::MvpuoPlayComplete(TInt aError)
326
{
327
    TRACE_CONTEXT(AbstractVideoPlayer::MvpuoPlayComplete, EVideoApi)
328
    TRACE_ENTRY("state %d error %d", state(), aError);
329
330
    // Call base class function which handles end of playback for both
331
    // audio and video clips.
332
    playbackComplete(aError);
333
334
    TRACE_EXIT_0();
335
}
336
337
void MMF::AbstractVideoPlayer::MvpuoEvent(const TMMFEvent &aEvent)
338
{
339
    TRACE_CONTEXT(AbstractVideoPlayer::MvpuoEvent, EVideoApi);
340
    TRACE_ENTRY("state %d", state());
341
342
    Q_UNUSED(aEvent);
343
344
    TRACE_EXIT_0();
345
}
346
347
348
//-----------------------------------------------------------------------------
349
// MVideoLoadingObserver callbacks
350
//-----------------------------------------------------------------------------
351
352
void MMF::AbstractVideoPlayer::MvloLoadingStarted()
353
{
354
    bufferingStarted();
355
}
356
357
void MMF::AbstractVideoPlayer::MvloLoadingComplete()
358
{
359
    bufferingComplete();
360
}
361
362
363
//-----------------------------------------------------------------------------
364
// Video window updates
365
//-----------------------------------------------------------------------------
366
367
void MMF::AbstractVideoPlayer::videoOutputChanged()
368
{
369
    TRACE_CONTEXT(AbstractVideoPlayer::videoOutputChanged, EVideoInternal);
370
    TRACE_ENTRY_0();
371
372
    if (m_videoOutput)
373
        initVideoOutput();
374
375
    videoWindowChanged();
376
377
    TRACE_EXIT_0();
378
}
379
380
void MMF::AbstractVideoPlayer::initVideoOutput()
381
{
382
    Q_ASSERT(m_videoOutput);
383
384
    bool connected = connect(
385
        m_videoOutput, SIGNAL(videoWindowChanged()),
386
        this, SLOT(videoWindowChanged())
387
    );
388
    Q_ASSERT(connected);
389
390
    connected = connect(
391
        m_videoOutput, SIGNAL(aspectRatioChanged()),
392
        this, SLOT(aspectRatioChanged())
393
    );
394
    Q_ASSERT(connected);
395
396
    connected = connect(
397
        m_videoOutput, SIGNAL(scaleModeChanged()),
398
        this, SLOT(scaleModeChanged())
399
    );
400
    Q_ASSERT(connected);
401
402
    // Suppress warnings in release builds
403
    Q_UNUSED(connected);
404
405
    m_videoOutput->setVideoSize(m_videoFrameSize);
406
}
407
408
// Helper function for aspect ratio / scale mode handling
409
QSize scaleToAspect(const QSize &srcRect, int aspectWidth, int aspectHeight)
410
{
411
    const qreal aspectRatio = qreal(aspectWidth) / aspectHeight;
412
413
    int width = srcRect.width();
414
    int height = srcRect.width() / aspectRatio;
415
    if (height > srcRect.height()){
416
        height = srcRect.height();
417
        width = srcRect.height() * aspectRatio;
418
    }
419
    return QSize(width, height);
420
}
421
422
void MMF::AbstractVideoPlayer::updateScaleFactors(const QSize &windowSize, bool apply)
423
{
424
    Q_ASSERT(m_videoOutput);
425
426
    if (m_videoFrameSize.isValid()) {
427
        QRect videoRect;
428
429
        // Calculate size of smallest rect which contains video frame size
430
        // and conforms to aspect ratio
431
        switch (m_videoOutput->aspectRatio()) {
432
        case Phonon::VideoWidget::AspectRatioAuto:
433
            videoRect.setSize(m_videoFrameSize);
434
            break;
435
436
        case Phonon::VideoWidget::AspectRatioWidget:
437
            videoRect.setSize(windowSize);
438
            break;
439
440
        case Phonon::VideoWidget::AspectRatio4_3:
441
            videoRect.setSize(scaleToAspect(m_videoFrameSize, 4, 3));
442
            break;
443
444
        case Phonon::VideoWidget::AspectRatio16_9:
445
            videoRect.setSize(scaleToAspect(m_videoFrameSize, 16, 9));
446
            break;
447
        }
448
449
        // Scale to fill the window width
450
        const int windowWidth = windowSize.width();
451
        const int windowHeight = windowSize.height();
452
        const qreal windowScaleFactor = qreal(windowWidth) / videoRect.width();
453
        int videoWidth = windowWidth;
454
        int videoHeight = videoRect.height() * windowScaleFactor;
455
456
        const qreal windowToVideoHeightRatio = qreal(windowHeight) / videoHeight;
457
458
        switch (m_videoOutput->scaleMode()) {
459
        case Phonon::VideoWidget::ScaleAndCrop:
460
            if (videoHeight < windowHeight) {
461
                videoWidth *= windowToVideoHeightRatio;
462
                videoHeight = windowHeight;
463
            }
464
            break;
465
        case Phonon::VideoWidget::FitInView:
466
        default:
467
            if (videoHeight > windowHeight) {
468
                videoWidth *= windowToVideoHeightRatio;
469
                videoHeight = windowHeight;
470
            }
471
            break;
472
        }
473
474
        // Calculate scale factors
475
        m_scaleWidth = 100.0f * videoWidth / m_videoFrameSize.width();
476
        m_scaleHeight = 100.0f * videoHeight / m_videoFrameSize.height();
477
478
        if (apply)
479
            parametersChanged(ScaleFactors);
480
    }
481
}
482
483
void MMF::AbstractVideoPlayer::parametersChanged(VideoParameters parameters)
484
{
485
    if (state() == LoadingState || progressiveDownloadStalled() && BufferingState == state())
486
        m_pendingChanges |= parameters;
487
    else
488
        handleParametersChanged(parameters);
489
}
490
491
void MMF::AbstractVideoPlayer::handlePendingParametersChanged()
492
{
493
    if (m_pendingChanges)
494
        handleParametersChanged(m_pendingChanges);
495
    m_pendingChanges = 0;
496
}
497
498
499
//-----------------------------------------------------------------------------
500
// Metadata
501
//-----------------------------------------------------------------------------
502
503
int MMF::AbstractVideoPlayer::numberOfMetaDataEntries() const
504
{
505
    int numberOfEntries = 0;
506
    TRAP_IGNORE(numberOfEntries = m_player->NumberOfMetaDataEntriesL());
507
    return numberOfEntries;
508
}
509
510
QPair<QString, QString> MMF::AbstractVideoPlayer::metaDataEntry(int index) const
511
{
512
    CMMFMetaDataEntry *entry = 0;
513
    QT_TRAP_THROWING(entry = m_player->MetaDataEntryL(index));
514
    return QPair<QString, QString>(qt_TDesC2QString(entry->Name()), qt_TDesC2QString(entry->Value()));
515
}
516
517
QT_END_NAMESPACE