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 MediaPlayerPrivateInterface* MediaPlayerPrivatePhonon::create(MediaPlayer* player) 122 { 123 return 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 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 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 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 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 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 437 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 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 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 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 494 void MediaPlayerPrivatePhonon::metaDataChanged() 495 { 496 LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()"); 497 LOG_MEDIAOBJECT(); 498 } 499 500 void MediaPlayerPrivatePhonon::seekableChanged(bool) 501 { 502 notImplemented(); 503 LOG_MEDIAOBJECT(); 504 } 505 506 void MediaPlayerPrivatePhonon::hasVideoChanged(bool hasVideo) 507 { 508 LOG(Media, "MediaPlayerPrivatePhonon::hasVideoChanged(%s)", hasVideo ? "true" : "false"); 509 } 510 511 void MediaPlayerPrivatePhonon::bufferStatus(int) 512 { 513 notImplemented(); 514 LOG_MEDIAOBJECT(); 515 } 516 517 void MediaPlayerPrivatePhonon::finished() 518 { 519 notImplemented(); 520 LOG_MEDIAOBJECT(); 521 } 522 523 void MediaPlayerPrivatePhonon::currentSourceChanged(const Phonon::MediaSource&) 524 { 525 notImplemented(); 526 LOG_MEDIAOBJECT(); 527 } 528 529 void MediaPlayerPrivatePhonon::aboutToFinish() 530 { 531 notImplemented(); 532 LOG_MEDIAOBJECT(); 533 } 534 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