From c24280aef0962c4a45f9fa6c927b2df3a5f34245 Mon Sep 17 00:00:00 2001 From: Dmytro Poplavskiy Date: Thu, 4 Mar 2010 14:57:43 +1000 Subject: Added QMediaPlayer::StreamPlayback flag to query/select backend capable of playing from QIODevice based stream. --- src/multimedia/base/qmediaserviceprovider.cpp | 43 ++++++++++--- src/multimedia/base/qmediaserviceprovider.h | 3 +- src/multimedia/playback/qmediaplayer.cpp | 18 +++++- src/multimedia/playback/qmediaplayer.h | 3 +- .../tst_qmediaserviceprovider.cpp | 72 ++++++++++++++++++++++ 5 files changed, 125 insertions(+), 14 deletions(-) diff --git a/src/multimedia/base/qmediaserviceprovider.cpp b/src/multimedia/base/qmediaserviceprovider.cpp index d51d682..6e11079 100644 --- a/src/multimedia/base/qmediaserviceprovider.cpp +++ b/src/multimedia/base/qmediaserviceprovider.cpp @@ -102,6 +102,9 @@ public: \value RecordingSupport The service provides audio or video recording functions. + + \value StreamPlayback + The service is capable of playing QIODevice based streams. */ /*! @@ -396,14 +399,25 @@ public: QMediaServiceSupportedFormatsInterface *iface = qobject_cast(obj); - //if low latency playback was asked, skip services known - //not to provide low latency playback - if (flags & QMediaPlayer::LowLatency) { + + if (flags) { QMediaServiceFeaturesInterface *iface = qobject_cast(obj); - if (iface && !(iface->supportedFeatures(serviceType) & QMediaServiceProviderHint::LowLatencyPlayback)) - continue; + if (iface) { + QMediaServiceProviderHint::Features features = iface->supportedFeatures(serviceType); + + //if low latency playback was asked, skip services known + //not to provide low latency playback + if ((flags & QMediaPlayer::LowLatency) && + !(features & QMediaServiceProviderHint::LowLatencyPlayback)) + continue; + + //the same for QIODevice based streams support + if ((flags & QMediaPlayer::StreamPlayback) && + !(features & QMediaServiceProviderHint::StreamPlayback)) + continue; + } } if (iface) @@ -434,14 +448,25 @@ public: QMediaServiceSupportedFormatsInterface *iface = qobject_cast(obj); - // If low latency playback was asked for, skip MIME types from services known - // not to provide low latency playback + if (flags & QMediaPlayer::LowLatency) { QMediaServiceFeaturesInterface *iface = qobject_cast(obj); - if (iface && !(iface->supportedFeatures(serviceType) & QMediaServiceProviderHint::LowLatencyPlayback)) - continue; + if (iface) { + QMediaServiceProviderHint::Features features = iface->supportedFeatures(serviceType); + + // If low latency playback was asked for, skip MIME types from services known + // not to provide low latency playback + if ((flags & QMediaPlayer::LowLatency) && + !(features & QMediaServiceProviderHint::LowLatencyPlayback)) + continue; + + //the same for QIODevice based streams support + if ((flags & QMediaPlayer::StreamPlayback) && + !(features & QMediaServiceProviderHint::StreamPlayback)) + continue; + } } if (iface) { diff --git a/src/multimedia/base/qmediaserviceprovider.h b/src/multimedia/base/qmediaserviceprovider.h index 2ee0ae4..6e31493 100644 --- a/src/multimedia/base/qmediaserviceprovider.h +++ b/src/multimedia/base/qmediaserviceprovider.h @@ -64,7 +64,8 @@ public: enum Feature { LowLatencyPlayback = 0x01, - RecordingSupport = 0x02 + RecordingSupport = 0x02, + StreamPlayback = 0x04 }; Q_DECLARE_FLAGS(Features, Feature) diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp index 27bff02..8056878 100644 --- a/src/multimedia/playback/qmediaplayer.cpp +++ b/src/multimedia/playback/qmediaplayer.cpp @@ -243,10 +243,17 @@ void QMediaPlayerPrivate::_q_playlistDestroyed() static QMediaService *playerService(QMediaPlayer::Flags flags, QMediaServiceProvider *provider) { - if (flags && QMediaPlayer::LowLatency) + if (flags) { + QMediaServiceProviderHint::Features features = 0; + if (flags & QMediaPlayer::LowLatency) + features |= QMediaServiceProviderHint::LowLatencyPlayback; + + if (flags & QMediaPlayer::StreamPlayback) + features |= QMediaServiceProviderHint::StreamPlayback; + return provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER, - QMediaServiceProviderHint(QMediaServiceProviderHint::LowLatencyPlayback)); - else + QMediaServiceProviderHint(features)); + } else return provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER); } @@ -945,6 +952,11 @@ QStringList QMediaPlayer::supportedMimeTypes(Flags flags) The player is expected to be used with simple audio formats, but playback should start without significant delay. Such playback service can be used for beeps, ringtones, etc. + + \value StreamPlayback + The player is expected to play QIODevice based streams. + If passed to QMediaPlayer constructor, the service supporting + streams playback will be choosen. */ QT_END_NAMESPACE diff --git a/src/multimedia/playback/qmediaplayer.h b/src/multimedia/playback/qmediaplayer.h index 1b761ce..129b244 100644 --- a/src/multimedia/playback/qmediaplayer.h +++ b/src/multimedia/playback/qmediaplayer.h @@ -99,7 +99,8 @@ public: enum Flag { - LowLatency = 0x01 + LowLatency = 0x01, + StreamPlayback = 0x02 }; Q_DECLARE_FLAGS(Flags, Flag) diff --git a/tests/auto/qmediaserviceprovider/tst_qmediaserviceprovider.cpp b/tests/auto/qmediaserviceprovider/tst_qmediaserviceprovider.cpp index 9bca189..d839fe5 100644 --- a/tests/auto/qmediaserviceprovider/tst_qmediaserviceprovider.cpp +++ b/tests/auto/qmediaserviceprovider/tst_qmediaserviceprovider.cpp @@ -213,6 +213,57 @@ public: } }; +class MockServicePlugin4 : public QMediaServiceProviderPlugin, + public QMediaServiceSupportedFormatsInterface, + public QMediaServiceFeaturesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedFormatsInterface) + Q_INTERFACES(QMediaServiceFeaturesInterface) +public: + QStringList keys() const + { + return QStringList() << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER); + } + + QMediaService* create(QString const& key) + { + if (keys().contains(key)) + return new MockMediaService("MockServicePlugin4"); + else + return 0; + } + + void release(QMediaService *service) + { + delete service; + } + + QtMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const + { + if (codecs.contains(QLatin1String("jpeg2000"))) + return QtMultimedia::NotSupported; + + if (supportedMimeTypes().contains(mimeType)) + return QtMultimedia::ProbablySupported; + + return QtMultimedia::MaybeSupported; + } + + QStringList supportedMimeTypes() const + { + return QStringList() << "video/mp4" << "video/quicktime"; + } + + QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const + { + if (service == QByteArray(Q_MEDIASERVICE_MEDIAPLAYER)) + return QMediaServiceProviderHint::StreamPlayback; + else + return 0; + } +}; + class MockMediaServiceProvider : public QMediaServiceProvider @@ -253,6 +304,7 @@ void tst_QMediaServiceProvider::initTestCase() plugins << new MockServicePlugin1; plugins << new MockServicePlugin2; plugins << new MockServicePlugin3; + plugins << new MockServicePlugin4; QMediaPluginLoader::setStaticPlugins(QLatin1String("/mediaservices"), plugins); } @@ -312,12 +364,32 @@ void tst_QMediaServiceProvider::testHasSupport() QCOMPARE(QMediaPlayer::hasSupport("audio/ogg"), QtMultimedia::ProbablySupported); QCOMPARE(QMediaPlayer::hasSupport("audio/wav"), QtMultimedia::ProbablySupported); + //test low latency flag support + QCOMPARE(QMediaPlayer::hasSupport("audio/wav", QStringList(), QMediaPlayer::LowLatency), + QtMultimedia::ProbablySupported); + //plugin1 probably supports audio/ogg, it checked because it doesn't provide features iface + QCOMPARE(QMediaPlayer::hasSupport("audio/ogg", QStringList(), QMediaPlayer::LowLatency), + QtMultimedia::ProbablySupported); + //Plugin4 is not checked here, sine it's known not support low latency + QCOMPARE(QMediaPlayer::hasSupport("video/quicktime", QStringList(), QMediaPlayer::LowLatency), + QtMultimedia::MaybeSupported); + + //test streaming flag support + QCOMPARE(QMediaPlayer::hasSupport("video/quicktime", QStringList(), QMediaPlayer::StreamPlayback), + QtMultimedia::ProbablySupported); + //Plugin2 is not checked here, sine it's known not support streaming + QCOMPARE(QMediaPlayer::hasSupport("audio/wav", QStringList(), QMediaPlayer::StreamPlayback), + QtMultimedia::MaybeSupported); + //ensure the correct media player plugin is choosen for mime type QMediaPlayer simplePlayer(0, QMediaPlayer::LowLatency); QCOMPARE(simplePlayer.service()->objectName(), QLatin1String("MockServicePlugin2")); QMediaPlayer mediaPlayer; QVERIFY(mediaPlayer.service()->objectName() != QLatin1String("MockServicePlugin2")); + + QMediaPlayer streamPlayer(0, QMediaPlayer::StreamPlayback); + QCOMPARE(streamPlayer.service()->objectName(), QLatin1String("MockServicePlugin4")); } void tst_QMediaServiceProvider::testSupportedMimeTypes() -- cgit v0.12 From 787da2f188c34fd932662ee0229908bec8c4c91a Mon Sep 17 00:00:00 2001 From: Dmytro Poplavskiy Date: Thu, 4 Mar 2010 17:21:33 +1000 Subject: Added playlist playback modes combo box to player demo Reviewed-by: Justin McPherson --- demos/multimedia/player/player.cpp | 28 +++++++++++++++++++++++++++- demos/multimedia/player/player.h | 3 +++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/demos/multimedia/player/player.cpp b/demos/multimedia/player/player.cpp index 49d18cb..af30a97 100644 --- a/demos/multimedia/player/player.cpp +++ b/demos/multimedia/player/player.cpp @@ -81,6 +81,22 @@ Player::Player(QWidget *parent) connect(playlistView, SIGNAL(activated(QModelIndex)), this, SLOT(jump(QModelIndex))); + playbackModeBox = new QComboBox; + playbackModeBox->addItem(tr("Linear"), + QVariant::fromValue(QMediaPlaylist::Linear)); + playbackModeBox->addItem(tr("Loop"), + QVariant::fromValue(QMediaPlaylist::Loop)); + playbackModeBox->addItem(tr("Random"), + QVariant::fromValue(QMediaPlaylist::Random)); + playbackModeBox->addItem(tr("Current Item Once"), + QVariant::fromValue(QMediaPlaylist::CurrentItemOnce)); + playbackModeBox->addItem(tr("Current Item In Loop"), + QVariant::fromValue(QMediaPlaylist::CurrentItemInLoop)); + playbackModeBox->setCurrentIndex(0); + + connect(playbackModeBox, SIGNAL(activated(int)), SLOT(updatePlaybackMode())); + updatePlaybackMode(); + slider = new QSlider(Qt::Horizontal); slider->setRange(0, player->duration() / 1000); @@ -126,12 +142,16 @@ Player::Player(QWidget *parent) else colorButton->setEnabled(false); + QBoxLayout *playlistLayout = new QVBoxLayout; + playlistLayout->addWidget(playlistView); + playlistLayout->addWidget(playbackModeBox); + QBoxLayout *displayLayout = new QHBoxLayout; if (videoWidget) displayLayout->addWidget(videoWidget, 2); else displayLayout->addWidget(coverLabel, 2); - displayLayout->addWidget(playlistView); + displayLayout->addLayout(playlistLayout); QBoxLayout *controlLayout = new QHBoxLayout; controlLayout->setMargin(0); @@ -333,3 +353,9 @@ void Player::showColorDialog() } colorDialog->show(); } + +void Player::updatePlaybackMode() +{ + playlist->setPlaybackMode( + playbackModeBox->itemData(playbackModeBox->currentIndex()).value()); +} diff --git a/demos/multimedia/player/player.h b/demos/multimedia/player/player.h index 1de8b1a..cda3eb9 100644 --- a/demos/multimedia/player/player.h +++ b/demos/multimedia/player/player.h @@ -57,6 +57,7 @@ class QAbstractItemView; class QLabel; class QModelIndex; class QSlider; +class QComboBox; class QMediaPlayer; class QVideoWidget; class PlaylistModel; @@ -87,6 +88,7 @@ private slots: void bufferingProgress(int progress); void showColorDialog(); + void updatePlaybackMode(); private: void setTrackInfo(const QString &info); @@ -97,6 +99,7 @@ private: QVideoWidget *videoWidget; QLabel *coverLabel; QSlider *slider; + QComboBox *playbackModeBox; PlaylistModel *playlistModel; QAbstractItemView *playlistView; QDialog *colorDialog; -- cgit v0.12 From 53af250a5366b4259bfb97a6b3c29c31e0aa10e0 Mon Sep 17 00:00:00 2001 From: Dmytro Poplavskiy Date: Thu, 4 Mar 2010 17:23:22 +1000 Subject: Debug media player status and media state changes. Disabled by default. Reviewed-by: Justin McPherson --- src/multimedia/playback/qmediaplayer.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp index 8056878..9466cad 100644 --- a/src/multimedia/playback/qmediaplayer.cpp +++ b/src/multimedia/playback/qmediaplayer.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -55,6 +56,7 @@ #include #include +//#define DEBUG_PLAYER_STATE QT_BEGIN_HEADER @@ -152,10 +154,16 @@ public: void _q_playlistDestroyed(); }; +#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v))) + void QMediaPlayerPrivate::_q_stateChanged(QMediaPlayer::State ps) { Q_Q(QMediaPlayer); +#ifdef DEBUG_PLAYER_STATE + qDebug() << "State changed:" << ENUM_NAME(QMediaPlayer, "State", ps) << (filterStates ? "(filtered)" : ""); +#endif + if (filterStates) return; @@ -183,6 +191,10 @@ void QMediaPlayerPrivate::_q_mediaStatusChanged(QMediaPlayer::MediaStatus status { Q_Q(QMediaPlayer); +#ifdef DEBUG_PLAYER_STATE + qDebug() << "MediaStatus changed:" << ENUM_NAME(QMediaPlayer, "MediaStatus", status); +#endif + switch (status) { case QMediaPlayer::StalledMedia: case QMediaPlayer::BufferingMedia: @@ -230,8 +242,12 @@ void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media) state = control->state(); - if (state != currentState) + if (state != currentState) { +#ifdef DEBUG_PLAYER_STATE + qDebug() << "State changed:" << ENUM_NAME(QMediaPlayer, "State", state); +#endif emit q_func()->stateChanged(state); + } } void QMediaPlayerPrivate::_q_playlistDestroyed() -- cgit v0.12 From 5a3b9d3daf64ea686427478391d3773c5a1e024e Mon Sep 17 00:00:00 2001 From: Justin McPherson Date: Thu, 4 Mar 2010 17:26:19 +1000 Subject: WaveDecoder; be more permissive in handling of wave file formats. Reviewed-by: Dmytro Poplavskiy --- src/multimedia/effects/wavedecoder_p.cpp | 21 ++++++++++++++++----- src/multimedia/effects/wavedecoder_p.h | 1 - 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/multimedia/effects/wavedecoder_p.cpp b/src/multimedia/effects/wavedecoder_p.cpp index f2277ae..b534ded 100644 --- a/src/multimedia/effects/wavedecoder_p.cpp +++ b/src/multimedia/effects/wavedecoder_p.cpp @@ -55,7 +55,7 @@ WaveDecoder::WaveDecoder(QIODevice *s, QObject *parent): { open(QIODevice::ReadOnly | QIODevice::Unbuffered); - if (source->bytesAvailable() >= sizeof(CombinedHeader)) + if (source->bytesAvailable() >= qint64(sizeof(CombinedHeader) + sizeof(DATAHeader) + sizeof(quint16))) QTimer::singleShot(0, this, SLOT(handleData())); else connect(source, SIGNAL(readyRead()), SLOT(handleData())); @@ -105,7 +105,7 @@ qint64 WaveDecoder::writeData(const char *data, qint64 len) void WaveDecoder::handleData() { - if (source->bytesAvailable() < sizeof(CombinedHeader)) + if (source->bytesAvailable() < qint64(sizeof(CombinedHeader) + sizeof(DATAHeader) + sizeof(quint16))) return; source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); @@ -114,12 +114,23 @@ void WaveDecoder::handleData() if (qstrncmp(header.riff.descriptor.id, "RIFF", 4) != 0 || qstrncmp(header.riff.type, "WAVE", 4) != 0 || qstrncmp(header.wave.descriptor.id, "fmt ", 4) != 0 || - (header.wave.audioFormat != 0 && header.wave.audioFormat != 1) || - qstrncmp(header.data.descriptor.id, "data", 4) != 0) { + (header.wave.audioFormat != 0 && header.wave.audioFormat != 1)) { emit invalidFormat(); } else { + DATAHeader dataHeader; + + if (qFromLittleEndian(header.wave.descriptor.size) > sizeof(WAVEHeader)) { + // Extended data available + quint16 extraFormatBytes; + source->peek((char*)&extraFormatBytes, sizeof(quint16)); + extraFormatBytes = qFromLittleEndian(extraFormatBytes); + source->read(sizeof(quint16) + extraFormatBytes); // dump it all + } + + source->read((char*)&dataHeader, sizeof(DATAHeader)); + int bps = qFromLittleEndian(header.wave.bitsPerSample); format.setCodec(QLatin1String("audio/pcm")); @@ -129,7 +140,7 @@ void WaveDecoder::handleData() format.setSampleSize(bps); format.setChannels(qFromLittleEndian(header.wave.numChannels)); - dataSize = qFromLittleEndian(header.data.descriptor.size); + dataSize = qFromLittleEndian(dataHeader.descriptor.size); haveFormat = true; connect(source, SIGNAL(readyRead()), SIGNAL(readyRead())); diff --git a/src/multimedia/effects/wavedecoder_p.h b/src/multimedia/effects/wavedecoder_p.h index 00aa14e..fa1f77e 100644 --- a/src/multimedia/effects/wavedecoder_p.h +++ b/src/multimedia/effects/wavedecoder_p.h @@ -116,7 +116,6 @@ private: { RIFFHeader riff; WAVEHeader wave; - DATAHeader data; }; bool haveFormat; -- cgit v0.12