--- a/qtmobility/examples/player/player.cpp Fri Apr 16 15:51:22 2010 +0300
+++ b/qtmobility/examples/player/player.cpp Mon May 03 13:18:40 2010 +0300
@@ -50,20 +50,34 @@
#include <QtGui>
+#ifdef Q_OS_SYMBIAN
+#include <QtGui/QDialog>
+#include <QtGui/QLineEdit>
+#include <QtGui/QListWidget>
+#include <QtNetwork/QHttp>
+#include <QDomDocument>
+
+#include "mediakeysobserver.h"
+#endif
+
Player::Player(QWidget *parent)
: QWidget(parent)
, videoWidget(0)
, coverLabel(0)
, slider(0)
-#ifdef Q_OS_SYMBIAN
+#ifdef Q_OS_SYMBIAN
, mediaKeysObserver(0)
, playlistDialog(0)
+ , toggleAspectRatio(0)
+ , showYoutubeDialog(0)
+ , youtubeDialog(0)
#else
, colorDialog(0)
-#endif
+#endif
{
player = new QMediaPlayer(this);
- playlist = new QMediaPlaylist(this);
+ // owerd by PlaylistModel
+ playlist = new QMediaPlaylist();
playlist->setMediaObject(player);
connect(player, SIGNAL(durationChanged(qint64)), SLOT(durationChanged(qint64)));
@@ -73,31 +87,31 @@
connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
this, SLOT(statusChanged(QMediaPlayer::MediaStatus)));
connect(player, SIGNAL(bufferStatusChanged(int)), this, SLOT(bufferingProgress(int)));
+ connect(player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(displayErrorMessage()));
- videoWidget = new VideoWidget;
+ videoWidget = new VideoWidget(this);
videoWidget->setMediaObject(player);
playlistModel = new PlaylistModel(this);
playlistModel->setPlaylist(playlist);
- playlistView = new QListView;
+ playlistView = new QListView(this);
playlistView->setModel(playlistModel);
playlistView->setCurrentIndex(playlistModel->index(playlist->currentIndex(), 0));
connect(playlistView, SIGNAL(activated(QModelIndex)), this, SLOT(jump(QModelIndex)));
- slider = new QSlider(Qt::Horizontal);
+ slider = new QSlider(Qt::Horizontal, this);
slider->setRange(0, player->duration() / 1000);
connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(seek(int)));
-#ifdef Q_OS_SYMBIAN
-#else
+#ifndef Q_OS_SYMBIAN
QPushButton *openButton = new QPushButton(tr("Open"), this);
connect(openButton, SIGNAL(clicked()), this, SLOT(open()));
+#endif
-#endif
PlayerControls *controls = new PlayerControls(this);
controls->setState(player->state());
controls->setVolume(player->volume());
@@ -107,18 +121,19 @@
connect(controls, SIGNAL(pause()), player, SLOT(pause()));
connect(controls, SIGNAL(stop()), player, SLOT(stop()));
connect(controls, SIGNAL(next()), playlist, SLOT(next()));
- connect(controls, SIGNAL(previous()), playlist, SLOT(previous()));
+ connect(controls, SIGNAL(previous()), this, SLOT(previousClicked()));
connect(controls, SIGNAL(changeVolume(int)), player, SLOT(setVolume(int)));
connect(controls, SIGNAL(changeMuting(bool)), player, SLOT(setMuted(bool)));
connect(controls, SIGNAL(changeRate(qreal)), player, SLOT(setPlaybackRate(qreal)));
+ connect(controls, SIGNAL(stop()), videoWidget, SLOT(update()));
+
connect(player, SIGNAL(stateChanged(QMediaPlayer::State)),
controls, SLOT(setState(QMediaPlayer::State)));
connect(player, SIGNAL(volumeChanged(int)), controls, SLOT(setVolume(int)));
connect(player, SIGNAL(mutedChanged(bool)), controls, SLOT(setMuted(bool)));
-#ifdef Q_OS_SYMBIAN
-#else
+#ifndef Q_OS_SYMBIAN
QPushButton *fullScreenButton = new QPushButton(tr("FullScreen"), this);
fullScreenButton->setCheckable(true);
@@ -130,18 +145,18 @@
fullScreenButton->setEnabled(false);
}
- QPushButton *colorButton = new QPushButton(tr("Color Options..."));
+ QPushButton *colorButton = new QPushButton(tr("Color Options..."), this);
if (videoWidget)
connect(colorButton, SIGNAL(clicked()), this, SLOT(showColorDialog()));
else
colorButton->setEnabled(false);
#endif
-
+
#ifdef Q_OS_SYMBIAN
// Set some sensible default volume.
player->setVolume(50);
-
+
QLabel *label = new QLabel(tr("Playlist"), this);
QVBoxLayout *playlistDialogLayout = new QVBoxLayout;
playlistDialogLayout->addWidget(label);
@@ -150,28 +165,36 @@
playlistDialog->setWindowTitle(tr("Playlist"));
playlistDialog->setLayout(playlistDialogLayout);
playlistDialog->setContextMenuPolicy(Qt::NoContextMenu);
-
+
QAction *close = new QAction(tr("Close"), this);
close->setSoftKeyRole(QAction::NegativeSoftKey);
playlistDialog->addAction(close);
-
+
mediaKeysObserver = new MediaKeysObserver(this);
-
+
+ coverLabel = new QLabel(this);
+ coverLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ coverLabel->setMinimumSize(1, 1);
+ coverLabel->hide();
+
slider->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
slider->setMinimumSize(1, 1);
-
+
connect(controls, SIGNAL(open()), this, SLOT(open()));
connect(controls, SIGNAL(fullScreen(bool)), this, SLOT(handleFullScreen(bool)));
- connect(videoWidget, SIGNAL(fullScreenChanged(bool)), this, SLOT(handleFullScreen(bool)));
connect(controls, SIGNAL(openPlayList()), this, SLOT(showPlayList()));
connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(handleStateChange(QMediaPlayer::State)));
connect(mediaKeysObserver, SIGNAL(mediaKeyPressed(MediaKeysObserver::MediaKeys)), this, SLOT(handleMediaKeyEvent(MediaKeysObserver::MediaKeys)));
connect(close, SIGNAL(triggered()), playlistDialog, SLOT(reject()));
-
+
QBoxLayout *layout = new QVBoxLayout;
+ layout->setMargin(0);
layout->addWidget(videoWidget, 7);
+ layout->addWidget(coverLabel, 7);
layout->addWidget(slider, 1);
layout->addWidget(controls, 2);
+
+ createMenus();
#else
QBoxLayout *displayLayout = new QHBoxLayout;
if (videoWidget)
@@ -193,7 +216,7 @@
layout->addLayout(displayLayout);
layout->addWidget(slider);
layout->addLayout(controlLayout);
-#endif
+#endif
setLayout(layout);
@@ -211,12 +234,6 @@
Player::~Player()
{
-#ifdef Q_OS_SYMBIAN
- delete playlistDialog;
-#else
- delete playlist;
-#endif
- delete player;
}
void Player::open()
@@ -233,11 +250,58 @@
void Player::positionChanged(qint64 progress)
{
- slider->setValue(progress / 1000);
+ if (!slider->isSliderDown()) {
+ slider->setValue(progress / 1000);
+ }
}
void Player::metaDataChanged()
{
+#ifdef Q_OS_SYMBIAN
+ if (player->isMetaDataAvailable()) {
+ setTrackInfo(QString("(%1/%2) %3 - %4")
+ .arg(playlist->currentIndex()+1)
+ .arg(playlist->mediaCount())
+ .arg(player->metaData(QtMedia::AlbumArtist).toString())
+ .arg(player->metaData(QtMedia::Title).toString()));
+
+ if (!player->isVideoAvailable()) {
+ QUrl uri = player->metaData(QtMedia::CoverArtUrlLarge).value<QUrl>();
+ QPixmap pixmap = NULL;
+
+ if (uri.isEmpty()) {
+ QVariant picture = player->extendedMetaData("attachedpicture");
+ // Load picture from metadata
+ if (!picture.isNull() && picture.canConvert<QByteArray>())
+ pixmap.loadFromData(picture.value<QByteArray>());
+
+ // Load some jpg from same dir as media
+ else {
+ QUrl url = player->media().canonicalUrl();
+ QString path = url.path();
+ path = path.mid(1,path.lastIndexOf("/"));
+ QRegExp rx("*.jpg");
+ rx.setCaseSensitivity(Qt::CaseInsensitive);
+ rx.setPatternSyntax(QRegExp::Wildcard);
+ QDir directory(path);
+ QStringList allFiles = directory.entryList(QDir::Files | QDir::NoSymLinks);
+
+ foreach (QString file, allFiles)
+ if (rx.exactMatch(file)) {
+ path.append(file);
+ break;
+ }
+ pixmap.load(path);
+ }
+ // Load picture from file pointed by uri
+ } else
+ pixmap.load(uri.toString());
+
+ coverLabel->setPixmap((!pixmap.isNull())?pixmap:QPixmap());
+ }
+ hideOrShowCoverArt();
+ }
+#else
//qDebug() << "update metadata" << player->metaData(QtMedia::Title).toString();
if (player->isMetaDataAvailable()) {
setTrackInfo(QString("%1 - %2")
@@ -252,6 +316,17 @@
: QPixmap());
}
}
+#endif
+}
+
+void Player::previousClicked()
+{
+ // Go to previous track if we are within the first 5 seconds of playback
+ // Otherwise, seek to the beginning.
+ if(player->position() <= 5000)
+ playlist->previous();
+ else
+ player->setPosition(0);
}
void Player::jump(const QModelIndex &index)
@@ -278,42 +353,42 @@
void Player::statusChanged(QMediaPlayer::MediaStatus status)
{
+ handleCursor(status);
+
+ // handle status message
switch (status) {
case QMediaPlayer::UnknownMediaStatus:
case QMediaPlayer::NoMedia:
case QMediaPlayer::LoadedMedia:
case QMediaPlayer::BufferingMedia:
case QMediaPlayer::BufferedMedia:
-#ifndef QT_NO_CURSOR
- unsetCursor();
-#endif
setStatusInfo(QString());
break;
case QMediaPlayer::LoadingMedia:
-#ifndef QT_NO_CURSOR
- setCursor(QCursor(Qt::BusyCursor));
-#endif
setStatusInfo(tr("Loading..."));
break;
case QMediaPlayer::StalledMedia:
-#ifndef QT_NO_CURSOR
- setCursor(QCursor(Qt::BusyCursor));
-#endif
+ setStatusInfo(tr("Media Stalled"));
break;
case QMediaPlayer::EndOfMedia:
-#ifndef QT_NO_CURSOR
- unsetCursor();
-#endif
- setStatusInfo(QString());
QApplication::alert(this);
break;
case QMediaPlayer::InvalidMedia:
+ displayErrorMessage();
+ break;
+ }
+}
+
+void Player::handleCursor(QMediaPlayer::MediaStatus status)
+{
#ifndef QT_NO_CURSOR
+ if( status == QMediaPlayer::LoadingMedia ||
+ status == QMediaPlayer::BufferingMedia ||
+ status == QMediaPlayer::StalledMedia)
+ setCursor(QCursor(Qt::BusyCursor));
+ else
unsetCursor();
#endif
- setStatusInfo(player->errorString());
- break;
- }
}
void Player::bufferingProgress(int progress)
@@ -324,25 +399,50 @@
void Player::setTrackInfo(const QString &info)
{
trackInfo = info;
-
+#ifdef Q_OS_SYMBIAN
+ QMainWindow *main = qobject_cast<QMainWindow *>(this->parent());
+ if (!statusInfo.isEmpty())
+ main->setWindowTitle(QString("%1 | %2").arg(trackInfo).arg(statusInfo));
+ else
+ main->setWindowTitle(trackInfo);
+#else
if (!statusInfo.isEmpty())
setWindowTitle(QString("%1 | %2").arg(trackInfo).arg(statusInfo));
else
setWindowTitle(trackInfo);
-
+#endif
}
void Player::setStatusInfo(const QString &info)
{
statusInfo = info;
-
+#ifdef Q_OS_SYMBIAN
+ QMainWindow *main = qobject_cast<QMainWindow *>(this->parent());
+ if (!statusInfo.isEmpty())
+ main->setWindowTitle(QString("%1 | %2").arg(trackInfo).arg(statusInfo));
+ else
+ main->setWindowTitle(trackInfo);
+#else
if (!statusInfo.isEmpty())
setWindowTitle(QString("%1 | %2").arg(trackInfo).arg(statusInfo));
else
setWindowTitle(trackInfo);
+#endif
}
+
+void Player::displayErrorMessage()
+{
#ifdef Q_OS_SYMBIAN
+ if(player->error()!=QMediaPlayer::NoError)
+ QMessageBox::critical(NULL, tr("Error"), player->errorString(), QMessageBox::Ok);
#else
+ setStatusInfo(player->errorString());
+#endif
+
+
+}
+
+#ifndef Q_OS_SYMBIAN
void Player::showColorDialog()
{
if (!colorDialog) {
@@ -384,32 +484,68 @@
}
#endif
#ifdef Q_OS_SYMBIAN
+void Player::createMenus()
+{
+ toggleAspectRatio = new QAction(tr("Ignore Aspect Ratio"), this);
+ toggleAspectRatio->setCheckable(true);
+ qobject_cast<QMainWindow *>(this->parent())->menuBar()->addAction(toggleAspectRatio);
+ connect(toggleAspectRatio, SIGNAL(toggled(bool)), this, SLOT(handleAspectRatio(bool)));
+
+ showYoutubeDialog = new QAction(tr("Youtube Search"), this);
+ qobject_cast<QMainWindow *>(this->parent())->menuBar()->addAction(showYoutubeDialog);
+ connect(showYoutubeDialog, SIGNAL(triggered()), this, SLOT(launchYoutubeDialog()));
+}
+
void Player::handleFullScreen(bool isFullscreen)
{
+ QMainWindow* mainWindow = qobject_cast<QMainWindow *>(this->parent());
if(isFullscreen) {
- showFullScreen();
- if(player->state()==QMediaPlayer::PlayingState ||
- player->state()==QMediaPlayer::PausedState)
+ if(player->state()==QMediaPlayer::StoppedState)
+ videoWidget->setFullScreen(false);
+ else
videoWidget->setFullScreen(true);
- else
- videoWidget->setFullScreen(false);
-
+
+ qobject_cast<QMainWindow *>(this->parent())->showFullScreen();
} else
- showMaximized();
+ qobject_cast<QMainWindow *>(this->parent())->showMaximized();
+}
+
+void Player::handleAspectRatio(bool aspectRatio)
+{
+ if(aspectRatio) {
+ toggleAspectRatio->setText(tr("Keep Aspect Ratio"));
+ videoWidget->setAspectRatioMode(Qt::IgnoreAspectRatio);
+
+ } else {
+ toggleAspectRatio->setText(tr("Ignore Aspect Ratio"));
+ videoWidget->setAspectRatioMode(Qt::KeepAspectRatio);
+ }
+}
+
+void Player::hideOrShowCoverArt()
+{
+ if(player->isVideoAvailable()) {
+ coverLabel->hide();
+ videoWidget->show();
+ videoWidget->repaint();
+ } else {
+ coverLabel->show();
+ videoWidget->hide();
+ }
}
void Player::handleStateChange(QMediaPlayer::State state)
{
if (state == QMediaPlayer::PausedState)
return;
-
- handleFullScreen(isFullScreen());
+
+ handleFullScreen(qobject_cast<QMainWindow *>(this->parent())->isFullScreen());
}
void Player::handleMediaKeyEvent(MediaKeysObserver::MediaKeys key)
{
switch (key) {
- case MediaKeysObserver::EVolIncKey:
+ case MediaKeysObserver::EVolIncKey:
player->setVolume(player->volume() + 10);
break;
case MediaKeysObserver::EVolDecKey:
@@ -423,7 +559,139 @@
{
if (!playlistDialog)
return;
-
+
playlistDialog->exec();
}
+
+void Player::launchYoutubeDialog()
+{
+ if(!youtubeDialog) {
+ youtubeDialog = new QDialog(this);
+
+ QLineEdit *input= new QLineEdit(youtubeDialog);
+ QPushButton *searchButton = new QPushButton("Search", youtubeDialog);
+ QListWidget *resultList = new QListWidget(youtubeDialog);
+ QAction *add = new QAction(tr("Add"), youtubeDialog);
+ QAction *close = new QAction(tr("Close"), youtubeDialog);
+
+ add->setSoftKeyRole(QAction::PositiveSoftKey);
+ close->setSoftKeyRole(QAction::NegativeSoftKey);
+ youtubeDialog->addAction(add);
+ youtubeDialog->addAction(close);
+
+ connect(searchButton, SIGNAL(clicked()), this, SLOT(searchYoutubeVideo()));
+ connect(add, SIGNAL(triggered()), this, SLOT(addYoutubeVideo()));
+ connect(close, SIGNAL(triggered()), youtubeDialog, SLOT(close()));
+ connect(&http, SIGNAL(requestFinished(int, bool)), this, SLOT(youtubeHttpRequestFinished(int, bool)));
+ connect(&http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)), this, SLOT(youtubeReadResponseHeader(const QHttpResponseHeader&)));
+
+ QHBoxLayout *topLayout = new QHBoxLayout;
+ topLayout->addWidget(input);
+ topLayout->addWidget(searchButton);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->addLayout(topLayout);
+ mainLayout->addWidget(resultList);
+ youtubeDialog->setLayout(mainLayout);
+ }
+ youtubeDialog->showMaximized();
+}
+
+void Player::youtubeReadResponseHeader(const QHttpResponseHeader& responseHeader)
+{
+ switch (responseHeader.statusCode())
+ {
+ case 200: // Ok
+ case 301: // Moved Permanently
+ case 302: // Found
+ case 303: // See Other
+ case 307: // Temporary Redirect
+ // these are not error conditions
+ break;
+ default: {
+ http.abort();
+ QMessageBox::critical(NULL, tr("Error"), tr("Download failed: %1.").arg(responseHeader.reasonPhrase()));
+ break;
+ }
+ }
+}
+
+void Player::addYoutubeVideo()
+{
+ if(!youtubeDialog)
+ return;
+
+ QListWidget *resultList = youtubeDialog->findChild<QListWidget *>();
+ if(!resultList || resultList->count() == 0)
+ return;
+
+ playlist->addMedia(resultList->currentItem()->data(Qt::UserRole).toUrl());
+}
+
+void Player::searchYoutubeVideo()
+{
+ if(!youtubeDialog)
+ return;
+
+ QLineEdit *input = youtubeDialog->findChild<QLineEdit *>();
+ QListWidget *resultList = youtubeDialog->findChild<QListWidget *>();
+ QString urlstring = QString("http://gdata.youtube.com/feeds/api/videos?q=%1&max-results=25&v=2&format=6").arg(input->text().replace(' ', '+'));
+ QUrl url(urlstring);
+ http.setHost(url.host(), QHttp::ConnectionModeHttp, url.port() == -1 ? 0 : url.port());
+ resultList->clear();
+ httpGetId = http.get(urlstring);
+}
+
+void Player::youtubeHttpRequestFinished(int requestId, bool error)
+{
+ if(!youtubeDialog || requestId != httpGetId)
+ return;
+
+ if (error) {
+ QMessageBox::critical(NULL, tr("Error"), tr("Download failed: %1.").arg(http.errorString()));
+ return;
+ }
+
+ QTemporaryFile file;
+ if (!file.open()) {
+ QMessageBox::critical(NULL, tr("Error"), tr("Could not open temporary file"));
+ return;
+ }
+
+ QString data = http.readAll();
+ QTextStream out(&file);
+ out << data;
+ file.close();
+
+ QDomDocument domDocument;
+ QString errorMessage;
+ if (!domDocument.setContent(&file, true, &errorMessage)) {
+ QMessageBox::critical(NULL, tr("Error"), errorMessage);
+ return;
+ }
+
+ QDomElement root = domDocument.documentElement();
+ if (root.tagName() != "feed")
+ return;
+
+ QListWidget *resultList = youtubeDialog->findChild<QListWidget *>();
+ QDomElement entryElement = root.firstChildElement("entry");
+ while(!entryElement.isNull())
+ {
+ QString title = entryElement.firstChildElement("title").text();
+ QDomElement groupElement = entryElement.firstChildElement("group");
+ QDomElement incidentElement2 = groupElement.firstChildElement("content");
+ while(!incidentElement2.isNull())
+ {
+ // "6" = MPEG-4 SP video (up to 176x144) and AAC audio.
+ if (incidentElement2.attribute("format") == "6") {
+ QListWidgetItem* item = new QListWidgetItem(title, resultList);
+ item->setData(Qt::UserRole, incidentElement2.attribute("url"));
+ break;
+ }
+ incidentElement2 = incidentElement2.nextSiblingElement("content");
+ }
+ entryElement = entryElement.nextSiblingElement("entry");
+ }
+}
#endif