• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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