Skip to content

Commit bb994a5

Browse files
author
Gareth Stockwell
committed
Progressive download in Phonon MMF backend: integrated with player
This commit integrates the Download class with the media playback classes in the backend, to implement Progressive Download. Note that this PDL implementation has one drawback: when video playback is paused due to shortage of data (i.e. due to the download being temporarily stalled), the display goes black. This is because, when the end of the currently-downloaded data is reached, the playback session is closed. When more data becomes available, the clip is re-opened, a seek is done to reach the previous playback position, and playback is re-started. Closing the playback session closes the video stack's connection to the display, thereby causing the video widget to go black while more data is buffered. This is a consequence of the level in the native video stack at which the Phonon integration is done: managing a network stall without requiring the playback session to be closed would require integration below the MMF client API, specifically at the MMF controller level. Task-number: QTBUG-10769 Reviewed-by: Derick Hawcroft
1 parent fcf4b59 commit bb994a5

File tree

8 files changed

+211
-43
lines changed

8 files changed

+211
-43
lines changed

src/3rdparty/phonon/mmf/abstractmediaplayer.cpp

Lines changed: 134 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,13 @@ MMF::AbstractMediaPlayer::AbstractMediaPlayer
5151
, m_parent(parent)
5252
, m_pending(NothingPending)
5353
, m_positionTimer(new QTimer(this))
54+
, m_position(0)
5455
, m_bufferStatusTimer(new QTimer(this))
5556
, m_mmfMaxVolume(NullMaxVolume)
5657
, m_prefinishMarkSent(false)
5758
, m_aboutToFinishSent(false)
59+
, m_download(0)
60+
, m_downloadStalled(false)
5861
{
5962
connect(m_positionTimer.data(), SIGNAL(timeout()), this, SLOT(positionTick()));
6063
connect(m_bufferStatusTimer.data(), SIGNAL(timeout()), this, SLOT(bufferStatusTick()));
@@ -183,6 +186,7 @@ void MMF::AbstractMediaPlayer::seek(qint64 ms)
183186
}
184187

185188
doSeek(ms);
189+
m_position = ms;
186190
resetMarksIfRewound();
187191

188192
if(wasPlaying && state() != ErrorState) {
@@ -207,6 +211,11 @@ bool MMF::AbstractMediaPlayer::isSeekable() const
207211
return true;
208212
}
209213

214+
qint64 MMF::AbstractMediaPlayer::currentTime() const
215+
{
216+
return m_position;
217+
}
218+
210219
void MMF::AbstractMediaPlayer::doSetTickInterval(qint32 interval)
211220
{
212221
TRACE_CONTEXT(AbstractMediaPlayer::doSetTickInterval, EAudioApi);
@@ -247,6 +256,14 @@ void MMF::AbstractMediaPlayer::open()
247256
symbianErr = openFile(*file);
248257
if (KErrNone != symbianErr)
249258
errorMessage = tr("Error opening file");
259+
} else if (url.scheme() == QLatin1String("http")) {
260+
Q_ASSERT(!m_download);
261+
m_download = new Download(url, this);
262+
connect(m_download, SIGNAL(lengthChanged(qint64)),
263+
this, SLOT(downloadLengthChanged(qint64)));
264+
connect(m_download, SIGNAL(stateChanged(Download::State)),
265+
this, SLOT(downloadStateChanged(Download::State)));
266+
m_download->start();
250267
} else {
251268
symbianErr = openUrl(url.toString());
252269
if (KErrNone != symbianErr)
@@ -288,6 +305,14 @@ void MMF::AbstractMediaPlayer::open()
288305
TRACE_EXIT_0();
289306
}
290307

