1 /*
2 Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2009 Apple Inc. All rights reserved.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19 */
20
21 #include "config.h"
22 #include "MediaPlayerPrivatePhonon.h"
23
24 #include <limits>
25
26 #include "FrameView.h"
27 #include "GraphicsContext.h"
28 #include "Logging.h"
29 #include "MIMETypeRegistry.h"
30 #include "NotImplemented.h"
31 #include "TimeRanges.h"
32 #include "Widget.h"
33 #include <wtf/HashSet.h>
34 #include <wtf/text/CString.h>
35
36 #include <QDebug>
37 #include <QEvent>
38 #include <QMetaEnum>
39 #include <QPainter>
40 #include <QWidget>
41 #include <QUrl>
42
43 #include <phonon/audiooutput.h>
44 #include <phonon/backendcapabilities.h>
45 #include <phonon/path.h>
46 #include <phonon/mediaobject.h>
47 #include <phonon/videowidget.h>
48
49 using namespace Phonon;
50
51 #define LOG_MEDIAOBJECT() (LOG(Media, "%s", debugMediaObject(this, *m_mediaObject).constData()))
52
53 #if !LOG_DISABLED
debugMediaObject(WebCore::MediaPlayerPrivatePhonon * mediaPlayer,const MediaObject & mediaObject)54 static QByteArray debugMediaObject(WebCore::MediaPlayerPrivatePhonon* mediaPlayer, const MediaObject& mediaObject)
55 {
56 QByteArray byteArray;
57 QTextStream stream(&byteArray);
58
59 const QMetaObject* metaObj = mediaPlayer->metaObject();
60 QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState"));
61
62 stream << "debugMediaObject -> Phonon::MediaObject(";
63 stream << "State: " << phononStates.valueToKey(mediaObject.state());
64 stream << " | Current time: " << mediaObject.currentTime();
65 stream << " | Remaining time: " << mediaObject.remainingTime();
66 stream << " | Total time: " << mediaObject.totalTime();
67 stream << " | Meta-data: ";
68 QMultiMap<QString, QString> map = mediaObject.metaData();
69 for (QMap<QString, QString>::const_iterator it = map.constBegin();
70 it != map.constEnd(); ++it) {
71 stream << "(" << it.key() << ", " << it.value() << ")";
72 }
73 stream << " | Has video: " << mediaObject.hasVideo();
74 stream << " | Is seekable: " << mediaObject.isSeekable();
75 stream << ")";
76
77 stream.flush();
78
79 return byteArray;
80 }
81 #endif
82
83 using namespace WTF;
84
85 namespace WebCore {
86
MediaPlayerPrivatePhonon(MediaPlayer * player)87 MediaPlayerPrivatePhonon::MediaPlayerPrivatePhonon(MediaPlayer* player)
88 : m_player(player)
89 , m_networkState(MediaPlayer::Empty)
90 , m_readyState(MediaPlayer::HaveNothing)
91 , m_mediaObject(new MediaObject())
92 , m_videoWidget(new VideoWidget(0))
93 , m_audioOutput(new AudioOutput())
94 , m_isVisible(false)
95 {
96 // Hint to Phonon to disable overlay painting
97 m_videoWidget->setAttribute(Qt::WA_DontShowOnScreen);
98 m_videoWidget->setAttribute(Qt::WA_QuitOnClose, false);
99
100 createPath(m_mediaObject, m_videoWidget);
101 createPath(m_mediaObject, m_audioOutput);
102
103 // Make sure we get updates for each frame
104 m_videoWidget->installEventFilter(this);
105 foreach (QWidget* widget, m_videoWidget->findChildren<QWidget*>())
106 widget->installEventFilter(this);
107
108 connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
109 this, SLOT(stateChanged(Phonon::State,Phonon::State)));
110 connect(m_mediaObject, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
111 connect(m_mediaObject, SIGNAL(seekableChanged(bool)), this, SLOT(seekableChanged(bool)));
112 connect(m_mediaObject, SIGNAL(hasVideoChanged(bool)), this, SLOT(hasVideoChanged(bool)));
113 connect(m_mediaObject, SIGNAL(bufferStatus(int)), this, SLOT(bufferStatus(int)));
114 connect(m_mediaObject, SIGNAL(finished()), this, SLOT(finished()));
115 connect(m_mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
116 this, SLOT(currentSourceChanged(Phonon::MediaSource)));
117 connect(m_mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish()));
118 connect(m_mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
119 }
120
create(MediaPlayer * player)121 MediaPlayerPrivateInterface* MediaPlayerPrivatePhonon::create(MediaPlayer* player)
122 {
123 return new MediaPlayerPrivatePhonon(player);
124 }
125
registerMediaEngine(MediaEngineRegistrar registrar)126 void MediaPlayerPrivatePhonon::registerMediaEngine(MediaEngineRegistrar registrar)
127 {
128 if (isAvailable())
129 registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
130 }
131
132
~MediaPlayerPrivatePhonon()133 MediaPlayerPrivatePhonon::~MediaPlayerPrivatePhonon()
134 {
135 LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting videowidget");
136 m_videoWidget->close();
137 delete m_videoWidget;
138 m_videoWidget = 0;
139
140 LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting audiooutput");
141 delete m_audioOutput;
142 m_audioOutput = 0;
143
144 LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting mediaobject");
145 delete m_mediaObject;
146 m_mediaObject = 0;
147 }
148
supportedTypesCache()149 HashSet<String>& MediaPlayerPrivatePhonon::supportedTypesCache()
150 {
151 static HashSet<String> supportedTypes;
152 if (!supportedTypes.isEmpty())
153 return supportedTypes;
154
155 // FIXME: we should rebuild the MIME type cache every time the backend is changed,
156 // however, this would have no effect on MIMETypeRegistry anyway, because it
157 // pulls this data only once.
158
159 QStringList types = Phonon::BackendCapabilities::availableMimeTypes();
160 foreach (const QString& type, types) {
161 QString first = type.split(QLatin1Char('/')).at(0);
162
163 // We're only interested in types which are not supported by WebCore itself.
164 if (first != QLatin1String("video")
165 && first != QLatin1String("audio")
166 && first != QLatin1String("application"))
167 continue;
168 if (MIMETypeRegistry::isSupportedNonImageMIMEType(type))
169 continue;
170
171 supportedTypes.add(String(type));
172 }
173
174 // These formats are supported by GStreamer, but not correctly advertised.
175 if (supportedTypes.contains(String("video/x-h264"))
176 || supportedTypes.contains(String("audio/x-m4a"))) {
177 supportedTypes.add(String("video/mp4"));
178 supportedTypes.add(String("audio/aac"));
179 }
180
181 if (supportedTypes.contains(String("video/x-theora")))
182 supportedTypes.add(String("video/ogg"));
183
184 if (supportedTypes.contains(String("audio/x-vorbis")))
185 supportedTypes.add(String("audio/ogg"));
186
187 if (supportedTypes.contains(String("audio/x-wav")))
188 supportedTypes.add(String("audio/wav"));
189
190 return supportedTypes;
191 }
192
getSupportedTypes(HashSet<String> & types)193 void MediaPlayerPrivatePhonon::getSupportedTypes(HashSet<String>& types)
194 {
195 types = supportedTypesCache();
196 }
197
supportsType(const String & type,const String & codecs)198 MediaPlayer::SupportsType MediaPlayerPrivatePhonon::supportsType(const String& type, const String& codecs)
199 {
200 if (type.isEmpty())
201 return MediaPlayer::IsNotSupported;
202
203 if (supportedTypesCache().contains(type))
204 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
205 return MediaPlayer::IsNotSupported;
206 }
207
hasVideo() const208 bool MediaPlayerPrivatePhonon::hasVideo() const
209 {
210 bool hasVideo = m_mediaObject->hasVideo();
211 LOG(Media, "MediaPlayerPrivatePhonon::hasVideo() -> %s", hasVideo ? "true" : "false");
212 return hasVideo;
213 }
214
hasAudio() const215 bool MediaPlayerPrivatePhonon::hasAudio() const
216 {
217 // FIXME: Phonon::MediaObject does not have such a hasAudio() function
218 bool hasAudio = true;
219 LOG(Media, "MediaPlayerPrivatePhonon::hasAudio() -> %s", hasAudio ? "true" : "false");
220 return hasAudio;
221 }
222
load(const String & url)223 void MediaPlayerPrivatePhonon::load(const String& url)
224 {
225 LOG(Media, "MediaPlayerPrivatePhonon::load(\"%s\")", url.utf8().data());
226
227 // We are now loading
228 if (m_networkState != MediaPlayer::Loading) {
229 m_networkState = MediaPlayer::Loading;
230 m_player->networkStateChanged();
231 }
232
233 // And we don't have any data yet
234 if (m_readyState != MediaPlayer::HaveNothing) {
235 m_readyState = MediaPlayer::HaveNothing;
236 m_player->readyStateChanged();
237 }
238
239 m_mediaObject->setCurrentSource(QUrl(url));
240 m_audioOutput->setVolume(m_player->volume());
241 setVisible(m_player->visible());
242 }
243
cancelLoad()244 void MediaPlayerPrivatePhonon::cancelLoad()
245 {
246 notImplemented();
247 }
248
249
play()250 void MediaPlayerPrivatePhonon::play()
251 {
252 LOG(Media, "MediaPlayerPrivatePhonon::play()");
253 m_mediaObject->play();
254 }
255
pause()256 void MediaPlayerPrivatePhonon::pause()
257 {
258 LOG(Media, "MediaPlayerPrivatePhonon::pause()");
259 m_mediaObject->pause();
260 }
261
262
paused() const263 bool MediaPlayerPrivatePhonon::paused() const
264 {
265 bool paused = m_mediaObject->state() == Phonon::PausedState;
266 LOG(Media, "MediaPlayerPrivatePhonon::paused() --> %s", paused ? "true" : "false");
267 return paused;
268 }
269
seek(float position)270 void MediaPlayerPrivatePhonon::seek(float position)
271 {
272 LOG(Media, "MediaPlayerPrivatePhonon::seek(%f)", position);
273
274 if (!m_mediaObject->isSeekable())
275 return;
276
277 if (position > duration())
278 position = duration();
279
280 m_mediaObject->seek(position * 1000.0f);
281 }
282
seeking() const283 bool MediaPlayerPrivatePhonon::seeking() const
284 {
285 return false;
286 }
287
duration() const288 float MediaPlayerPrivatePhonon::duration() const
289 {
290 if (m_readyState < MediaPlayer::HaveMetadata)
291 return 0.0f;
292
293 float duration = m_mediaObject->totalTime() / 1000.0f;
294
295 if (duration == 0.0f) // We are streaming
296 duration = std::numeric_limits<float>::infinity();
297
298 LOG(Media, "MediaPlayerPrivatePhonon::duration() --> %f", duration);
299 return duration;
300 }
301
currentTime() const302 float MediaPlayerPrivatePhonon::currentTime() const
303 {
304 float currentTime = m_mediaObject->currentTime() / 1000.0f;
305
306 LOG(Media, "MediaPlayerPrivatePhonon::currentTime() --> %f", currentTime);
307 return currentTime;
308 }
309
buffered() const310 PassRefPtr<TimeRanges> MediaPlayerPrivatePhonon::buffered() const
311 {
312 notImplemented();
313 return TimeRanges::create();
314 }
315
maxTimeSeekable() const316 float MediaPlayerPrivatePhonon::maxTimeSeekable() const
317 {
318 notImplemented();
319 return 0.0f;
320 }
321
bytesLoaded() const322 unsigned MediaPlayerPrivatePhonon::bytesLoaded() const
323 {
324 notImplemented();
325 return 0;
326 }
327
totalBytes() const328 unsigned MediaPlayerPrivatePhonon::totalBytes() const
329 {
330 //notImplemented();
331 return 0;
332 }
333
setRate(float)334 void MediaPlayerPrivatePhonon::setRate(float)
335 {
336 notImplemented();
337 }
338
setVolume(float volume)339 void MediaPlayerPrivatePhonon::setVolume(float volume)
340 {
341 LOG(Media, "MediaPlayerPrivatePhonon::setVolume()");
342 m_audioOutput->setVolume(volume);
343 }
344
setMuted(bool muted)345 void MediaPlayerPrivatePhonon::setMuted(bool muted)
346 {
347 LOG(Media, "MediaPlayerPrivatePhonon::setMuted()");
348 m_audioOutput->setMuted(muted);
349 }
350
networkState() const351 MediaPlayer::NetworkState MediaPlayerPrivatePhonon::networkState() const
352 {
353 const QMetaObject* metaObj = this->metaObject();
354 QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState"));
355 LOG(Media, "MediaPlayerPrivatePhonon::networkState() --> %s", networkStates.valueToKey(m_networkState));
356 return m_networkState;
357 }
358
readyState() const359 MediaPlayer::ReadyState MediaPlayerPrivatePhonon::readyState() const
360 {
361 const QMetaObject* metaObj = this->metaObject();
362 QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState"));
363 LOG(Media, "MediaPlayerPrivatePhonon::readyState() --> %s", readyStates.valueToKey(m_readyState));
364 return m_readyState;
365 }
366
updateStates()367 void MediaPlayerPrivatePhonon::updateStates()
368 {
369 MediaPlayer::NetworkState oldNetworkState = m_networkState;
370 MediaPlayer::ReadyState oldReadyState = m_readyState;
371
372 Phonon::State phononState = m_mediaObject->state();
373
374 if (phononState == Phonon::StoppedState) {
375 if (m_readyState < MediaPlayer::HaveMetadata) {
376 m_networkState = MediaPlayer::Loading; // FIXME: should this be MediaPlayer::Idle?
377 m_readyState = MediaPlayer::HaveMetadata;
378 m_mediaObject->pause();
379 }
380 } else if (phononState == Phonon::PausedState) {
381 m_networkState = MediaPlayer::Loaded;
382 m_readyState = MediaPlayer::HaveEnoughData;
383 } else if (phononState == Phonon::ErrorState) {
384 if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) {
385 // FIXME: is it possile to differentiate between different types of errors
386 m_networkState = MediaPlayer::NetworkError;
387 m_readyState = MediaPlayer::HaveNothing;
388 cancelLoad();
389 } else
390 m_mediaObject->pause();
391 }
392
393 if (seeking())
394 m_readyState = MediaPlayer::HaveNothing;
395
396 if (m_networkState != oldNetworkState) {
397 const QMetaObject* metaObj = this->metaObject();
398 QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState"));
399 LOG(Media, "Network state changed from '%s' to '%s'",
400 networkStates.valueToKey(oldNetworkState),
401 networkStates.valueToKey(m_networkState));
402 m_player->networkStateChanged();
403 }
404
405 if (m_readyState != oldReadyState) {
406 const QMetaObject* metaObj = this->metaObject();
407 QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState"));
408 LOG(Media, "Ready state changed from '%s' to '%s'",
409 readyStates.valueToKey(oldReadyState),
410 readyStates.valueToKey(m_readyState));
411 m_player->readyStateChanged();
412 }
413 }
414
setVisible(bool visible)415 void MediaPlayerPrivatePhonon::setVisible(bool visible)
416 {
417 m_isVisible = visible;
418 LOG(Media, "MediaPlayerPrivatePhonon::setVisible(%s)", visible ? "true" : "false");
419
420 m_videoWidget->setVisible(m_isVisible);
421 }
422
setSize(const IntSize & newSize)423 void MediaPlayerPrivatePhonon::setSize(const IntSize& newSize)
424 {
425 if (!m_videoWidget)
426 return;
427
428 LOG(Media, "MediaPlayerPrivatePhonon::setSize(%d,%d)",
429 newSize.width(), newSize.height());
430
431 QRect currentRect = m_videoWidget->rect();
432
433 if (newSize.width() != currentRect.width() || newSize.height() != currentRect.height())
434 m_videoWidget->resize(newSize.width(), newSize.height());
435 }
436
naturalSize() const437 IntSize MediaPlayerPrivatePhonon::naturalSize() const
438 {
439 if (!hasVideo()) {
440 LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
441 0, 0);
442 return IntSize();
443 }
444
445 if (m_readyState < MediaPlayer::HaveMetadata) {
446 LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
447 0, 0);
448 return IntSize();
449 }
450
451 QSize videoSize = m_videoWidget->sizeHint();
452 IntSize naturalSize(videoSize.width(), videoSize.height());
453 LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
454 naturalSize.width(), naturalSize.height());
455 return naturalSize;
456 }
457
eventFilter(QObject * obj,QEvent * event)458 bool MediaPlayerPrivatePhonon::eventFilter(QObject* obj, QEvent* event)
459 {
460 if (event->type() == QEvent::UpdateRequest)
461 m_player->repaint();
462
463 return QObject::eventFilter(obj, event);
464 }
465
paint(GraphicsContext * graphicsContect,const IntRect & rect)466 void MediaPlayerPrivatePhonon::paint(GraphicsContext* graphicsContect, const IntRect& rect)
467 {
468 if (graphicsContect->paintingDisabled())
469 return;
470
471 if (!m_isVisible)
472 return;
473
474 QPainter* painter = graphicsContect->platformContext();
475
476 painter->fillRect(rect, Qt::black);
477
478 m_videoWidget->render(painter, QPoint(rect.x(), rect.y()),
479 QRegion(0, 0, rect.width(), rect.height()));
480 }
481
482 // ====================== Phonon::MediaObject signals ======================
483
stateChanged(Phonon::State newState,Phonon::State oldState)484 void MediaPlayerPrivatePhonon::stateChanged(Phonon::State newState, Phonon::State oldState)
485 {
486 const QMetaObject* metaObj = this->metaObject();
487 QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState"));
488 LOG(Media, "MediaPlayerPrivatePhonon::stateChanged(newState=%s, oldState=%s)",
489 phononStates.valueToKey(newState), phononStates.valueToKey(oldState));
490
491 updateStates();
492 }
493
metaDataChanged()494 void MediaPlayerPrivatePhonon::metaDataChanged()
495 {
496 LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()");
497 LOG_MEDIAOBJECT();
498 }
499
seekableChanged(bool)500 void MediaPlayerPrivatePhonon::seekableChanged(bool)
501 {
502 notImplemented();
503 LOG_MEDIAOBJECT();
504 }
505
hasVideoChanged(bool hasVideo)506 void MediaPlayerPrivatePhonon::hasVideoChanged(bool hasVideo)
507 {
508 LOG(Media, "MediaPlayerPrivatePhonon::hasVideoChanged(%s)", hasVideo ? "true" : "false");
509 }
510
bufferStatus(int)511 void MediaPlayerPrivatePhonon::bufferStatus(int)
512 {
513 notImplemented();
514 LOG_MEDIAOBJECT();
515 }
516
finished()517 void MediaPlayerPrivatePhonon::finished()
518 {
519 notImplemented();
520 LOG_MEDIAOBJECT();
521 }
522
currentSourceChanged(const Phonon::MediaSource &)523 void MediaPlayerPrivatePhonon::currentSourceChanged(const Phonon::MediaSource&)
524 {
525 notImplemented();
526 LOG_MEDIAOBJECT();
527 }
528
aboutToFinish()529 void MediaPlayerPrivatePhonon::aboutToFinish()
530 {
531 notImplemented();
532 LOG_MEDIAOBJECT();
533 }
534
totalTimeChanged(qint64 totalTime)535 void MediaPlayerPrivatePhonon::totalTimeChanged(qint64 totalTime)
536 {
537 #if OS(WINDOWS)
538 LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%I64d)", totalTime);
539 #else
540 LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%lld)", totalTime);
541 #endif
542 LOG_MEDIAOBJECT();
543 }
544
545 } // namespace WebCore
546
547 #include "moc_MediaPlayerPrivatePhonon.cpp"
548