50ee0f1ec369a3b55a093c6516a02d886c73d58a
[WebKit-https.git] / Source / 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::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
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
121 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivatePhonon::create(MediaPlayer* player)
122 {
123     return adoptPtr(new MediaPlayerPrivatePhonon(player));
124 }
125
126 void MediaPlayerPrivatePhonon::registerMediaEngine(MediaEngineRegistrar registrar)
127 {
128     if (isAvailable())
129         registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
130 }
131
132
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
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
193 void MediaPlayerPrivatePhonon::getSupportedTypes(HashSet<String>& types)
194 {
195     types = supportedTypesCache();
196 }
197
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
208 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
215 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
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
244 void MediaPlayerPrivatePhonon::cancelLoad()
245 {
246     notImplemented();
247 }
248
249
250 void MediaPlayerPrivatePhonon::play()
251 {
252     LOG(Media, "MediaPlayerPrivatePhonon::play()");
253     m_mediaObject->play();
254 }
255
256 void MediaPlayerPrivatePhonon::pause()
257 {
258     LOG(Media, "MediaPlayerPrivatePhonon::pause()");
259     m_mediaObject->pause();
260 }
261
262
263 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
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
283 bool MediaPlayerPrivatePhonon::seeking() const
284 {
285     return false;
286 }
287
288 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
302 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
310 PassRefPtr<TimeRanges> MediaPlayerPrivatePhonon::buffered() const
311 {
312     notImplemented();
313     return TimeRanges::create();
314 }
315
316 float MediaPlayerPrivatePhonon::maxTimeSeekable() const
317 {
318     notImplemented();
319     return 0.0f;
320 }
321
322 unsigned MediaPlayerPrivatePhonon::bytesLoaded() const
323 {
324     notImplemented();
325     return 0;
326 }
327
328 unsigned MediaPlayerPrivatePhonon::totalBytes() const
329 {
330     //notImplemented();
331     return 0;
332 }
333
334 void MediaPlayerPrivatePhonon::setRate(float)
335 {
336     notImplemented();
337 }
338
339 void MediaPlayerPrivatePhonon::setVolume(float volume)
340 {
341     LOG(Media, "MediaPlayerPrivatePhonon::setVolume()");
342     m_audioOutput->setVolume(volume);
343 }
344
345 void MediaPlayerPrivatePhonon::setMuted(bool muted)
346 {
347     LOG(Media, "MediaPlayerPrivatePhonon::setMuted()");
348     m_audioOutput->setMuted(muted);
349 }
350
351 MediaPlayer::NetworkState MediaPlayerPrivatePhonon::networkState() const
352 {
353 #if !LOG_DISABLED
354     const QMetaObject* metaObj = this->metaObject();
355     QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState"));
356     LOG(Media, "MediaPlayerPrivatePhonon::networkState() --> %s", networkStates.valueToKey(m_networkState));
357 #endif
358     return m_networkState;
359 }
360
361 MediaPlayer::ReadyState MediaPlayerPrivatePhonon::readyState() const
362 {
363 #if !LOG_DISABLED
364     const QMetaObject* metaObj = this->metaObject();
365     QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState"));
366     LOG(Media, "MediaPlayerPrivatePhonon::readyState() --> %s", readyStates.valueToKey(m_readyState));
367 #endif
368     return m_readyState;
369 }
370
371 void MediaPlayerPrivatePhonon::updateStates()
372 {
373     MediaPlayer::NetworkState oldNetworkState = m_networkState;
374     MediaPlayer::ReadyState oldReadyState = m_readyState;
375
376     Phonon::State phononState = m_mediaObject->state();
377
378     if (phononState == Phonon::StoppedState) {
379         if (m_readyState < MediaPlayer::HaveMetadata) {
380             m_networkState = MediaPlayer::Loading; // FIXME: should this be MediaPlayer::Idle?
381             m_readyState = MediaPlayer::HaveMetadata;
382             m_mediaObject->pause();
383         }
384     } else if (phononState == Phonon::PausedState) {
385         m_networkState = MediaPlayer::Loaded;
386         m_readyState = MediaPlayer::HaveEnoughData;
387     } else if (phononState == Phonon::ErrorState) {
388          if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) {
389              // FIXME: is it possile to differentiate between different types of errors
390              m_networkState = MediaPlayer::NetworkError;
391              m_readyState = MediaPlayer::HaveNothing;
392              cancelLoad();
393          } else
394              m_mediaObject->pause();
395     }
396
397     if (seeking())
398         m_readyState = MediaPlayer::HaveNothing;
399
400     if (m_networkState != oldNetworkState) {
401 #if !LOG_DISABLED
402         const QMetaObject* metaObj = this->metaObject();
403         QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState"));
404         LOG(Media, "Network state changed from '%s' to '%s'",
405                 networkStates.valueToKey(oldNetworkState),
406                 networkStates.valueToKey(m_networkState));
407 #endif
408         m_player->networkStateChanged();
409     }
410
411     if (m_readyState != oldReadyState) {
412 #if !LOG_DISABLED
413         const QMetaObject* metaObj = this->metaObject();
414         QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState"));
415         LOG(Media, "Ready state changed from '%s' to '%s'",
416                 readyStates.valueToKey(oldReadyState),
417                 readyStates.valueToKey(m_readyState));
418 #endif
419         m_player->readyStateChanged();
420     }
421 }
422
423 void MediaPlayerPrivatePhonon::setVisible(bool visible)
424 {
425     m_isVisible = visible;
426     LOG(Media, "MediaPlayerPrivatePhonon::setVisible(%s)", visible ? "true" : "false");
427
428     m_videoWidget->setVisible(m_isVisible);
429 }
430
431 void MediaPlayerPrivatePhonon::setSize(const IntSize& newSize)
432 {
433     if (!m_videoWidget)
434         return;
435
436     LOG(Media, "MediaPlayerPrivatePhonon::setSize(%d,%d)",
437                 newSize.width(), newSize.height());
438
439     QRect currentRect = m_videoWidget->rect();
440
441     if (newSize.width() != currentRect.width() || newSize.height() != currentRect.height())
442         m_videoWidget->resize(newSize.width(), newSize.height());
443 }
444
445 IntSize MediaPlayerPrivatePhonon::naturalSize() const
446 {
447     if (!hasVideo()) {
448         LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
449                     0, 0);
450         return IntSize();
451     }
452
453     if (m_readyState < MediaPlayer::HaveMetadata) {
454         LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
455                            0, 0);
456         return IntSize();
457     }
458
459     QSize videoSize = m_videoWidget->sizeHint();
460     IntSize naturalSize(videoSize.width(), videoSize.height());
461     LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
462             naturalSize.width(), naturalSize.height());
463     return naturalSize;
464 }
465
466 bool MediaPlayerPrivatePhonon::eventFilter(QObject* obj, QEvent* event)
467 {
468     if (event->type() == QEvent::UpdateRequest)
469         m_player->repaint();
470
471     return QObject::eventFilter(obj, event);
472 }
473
474 void MediaPlayerPrivatePhonon::paint(GraphicsContext* graphicsContect, const IntRect& rect)
475 {
476     if (graphicsContect->paintingDisabled())
477         return;
478
479     if (!m_isVisible)
480         return;
481
482     QPainter* painter = graphicsContect->platformContext();
483
484     painter->fillRect(rect, Qt::black);
485
486     m_videoWidget->render(painter, QPoint(rect.x(), rect.y()),
487             QRegion(0, 0, rect.width(), rect.height()));
488 }
489
490 // ====================== Phonon::MediaObject signals ======================
491
492 void MediaPlayerPrivatePhonon::stateChanged(Phonon::State newState, Phonon::State oldState)
493 {
494 #if !LOG_DISABLED
495     const QMetaObject* metaObj = this->metaObject();
496     QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState"));
497     LOG(Media, "MediaPlayerPrivatePhonon::stateChanged(newState=%s, oldState=%s)",
498             phononStates.valueToKey(newState), phononStates.valueToKey(oldState));
499 #endif
500
501     updateStates();
502 }
503
504 void MediaPlayerPrivatePhonon::metaDataChanged()
505 {
506     LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()");
507     LOG_MEDIAOBJECT();
508 }
509
510 void MediaPlayerPrivatePhonon::seekableChanged(bool)
511 {
512     notImplemented();
513     LOG_MEDIAOBJECT();
514 }
515
516 void MediaPlayerPrivatePhonon::hasVideoChanged(bool hasVideo)
517 {
518     LOG(Media, "MediaPlayerPrivatePhonon::hasVideoChanged(%s)", hasVideo ? "true" : "false");
519 }
520
521 void MediaPlayerPrivatePhonon::bufferStatus(int)
522 {
523     notImplemented();
524     LOG_MEDIAOBJECT();
525 }
526
527 void MediaPlayerPrivatePhonon::finished()
528 {
529     notImplemented();
530     LOG_MEDIAOBJECT();
531 }
532
533 void MediaPlayerPrivatePhonon::currentSourceChanged(const Phonon::MediaSource&)
534 {
535     notImplemented();
536     LOG_MEDIAOBJECT();
537 }
538
539 void MediaPlayerPrivatePhonon::aboutToFinish()
540 {
541     notImplemented();
542     LOG_MEDIAOBJECT();
543 }
544
545 void MediaPlayerPrivatePhonon::totalTimeChanged(qint64 totalTime)
546 {
547 #if OS(WINDOWS)
548     LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%I64d)", totalTime);
549 #else
550     LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%lld)", totalTime);
551 #endif
552     LOG_MEDIAOBJECT();
553 }
554
555 } // namespace WebCore
556
557 #include "moc_MediaPlayerPrivatePhonon.cpp"