308+
void MMF::AbstractMediaPlayer::close()
309+
{
310+
doClose();
311+
delete m_download;
312+
m_download = 0;
313+
m_position = 0;
314+
}
315+
291316
void MMF::AbstractMediaPlayer::volumeChanged(qreal volume)
292317
{
293318
TRACE_CONTEXT(AbstractMediaPlayer::volumeChanged, EAudioInternal);
@@ -374,7 +399,8 @@ void MMF::AbstractMediaPlayer::bufferingComplete()
374399
{
375400
stopBufferStatusTimer();
376401
emit MMF::AbstractPlayer::bufferStatus(100);
377-
changeState(m_stateBeforeBuffering);
402+
if (!progressiveDownloadStalled())
403+
changeState(m_stateBeforeBuffering);
378404
}
379405

380406
void MMF::AbstractMediaPlayer::maxVolumeChanged(int mmfMaxVolume)
@@ -385,13 +411,28 @@ void MMF::AbstractMediaPlayer::maxVolumeChanged(int mmfMaxVolume)
385411

386412
void MMF::AbstractMediaPlayer::loadingComplete(int error)
387413
{
388-
Q_ASSERT(Phonon::LoadingState == state());
389-
390-
if (KErrNone == error) {
391-
updateMetaData();
392-
changeState(StoppedState);
414+
TRACE_CONTEXT(AbstractMediaPlayer::loadingComplete, EAudioApi);
415+
TRACE_ENTRY("state %d error %d", state(), error);
416+
if (progressiveDownloadStalled()) {
417+
Q_ASSERT(Phonon::BufferingState == state());
418+
if (KErrNone == error) {
419+
bufferingComplete();
420+
doSeek(m_position);
421+
startPlayback();
422+
m_downloadStalled = false;
423+
}
393424
} else {
394-
setError(tr("Loading clip failed"), error);
425+
Q_ASSERT(Phonon::LoadingState == state());
426+
if (KErrNone == error) {
427+
updateMetaData();
428+
changeState(StoppedState);
429+
} else {
430+
if (isProgressiveDownload() && KErrCorrupt == error) {
431+
setProgressiveDownloadStalled();
432+
} else {
433+
setError(tr("Loading clip failed"), error);
434+
}
435+
}
395436
}
396437
}
397438

@@ -415,8 +456,12 @@ void MMF::AbstractMediaPlayer::playbackComplete(int error)
415456
QMetaObject::invokeMethod(m_parent, "switchToNextSource", Qt::QueuedConnection);
416457
}
417458
else {
418-
setError(tr("Playback complete"), error);
419-
emit finished();
459+
if (isProgressiveDownload() && KErrCorrupt == error) {
460+
setProgressiveDownloadStalled();
461+
} else {
462+
setError(tr("Playback complete"), error);
463+
emit finished();
464+
}
420465
}
421466
}
422467

@@ -425,15 +470,28 @@ qint64 MMF::AbstractMediaPlayer::toMilliSeconds(const TTimeIntervalMicroSeconds
425470
return in.Int64() / 1000;
426471
}
427472

473+
bool MMF::AbstractMediaPlayer::isProgressiveDownload() const
474+
{
475+
return (0 != m_download);
476+
}
477+
478+
bool MMF::AbstractMediaPlayer::progressiveDownloadStalled() const
479+
{
480+
return m_downloadStalled;
481+
}
482+
428483
//-----------------------------------------------------------------------------
429484
// Slots
430485
//-----------------------------------------------------------------------------
431486

432487
void MMF::AbstractMediaPlayer::positionTick()
433488
{
434-
const qint64 current = currentTime();
435-
emitMarksIfReached(current);
436-
emit MMF::AbstractPlayer::tick(current);
489+
const qint64 pos = getCurrentTime();
490+
if (pos > m_position) {
491+
m_position = pos;
492+
emitMarksIfReached(m_position);
493+
emit MMF::AbstractPlayer::tick(m_position);
494+
}
437495
}
438496

439497
void MMF::AbstractMediaPlayer::emitMarksIfReached(qint64 current)
@@ -458,7 +516,7 @@ void MMF::AbstractMediaPlayer::emitMarksIfReached(qint64 current)
458516

459517
void MMF::AbstractMediaPlayer::resetMarksIfRewound()
460518
{
461-
const qint64 current = currentTime();
519+
const qint64 current = getCurrentTime();
462520
const qint64 total = totalTime();
463521
const qint64 remaining = total - current;
464522

@@ -487,9 +545,71 @@ void MMF::AbstractMediaPlayer::startPlayback()
487545
changeState(PlayingState);
488546
}
489547

