|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Mobility Components. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:BSD$ |
|
10 ** You may use this file under the terms of the BSD license as follows: |
|
11 ** |
|
12 ** "Redistribution and use in source and binary forms, with or without |
|
13 ** modification, are permitted provided that the following conditions are |
|
14 ** met: |
|
15 ** * Redistributions of source code must retain the above copyright |
|
16 ** notice, this list of conditions and the following disclaimer. |
|
17 ** * Redistributions in binary form must reproduce the above copyright |
|
18 ** notice, this list of conditions and the following disclaimer in |
|
19 ** the documentation and/or other materials provided with the |
|
20 ** distribution. |
|
21 ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor |
|
22 ** the names of its contributors may be used to endorse or promote |
|
23 ** products derived from this software without specific prior written |
|
24 ** permission. |
|
25 ** |
|
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
|
37 ** $QT_END_LICENSE$ |
|
38 ** |
|
39 ****************************************************************************/ |
|
40 |
|
41 #include "player.h" |
|
42 |
|
43 #include "playercontrols.h" |
|
44 #include "playlistmodel.h" |
|
45 #include "videowidget.h" |
|
46 |
|
47 #include <qmediaservice.h> |
|
48 #include <qmediaplaylist.h> |
|
49 |
|
50 #include <QtGui> |
|
51 |
|
52 |
|
53 Player::Player(QWidget *parent) |
|
54 : QWidget(parent) |
|
55 , videoWidget(0) |
|
56 , coverLabel(0) |
|
57 , slider(0) |
|
58 , colorDialog(0) |
|
59 { |
|
60 //! [create-objs] |
|
61 player = new QMediaPlayer(this); |
|
62 // owned by PlaylistModel |
|
63 playlist = new QMediaPlaylist(); |
|
64 player->setPlaylist(playlist); |
|
65 //! [create-objs] |
|
66 |
|
67 connect(player, SIGNAL(durationChanged(qint64)), SLOT(durationChanged(qint64))); |
|
68 connect(player, SIGNAL(positionChanged(qint64)), SLOT(positionChanged(qint64))); |
|
69 connect(player, SIGNAL(metaDataChanged()), SLOT(metaDataChanged())); |
|
70 connect(playlist, SIGNAL(currentIndexChanged(int)), SLOT(playlistPositionChanged(int))); |
|
71 connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), |
|
72 this, SLOT(statusChanged(QMediaPlayer::MediaStatus))); |
|
73 connect(player, SIGNAL(bufferStatusChanged(int)), this, SLOT(bufferingProgress(int))); |
|
74 connect(player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(displayErrorMessage())); |
|
75 |
|
76 //! [2] |
|
77 videoWidget = new VideoWidget(this); |
|
78 player->setVideoOutput(videoWidget); |
|
79 |
|
80 playlistModel = new PlaylistModel(this); |
|
81 playlistModel->setPlaylist(playlist); |
|
82 //! [2] |
|
83 |
|
84 playlistView = new QListView(this); |
|
85 playlistView->setModel(playlistModel); |
|
86 playlistView->setCurrentIndex(playlistModel->index(playlist->currentIndex(), 0)); |
|
87 |
|
88 connect(playlistView, SIGNAL(activated(QModelIndex)), this, SLOT(jump(QModelIndex))); |
|
89 |
|
90 slider = new QSlider(Qt::Horizontal, this); |
|
91 slider->setRange(0, player->duration() / 1000); |
|
92 |
|
93 connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(seek(int))); |
|
94 |
|
95 |
|
96 QPushButton *openButton = new QPushButton(tr("Open"), this); |
|
97 |
|
98 connect(openButton, SIGNAL(clicked()), this, SLOT(open())); |
|
99 |
|
100 PlayerControls *controls = new PlayerControls(this); |
|
101 controls->setState(player->state()); |
|
102 controls->setVolume(player->volume()); |
|
103 controls->setMuted(controls->isMuted()); |
|
104 |
|
105 connect(controls, SIGNAL(play()), player, SLOT(play())); |
|
106 connect(controls, SIGNAL(pause()), player, SLOT(pause())); |
|
107 connect(controls, SIGNAL(stop()), player, SLOT(stop())); |
|
108 connect(controls, SIGNAL(next()), playlist, SLOT(next())); |
|
109 connect(controls, SIGNAL(previous()), this, SLOT(previousClicked())); |
|
110 connect(controls, SIGNAL(changeVolume(int)), player, SLOT(setVolume(int))); |
|
111 connect(controls, SIGNAL(changeMuting(bool)), player, SLOT(setMuted(bool))); |
|
112 connect(controls, SIGNAL(changeRate(qreal)), player, SLOT(setPlaybackRate(qreal))); |
|
113 |
|
114 connect(controls, SIGNAL(stop()), videoWidget, SLOT(update())); |
|
115 |
|
116 connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), |
|
117 controls, SLOT(setState(QMediaPlayer::State))); |
|
118 connect(player, SIGNAL(volumeChanged(int)), controls, SLOT(setVolume(int))); |
|
119 connect(player, SIGNAL(mutedChanged(bool)), controls, SLOT(setMuted(bool))); |
|
120 |
|
121 QPushButton *fullScreenButton = new QPushButton(tr("FullScreen"), this); |
|
122 fullScreenButton->setCheckable(true); |
|
123 |
|
124 if (videoWidget != 0) { |
|
125 connect(fullScreenButton, SIGNAL(clicked(bool)), videoWidget, SLOT(setFullScreen(bool))); |
|
126 connect(videoWidget, SIGNAL(fullScreenChanged(bool)), |
|
127 fullScreenButton, SLOT(setChecked(bool))); |
|
128 } else { |
|
129 fullScreenButton->setEnabled(false); |
|
130 } |
|
131 |
|
132 QPushButton *colorButton = new QPushButton(tr("Color Options..."), this); |
|
133 if (videoWidget) |
|
134 connect(colorButton, SIGNAL(clicked()), this, SLOT(showColorDialog())); |
|
135 else |
|
136 colorButton->setEnabled(false); |
|
137 |
|
138 QBoxLayout *displayLayout = new QHBoxLayout; |
|
139 if (videoWidget) |
|
140 displayLayout->addWidget(videoWidget, 2); |
|
141 else |
|
142 displayLayout->addWidget(coverLabel, 2); |
|
143 displayLayout->addWidget(playlistView); |
|
144 |
|
145 QBoxLayout *controlLayout = new QHBoxLayout; |
|
146 controlLayout->setMargin(0); |
|
147 controlLayout->addWidget(openButton); |
|
148 controlLayout->addStretch(1); |
|
149 controlLayout->addWidget(controls); |
|
150 controlLayout->addStretch(1); |
|
151 controlLayout->addWidget(fullScreenButton); |
|
152 controlLayout->addWidget(colorButton); |
|
153 |
|
154 QBoxLayout *layout = new QVBoxLayout; |
|
155 layout->addLayout(displayLayout); |
|
156 layout->addWidget(slider); |
|
157 layout->addLayout(controlLayout); |
|
158 |
|
159 setLayout(layout); |
|
160 |
|
161 if (!player->isAvailable()) { |
|
162 QMessageBox::warning(this, tr("Service not available"), |
|
163 tr("The QMediaPlayer object does not have a valid service.\n"\ |
|
164 "Please check the media service plugins are installed.")); |
|
165 |
|
166 controls->setEnabled(false); |
|
167 playlistView->setEnabled(false); |
|
168 openButton->setEnabled(false); |
|
169 colorButton->setEnabled(false); |
|
170 fullScreenButton->setEnabled(false); |
|
171 } |
|
172 |
|
173 metaDataChanged(); |
|
174 |
|
175 QStringList arguments = qApp->arguments(); |
|
176 arguments.removeAt(0); |
|
177 addToPlaylist(arguments); |
|
178 } |
|
179 |
|
180 Player::~Player() |
|
181 { |
|
182 } |
|
183 |
|
184 void Player::open() |
|
185 { |
|
186 QStringList fileNames = QFileDialog::getOpenFileNames(); |
|
187 addToPlaylist(fileNames); |
|
188 } |
|
189 |
|
190 void Player::addToPlaylist(const QStringList& fileNames) |
|
191 { |
|
192 foreach (QString const &argument, fileNames) { |
|
193 QFileInfo fileInfo(argument); |
|
194 if (fileInfo.exists()) { |
|
195 QUrl url = QUrl::fromLocalFile(fileInfo.absoluteFilePath()); |
|
196 if (fileInfo.suffix().toLower() == QLatin1String("m3u")) { |
|
197 playlist->load(url); |
|
198 } else |
|
199 playlist->addMedia(url); |
|
200 } else { |
|
201 QUrl url(argument); |
|
202 if (url.isValid()) { |
|
203 playlist->addMedia(url); |
|
204 } |
|
205 } |
|
206 } |
|
207 } |
|
208 |
|
209 void Player::durationChanged(qint64 duration) |
|
210 { |
|
211 slider->setMaximum(duration / 1000); |
|
212 } |
|
213 |
|
214 void Player::positionChanged(qint64 progress) |
|
215 { |
|
216 if (!slider->isSliderDown()) { |
|
217 slider->setValue(progress / 1000); |
|
218 } |
|
219 } |
|
220 |
|
221 void Player::metaDataChanged() |
|
222 { |
|
223 //qDebug() << "update metadata" << player->metaData(QtMultimediaKit::Title).toString(); |
|
224 if (player->isMetaDataAvailable()) { |
|
225 setTrackInfo(QString("%1 - %2") |
|
226 .arg(player->metaData(QtMultimediaKit::AlbumArtist).toString()) |
|
227 .arg(player->metaData(QtMultimediaKit::Title).toString())); |
|
228 |
|
229 if (coverLabel) { |
|
230 QUrl url = player->metaData(QtMultimediaKit::CoverArtUrlLarge).value<QUrl>(); |
|
231 |
|
232 coverLabel->setPixmap(!url.isEmpty() |
|
233 ? QPixmap(url.toString()) |
|
234 : QPixmap()); |
|
235 } |
|
236 } |
|
237 } |
|
238 |
|
239 void Player::previousClicked() |
|
240 { |
|
241 // Go to previous track if we are within the first 5 seconds of playback |
|
242 // Otherwise, seek to the beginning. |
|
243 if(player->position() <= 5000) |
|
244 playlist->previous(); |
|
245 else |
|
246 player->setPosition(0); |
|
247 } |
|
248 |
|
249 void Player::jump(const QModelIndex &index) |
|
250 { |
|
251 if (index.isValid()) { |
|
252 playlist->setCurrentIndex(index.row()); |
|
253 player->play(); |
|
254 } |
|
255 } |
|
256 |
|
257 void Player::playlistPositionChanged(int currentItem) |
|
258 { |
|
259 playlistView->setCurrentIndex(playlistModel->index(currentItem, 0)); |
|
260 } |
|
261 |
|
262 void Player::seek(int seconds) |
|
263 { |
|
264 player->setPosition(seconds * 1000); |
|
265 } |
|
266 |
|
267 void Player::statusChanged(QMediaPlayer::MediaStatus status) |
|
268 { |
|
269 handleCursor(status); |
|
270 |
|
271 // handle status message |
|
272 switch (status) { |
|
273 case QMediaPlayer::UnknownMediaStatus: |
|
274 case QMediaPlayer::NoMedia: |
|
275 case QMediaPlayer::LoadedMedia: |
|
276 case QMediaPlayer::BufferingMedia: |
|
277 case QMediaPlayer::BufferedMedia: |
|
278 setStatusInfo(QString()); |
|
279 break; |
|
280 case QMediaPlayer::LoadingMedia: |
|
281 setStatusInfo(tr("Loading...")); |
|
282 break; |
|
283 case QMediaPlayer::StalledMedia: |
|
284 setStatusInfo(tr("Media Stalled")); |
|
285 break; |
|
286 case QMediaPlayer::EndOfMedia: |
|
287 QApplication::alert(this); |
|
288 break; |
|
289 case QMediaPlayer::InvalidMedia: |
|
290 displayErrorMessage(); |
|
291 break; |
|
292 } |
|
293 } |
|
294 |
|
295 void Player::handleCursor(QMediaPlayer::MediaStatus status) |
|
296 { |
|
297 #ifndef QT_NO_CURSOR |
|
298 if( status == QMediaPlayer::LoadingMedia || |
|
299 status == QMediaPlayer::BufferingMedia || |
|
300 status == QMediaPlayer::StalledMedia) |
|
301 setCursor(QCursor(Qt::BusyCursor)); |
|
302 else |
|
303 unsetCursor(); |
|
304 #endif |
|
305 } |
|
306 |
|
307 void Player::bufferingProgress(int progress) |
|
308 { |
|
309 setStatusInfo(tr("Buffering %4%").arg(progress)); |
|
310 } |
|
311 |
|
312 void Player::setTrackInfo(const QString &info) |
|
313 { |
|
314 trackInfo = info; |
|
315 if (!statusInfo.isEmpty()) |
|
316 setWindowTitle(QString("%1 | %2").arg(trackInfo).arg(statusInfo)); |
|
317 else |
|
318 setWindowTitle(trackInfo); |
|
319 } |
|
320 |
|
321 void Player::setStatusInfo(const QString &info) |
|
322 { |
|
323 statusInfo = info; |
|
324 if (!statusInfo.isEmpty()) |
|
325 setWindowTitle(QString("%1 | %2").arg(trackInfo).arg(statusInfo)); |
|
326 else |
|
327 setWindowTitle(trackInfo); |
|
328 } |
|
329 |
|
330 void Player::displayErrorMessage() |
|
331 { |
|
332 setStatusInfo(player->errorString()); |
|
333 |
|
334 |
|
335 } |
|
336 |
|
337 void Player::showColorDialog() |
|
338 { |
|
339 if (!colorDialog) { |
|
340 QSlider *brightnessSlider = new QSlider(Qt::Horizontal); |
|
341 brightnessSlider->setRange(-100, 100); |
|
342 brightnessSlider->setValue(videoWidget->brightness()); |
|
343 connect(brightnessSlider, SIGNAL(sliderMoved(int)), videoWidget, SLOT(setBrightness(int))); |
|
344 connect(videoWidget, SIGNAL(brightnessChanged(int)), brightnessSlider, SLOT(setValue(int))); |
|
345 |
|
346 QSlider *contrastSlider = new QSlider(Qt::Horizontal); |
|
347 contrastSlider->setRange(-100, 100); |
|
348 contrastSlider->setValue(videoWidget->contrast()); |
|
349 connect(contrastSlider, SIGNAL(sliderMoved(int)), videoWidget, SLOT(setContrast(int))); |
|
350 connect(videoWidget, SIGNAL(contrastChanged(int)), contrastSlider, SLOT(setValue(int))); |
|
351 |
|
352 QSlider *hueSlider = new QSlider(Qt::Horizontal); |
|
353 hueSlider->setRange(-100, 100); |
|
354 hueSlider->setValue(videoWidget->hue()); |
|
355 connect(hueSlider, SIGNAL(sliderMoved(int)), videoWidget, SLOT(setHue(int))); |
|
356 connect(videoWidget, SIGNAL(hueChanged(int)), hueSlider, SLOT(setValue(int))); |
|
357 |
|
358 QSlider *saturationSlider = new QSlider(Qt::Horizontal); |
|
359 saturationSlider->setRange(-100, 100); |
|
360 saturationSlider->setValue(videoWidget->saturation()); |
|
361 connect(saturationSlider, SIGNAL(sliderMoved(int)), videoWidget, SLOT(setSaturation(int))); |
|
362 connect(videoWidget, SIGNAL(saturationChanged(int)), saturationSlider, SLOT(setValue(int))); |
|
363 |
|
364 QFormLayout *layout = new QFormLayout; |
|
365 layout->addRow(tr("Brightness"), brightnessSlider); |
|
366 layout->addRow(tr("Contrast"), contrastSlider); |
|
367 layout->addRow(tr("Hue"), hueSlider); |
|
368 layout->addRow(tr("Saturation"), saturationSlider); |
|
369 |
|
370 colorDialog = new QDialog(this); |
|
371 colorDialog->setWindowTitle(tr("Color Options")); |
|
372 colorDialog->setLayout(layout); |
|
373 } |
|
374 colorDialog->show(); |
|
375 } |