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