548+
void MMF::AbstractMediaPlayer::setProgressiveDownloadStalled()
549+
{
550+
TRACE_CONTEXT(AbstractMediaPlayer::setProgressiveDownloadStalled, EAudioApi);
551+
TRACE_ENTRY("state %d", state());
552+
Q_ASSERT(isProgressiveDownload());
553+
m_downloadStalled = true;
554+
doClose();
555+
bufferingStarted();
556+
// Video player loses window handle when closed - need to reapply it here
557+
videoOutputChanged();
558+
#ifdef QT_PHONON_MMF_DOWNLOAD_DUMMY
559+
m_download->resume();
560+
#endif
561+
}
562+
490563
void MMF::AbstractMediaPlayer::bufferStatusTick()
491564
{
492-
emit MMF::AbstractPlayer::bufferStatus(bufferStatus());
565+
// During progressive download, there is no way to detect the buffering status.
566+
// Phonon does not support a "buffering; amount unknown" signal, therefore we
567+
// return a buffering status of zero.
568+
const int status = progressiveDownloadStalled() ? 0 : bufferStatus();
569+
emit MMF::AbstractPlayer::bufferStatus(status);
570+
}
571+
572+
void MMF::AbstractMediaPlayer::downloadLengthChanged(qint64 length)
573+
{
574+
TRACE_CONTEXT(AbstractMediaPlayer::downloadLengthChanged, EAudioApi);
575+
TRACE_ENTRY("length %Ld", length);
576+
Q_UNUSED(length)
577+
if (m_downloadStalled) {
578+
bufferingComplete();
579+
int err = m_parent->openFileHandle(m_download->targetFileName());
580+
if (KErrNone == err)
581+
err = openFile(*m_parent->file());
582+
if (KErrNone != err)
583+
setError(tr("Error opening file"));
584+
}
585+
}
586+
587+
void MMF::AbstractMediaPlayer::downloadStateChanged(Download::State state)
588+
{
589+
TRACE_CONTEXT(AbstractMediaPlayer::downloadStateChanged, EAudioApi);
590+
TRACE_ENTRY("state %d", state);
591+
switch (state) {
592+
case Download::Idle:
593+
case Download::Initializing:
594+
break;
595+
case Download::Downloading:
596+
{
597+
int err = m_parent->openFileHandle(m_download->targetFileName());
598+
if (KErrNone == err)
599+
err = openFile(*m_parent->file());
600+
else if (KErrCorrupt == err)
601+
// Insufficient data downloaded - enter Buffering state
602+
setProgressiveDownloadStalled();
603+
if (KErrNone != err)
604+
setError(tr("Error opening file"));
605+
}
606+
break;
607+
case Download::Complete:
608+
break;
609+
case Download::Error:
610+
setError(tr("Download error"));
611+
break;
612+
}
493613
}
494614

495615
Phonon::State MMF::AbstractMediaPlayer::phononState(PrivateState state) const

src/3rdparty/phonon/mmf/abstractmediaplayer.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ along with this library. If not, see <http://www.gnu.org/licenses/>.
2323
#include <QScopedPointer>
2424
#include <e32std.h>
2525
#include "abstractplayer.h"
26+
#include "download.h"
2627

2728
class RFile;
2829

@@ -48,13 +49,15 @@ class AbstractMediaPlayer : public AbstractPlayer
4849

4950
public:
5051
virtual void open();
52+
virtual void close();
5153

5254
// MediaObjectInterface
5355
virtual void play();
5456
virtual void pause();
5557
virtual void stop();
5658
virtual void seek(qint64 milliseconds);
5759
virtual bool isSeekable() const;
60+
virtual qint64 currentTime() const;
5861
virtual void volumeChanged(qreal volume);
5962

6063
protected:
@@ -68,12 +71,15 @@ class AbstractMediaPlayer : public AbstractPlayer
6871
virtual void doStop() = 0;
6972
virtual void doSeek(qint64 pos) = 0;
7073
virtual int setDeviceVolume(int mmfVolume) = 0;
74+
virtual int openFile(const QString &fileName) = 0;
7175
virtual int openFile(RFile& file) = 0;
7276
virtual int openUrl(const QString& url) = 0;
7377
virtual int openDescriptor(const TDesC8 &des) = 0;
7478
virtual int bufferStatus() const = 0;
79+
virtual void doClose() = 0;
7580

7681
void updateMetaData();
82+
virtual qint64 getCurrentTime() const = 0;
7783
virtual int numberOfMetaDataEntries() const = 0;
7884
virtual QPair<QString, QString> metaDataEntry(int index) const = 0;
7985

@@ -86,6 +92,9 @@ class AbstractMediaPlayer : public AbstractPlayer
8692

8793
static qint64 toMilliSeconds(const TTimeIntervalMicroSeconds &);
8894

95+
bool isProgressiveDownload() const;
96+
bool progressiveDownloadStalled() const;
97+
8998
private:
9099
void startPositionTimer();
91100
void stopPositionTimer();
@@ -96,6 +105,7 @@ class AbstractMediaPlayer : public AbstractPlayer
96105
void emitMarksIfReached(qint64 position);
97106
void resetMarksIfRewound();
98107
void startPlayback();
108+
void setProgressiveDownloadStalled();
99109

