2010-07-07 Jesus Sanchez-Palencia <jesus@webkit.org>
[WebKit.git] / WebCore / platform / graphics / qt / MediaPlayerPrivatePhonon.cpp
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
54 static QByteArray debugMediaObject(WebCore::MediaPlayerPrivate* 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
87 MediaPlayerPrivate::MediaPlayerPrivate(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, qFindChildren<QWidget*>(m_videoWidget))
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
121 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
122 {
123     return new MediaPlayerPrivate(player);
124 }
125
126 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
127 {
128     if (isAvailable())
129         registrar(create, getSupportedTypes, supportsType);
130 }
131
132
133 MediaPlayerPrivate::~MediaPlayerPrivate()
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
149 HashSet<String>& MediaPlayerPrivate::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
193 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
194 {
195     types = supportedTypesCache();
196 }
197
198 MediaPlayer::SupportsType MediaPlayerPrivate::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
208 bool MediaPlayerPrivate::hasVideo() const
209 {
210     bool hasVideo = m_mediaObject->hasVideo();
211     LOG(Media, "MediaPlayerPrivatePhonon::hasVideo() -> %s", hasVideo ? "true" : "false");
212     return hasVideo;
213 }
214
215 bool MediaPlayerPrivate::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
223 void MediaPlayerPrivate::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
244 void MediaPlayerPrivate::cancelLoad()
245 {
246     notImplemented();
247 }
248
249
250 void MediaPlayerPrivate::play()
251 {
252     LOG(Media, "MediaPlayerPrivatePhonon::play()");
253     m_mediaObject->play();
254 }
255
256 void MediaPlayerPrivate::pause()
257 {
258     LOG(Media, "MediaPlayerPrivatePhonon::pause()");
259     m_mediaObject->pause();
260 }
261
262
263 bool MediaPlayerPrivate::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
270 void MediaPlayerPrivate::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
283 bool MediaPlayerPrivate::seeking() const
284 {
285     return false;
286 }
287
288 float MediaPlayerPrivate::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
302 float MediaPlayerPrivate::currentTime() const
303 {
304     float currentTime = m_mediaObject->currentTime() / 1000.0f;
305
306     LOG(Media, "MediaPlayerPrivatePhonon::currentTime() --> %f", currentTime);
307     return currentTime;
308 }
309
310 PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const
311 {
312     notImplemented();
313     return TimeRanges::create();
314 }
315
316 float MediaPlayerPrivate::maxTimeSeekable() const
317 {
318     notImplemented();
319     return 0.0f;
320 }
321
322 unsigned MediaPlayerPrivate::bytesLoaded() const
323 {
324     notImplemented();
325     return 0;
326 }
327
328 unsigned MediaPlayerPrivate::totalBytes() const
329 {
330     //notImplemented();
331     return 0;
332 }
333
334 void MediaPlayerPrivate::setRate(float)
335 {
336     notImplemented();
337 }
338
339 void MediaPlayerPrivate::setVolume(float volume)
340 {
341     LOG(Media, "MediaPlayerPrivatePhonon::setVolume()");
342     m_audioOutput->setVolume(volume);
343 }
344
345 void MediaPlayerPrivate::setMuted(bool muted)
346 {
347     LOG(Media, "MediaPlayerPrivatePhonon::setMuted()");
348     m_audioOutput->setMuted(muted);
349 }
350
351 MediaPlayer::NetworkState MediaPlayerPrivate::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
359 MediaPlayer::ReadyState MediaPlayerPrivate::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
367 void MediaPlayerPrivate::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
415 void MediaPlayerPrivate::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
423 void MediaPlayerPrivate::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
437 IntSize MediaPlayerPrivate::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
458 bool MediaPlayerPrivate::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
466 void MediaPlayerPrivate::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
484 void MediaPlayerPrivate::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
494 void MediaPlayerPrivate::metaDataChanged()
495 {
496     LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()");
497     LOG_MEDIAOBJECT();
498 }
499
500 void MediaPlayerPrivate::seekableChanged(bool)
501 {
502     notImplemented();
503     LOG_MEDIAOBJECT();
504 }
505
506 void MediaPlayerPrivate::hasVideoChanged(bool hasVideo)
507 {
508     LOG(Media, "MediaPlayerPrivatePhonon::hasVideoChanged(%s)", hasVideo ? "true" : "false");
509 }
510
511 void MediaPlayerPrivate::bufferStatus(int)
512 {
513     notImplemented();
514     LOG_MEDIAOBJECT();
515 }
516
517 void MediaPlayerPrivate::finished()
518 {
519     notImplemented();
520     LOG_MEDIAOBJECT();
521 }
522
523 void MediaPlayerPrivate::currentSourceChanged(const Phonon::MediaSource&)
524 {
525     notImplemented();
526     LOG_MEDIAOBJECT();
527 }
528
529 void MediaPlayerPrivate::aboutToFinish()
530 {
531     notImplemented();
532     LOG_MEDIAOBJECT();
533 }
534
535 void MediaPlayerPrivate::totalTimeChanged(qint64 totalTime)
536 {
537     LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%lld)", totalTime);
538     LOG_MEDIAOBJECT();
539 }
540
541 } // namespace WebCore
542
543 #include "moc_MediaPlayerPrivatePhonon.cpp"