qtmobility/examples/player/player.cpp
changeset 4 90517678cc4f
parent 1 2b40d63a9c3d
child 5 453da2cfceef
--- 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