100110
enum Pending {
101111
NothingPending,
@@ -108,13 +118,16 @@ class AbstractMediaPlayer : public AbstractPlayer
108118
private Q_SLOTS:
109119
void positionTick();
110120
void bufferStatusTick();
121+
void downloadLengthChanged(qint64);
122+
void downloadStateChanged(Download::State);
111123

112124
private:
113125
MediaObject *const m_parent;
114126

115127
Pending m_pending;
116128

117129
QScopedPointer<QTimer> m_positionTimer;
130+
qint64 m_position;
118131

119132
QScopedPointer<QTimer> m_bufferStatusTimer;
120133
PrivateState m_stateBeforeBuffering;
@@ -127,6 +140,10 @@ private Q_SLOTS:
127140
// Used for playback of resource files
128141
TPtrC8 m_buffer;
129142

143+
// Used for progressive download
144+
Download *m_download;
145+
bool m_downloadStalled;
146+
130147
QMultiMap<QString, QString> m_metaData;
131148

132149
};

src/3rdparty/phonon/mmf/abstractvideoplayer.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ along with this library. If not, see <http://www.gnu.org/licenses/>.
1616
1717
*/
1818

19+
#include <QDir>
1920
#include <QUrl>
2021
#include <QTimer>
2122
#include <QWidget>
@@ -132,6 +133,13 @@ int MMF::AbstractVideoPlayer::setDeviceVolume(int mmfVolume)
132133
return err;
133134
}
134135

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+
135143
int MMF::AbstractVideoPlayer::openFile(RFile &file)
136144
{
137145
TRAPD(err, m_player->OpenFileL(file));
@@ -157,7 +165,7 @@ int MMF::AbstractVideoPlayer::bufferStatus() const
157165
return result;
158166
}
159167

160-
void MMF::AbstractVideoPlayer::close()
168+
void MMF::AbstractVideoPlayer::doClose()
161169
{
162170
m_player->Close();
163171
}
@@ -167,9 +175,9 @@ bool MMF::AbstractVideoPlayer::hasVideo() const
167175
return true;
168176
}
169177

170-
qint64 MMF::AbstractVideoPlayer::currentTime() const
178+
qint64 MMF::AbstractVideoPlayer::getCurrentTime() const
171179
{
172-
TRACE_CONTEXT(AbstractVideoPlayer::currentTime, EVideoApi);
180+
TRACE_CONTEXT(AbstractVideoPlayer::getCurrentTime, EVideoApi);
173181

174182
TTimeIntervalMicroSeconds us;
175183
TRAPD(err, us = m_player->PositionL())
@@ -246,7 +254,9 @@ void MMF::AbstractVideoPlayer::MvpuoOpenComplete(TInt aError)
246254
TRACE_CONTEXT(AbstractVideoPlayer::MvpuoOpenComplete, EVideoApi);
247255
TRACE_ENTRY("state %d error %d", state(), aError);
248256

249-
__ASSERT_ALWAYS(LoadingState == state(), Utils::panic(InvalidStatePanic));
257+
__ASSERT_ALWAYS(LoadingState == state() ||
258+
progressiveDownloadStalled() && BufferingState == state(),
259+
Utils::panic(InvalidStatePanic));
250260

251261
if (KErrNone == aError)
252262
m_player->Prepare();
@@ -261,7 +271,9 @@ void MMF::AbstractVideoPlayer::MvpuoPrepareComplete(TInt aError)
261271
TRACE_CONTEXT(AbstractVideoPlayer::MvpuoPrepareComplete, EVideoApi);
262272
TRACE_ENTRY("state %d error %d", state(), aError);
263273

264-
__ASSERT_ALWAYS(LoadingState == state(), Utils::panic(InvalidStatePanic));
274+
__ASSERT_ALWAYS(LoadingState == state() ||
275+
progressiveDownloadStalled() && BufferingState == state(),
276+
Utils::panic(InvalidStatePanic));
265277

266278
TRAPD(err, getVideoClipParametersL(aError));
267279

@@ -470,7 +482,7 @@ void MMF::AbstractVideoPlayer::updateScaleFactors(const QSize &windowSize, bool
470482

471483
void MMF::AbstractVideoPlayer::parametersChanged(VideoParameters parameters)
472484
{
473-
if (state() == LoadingState)
485+
if (state() == LoadingState || progressiveDownloadStalled() && BufferingState == state())
474486
m_pendingChanges |= parameters;
475487
else
476488
handleParametersChanged(parameters);

0 commit comments

Comments
 (0)