File indexing completed on 2024-05-05 16:10:15

0001 /*
0002  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
0003  *           (C) 2009 Michael Howell <mhowell123@gmail.com>.
0004  *           (C) 2010 Allan Sandfeld Jensen <sandfeld@kde.org>.
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
0016  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0018  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
0019  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0020  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0021  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0022  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
0023  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0024  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0025  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 #include <wtf/Platform.h>
0029 
0030 #include "HTMLMediaElement.h"
0031 #include "html_element.h"
0032 #include "HTMLSourceElement.h"
0033 #include "HTMLDocument.h"
0034 #include "MediaError.h"
0035 #include "TimeRanges.h"
0036 #include "css/cssstyleselector.h"
0037 #include "css/cssproperties.h"
0038 #include "css/cssvalues.h"
0039 #include "css/csshelper.h"
0040 #include <phonon/mediaobject.h>
0041 #include <phonon/backendcapabilities.h>
0042 #include <rendering/render_media.h>
0043 #include <rendering/render_style.h>
0044 
0045 const double doubleMax = 999999999.8; // ### numeric_limits<double>::max()
0046 const double doubleInf = 999999999.0; // ### numeric_limits<double>::infinity()
0047 
0048 using namespace DOM;
0049 namespace khtml
0050 {
0051 
0052 HTMLMediaElement::HTMLMediaElement(Document *doc)
0053     : HTMLElement(doc)
0054     , m_defaultPlaybackRate(1.0f)
0055     , m_networkState(NETWORK_EMPTY)
0056     , m_readyState(HAVE_NOTHING)
0057     , m_begun(false)
0058     , m_loadedFirstFrame(false)
0059     , m_autoplaying(true)
0060     , m_autobuffer(true)
0061     , m_volume(0.5f)
0062     , m_muted(false)
0063     , m_paused(true)
0064     , m_seeking(false)
0065     , m_currentTimeDuringSeek(0)
0066     , m_previousProgress(0)
0067     , m_previousProgressTime(doubleMax)
0068     , m_sentStalledEvent(false)
0069     , m_player(new MediaPlayer())
0070 {
0071 }
0072 
0073 void HTMLMediaElement::attach()
0074 {
0075     assert(!attached());
0076     assert(!m_render);
0077     assert(parentNode());
0078 
0079     RenderStyle *_style = document()->styleSelector()->styleForElement(this);
0080     _style->ref();
0081     if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() &&
0082             _style->display() != NONE) {
0083         m_render = new(document()->renderArena()) RenderMedia(this);
0084         static_cast<RenderMedia *>(m_render)->setPlayer(m_player.data());
0085         m_render->setStyle(_style);
0086         parentNode()->renderer()->addChild(m_render, nextRenderer());
0087     }
0088     _style->deref();
0089 
0090     NodeBaseImpl::attach();
0091     if (m_render) {
0092         m_render->updateFromElement();
0093     }
0094     setRenderer(m_render);
0095     updateLoadState();
0096 }
0097 
0098 void HTMLMediaElement::close()
0099 {
0100     HTMLElement::close();
0101     updateLoadState();
0102     if (renderer()) {
0103         renderer()->updateFromElement();
0104     }
0105 }
0106 
0107 HTMLMediaElement::~HTMLMediaElement()
0108 {
0109     if (m_player) {
0110         m_player->deleteLater();
0111     }
0112 }
0113 
0114 void HTMLMediaElement::attributeChanged(NodeImpl::Id attrId)
0115 {
0116     HTMLElement::attributeChanged(attrId);
0117 
0118     if (attrId == ATTR_SRC) {
0119         // 3.14.9.2.
0120         // change to src attribute triggers load()
0121         if (inDocument() && m_networkState == NETWORK_EMPTY) {
0122             scheduleLoad();
0123         }
0124         updateLoadState();
0125     } if (attrId == ATTR_CONTROLS) {
0126         /*if (!isVideo() && attached() && (controls() != (renderer() != 0))) {
0127             detach();
0128             attach();
0129         }*/
0130         if (renderer()) {
0131             renderer()->updateFromElement();
0132         }
0133     }
0134 }
0135 
0136 void HTMLMediaElement::scheduleLoad()
0137 {
0138     // qCDebug(KHTML_LOG) << "not implemented";
0139 }
0140 
0141 String serializeTimeOffset(float time)
0142 {
0143     QString timeString = QString::number(time);
0144     // FIXME serialize time offset values properly (format not specified yet)
0145     timeString.append("s");
0146     return timeString;
0147 }
0148 
0149 PassRefPtr<MediaError> HTMLMediaElement::error() const
0150 {
0151     return m_error;
0152 }
0153 
0154 String HTMLMediaElement::src() const
0155 {
0156     return document()->completeURL(getAttribute(ATTR_SRC).string());
0157 }
0158 
0159 void HTMLMediaElement::setSrc(const String &url)
0160 {
0161     setAttribute(ATTR_SRC, url);
0162 }
0163 
0164 String HTMLMediaElement::currentSrc() const
0165 {
0166     return m_currentSrc;
0167 }
0168 
0169 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
0170 {
0171     return m_networkState;
0172 }
0173 
0174 bool HTMLMediaElement::autobuffer() const
0175 {
0176     return m_autobuffer;
0177 }
0178 
0179 void HTMLMediaElement::setAutobuffer(bool b)
0180 {
0181     m_autobuffer = b;
0182 }
0183 
0184 void HTMLMediaElement::load(ExceptionCode &)
0185 {
0186     loadResource(m_currentSrc);
0187 }
0188 
0189 void HTMLMediaElement::loadResource(String &url)
0190 {
0191     QUrl kurl(url.string());
0192     if (!m_player) {
0193         return;
0194     }
0195     if (autoplay()) {
0196         m_player->play(kurl);
0197     } else {
0198         m_player->load(kurl);
0199     }
0200 }
0201 
0202 void HTMLMediaElement::updateLoadState()
0203 {
0204     String url = pickMedia();
0205     if (currentSrc() != url) {
0206         m_currentSrc = url;
0207         if (m_autobuffer) {
0208             loadResource(url);
0209         }
0210     }
0211 }
0212 
0213 String HTMLMediaElement::canPlayType(String type)
0214 {
0215     QString theType = type.string().simplified();
0216     int paramsIdx = theType.indexOf(';');
0217     bool hasParams = (paramsIdx > 0);
0218     // FIXME: Phonon doesn't provide the API to handle codec parameters yet
0219     if (hasParams) {
0220         theType.truncate(paramsIdx);
0221     }
0222     if (theType == QLatin1String("audio/ogg") || theType == QLatin1String("video/ogg")) {
0223         theType = QLatin1String("application/ogg");
0224     }
0225     if (Phonon::BackendCapabilities::isMimeTypeAvailable(theType)) {
0226         return "probably";
0227     }
0228     if (theType == QLatin1String("application/octet-stream") && hasParams) {
0229         return "";
0230     }
0231     return "maybe";
0232 }
0233 
0234 void HTMLMediaElement::setReadyState(ReadyState state)
0235 {
0236     // 3.14.9.6. The ready states
0237     if (m_readyState == state) {
0238         return;
0239     }
0240 
0241     // ###
0242 
0243     updatePlayState();
0244 }
0245 
0246 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
0247 {
0248     return m_readyState;
0249 }
0250 
0251 bool HTMLMediaElement::seeking() const
0252 {
0253     return m_seeking;
0254 }
0255 
0256 // playback state
0257 float HTMLMediaElement::currentTime() const
0258 {
0259     if (!m_player) {
0260         return 0;
0261     }
0262     if (m_seeking) {
0263         return m_currentTimeDuringSeek;
0264     }
0265     return m_player->currentTime();
0266 }
0267 
0268 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode &ec)
0269 {
0270     Q_UNUSED(time);
0271     Q_UNUSED(ec);
0272     //    seek(time, ec);
0273 }
0274 
0275 float HTMLMediaElement::startTime() const
0276 {
0277     return 0.0f;
0278 }
0279 
0280 float HTMLMediaElement::duration() const
0281 {
0282     return m_player ? m_player->totalTime() : 0;
0283 }
0284 
0285 bool HTMLMediaElement::paused() const
0286 {
0287     return m_paused;
0288 }
0289 
0290 float HTMLMediaElement::defaultPlaybackRate() const
0291 {
0292     return m_defaultPlaybackRate;
0293 }
0294 
0295 void HTMLMediaElement::setDefaultPlaybackRate(float rate, ExceptionCode &ec)
0296 {
0297     if (rate == 0.0f) {
0298         ec = DOMException::NOT_SUPPORTED_ERR;
0299         return;
0300     }
0301     if (m_defaultPlaybackRate != rate) {
0302         m_defaultPlaybackRate = rate;
0303         // ### dispatchEventAsync(ratechangeEvent);
0304     }
0305 }
0306 
0307 float HTMLMediaElement::playbackRate() const
0308 {
0309     return 0; // stub...
0310 }
0311 
0312 void HTMLMediaElement::setPlaybackRate(float rate, ExceptionCode &ec)
0313 {
0314     Q_UNUSED(rate);
0315     Q_UNUSED(ec);
0316     // stub
0317 #if 0
0318     if (rate == 0.0f) {
0319         ec = DOMException::NOT_SUPPORTED_ERR;
0320         return;
0321     }
0322     if (m_player && m_player->rate() != rate) {
0323         m_player->setRate(rate);
0324         // ### dispatchEventAsync(ratechangeEvent);
0325     }
0326 #endif
0327 }
0328 
0329 bool HTMLMediaElement::ended() const
0330 {
0331     return endedPlayback();
0332 }
0333 
0334 bool HTMLMediaElement::autoplay() const
0335 {
0336     return hasAttribute(ATTR_AUTOPLAY);
0337 }
0338 
0339 void HTMLMediaElement::setAutoplay(bool b)
0340 {
0341     setBooleanAttribute(ATTR_AUTOPLAY, b);
0342 }
0343 
0344 bool HTMLMediaElement::loop() const
0345 {
0346     return hasAttribute(ATTR_LOOP);
0347 }
0348 
0349 void HTMLMediaElement::setLoop(bool b)
0350 {
0351     setBooleanAttribute(ATTR_LOOP, b);
0352 }
0353 
0354 void HTMLMediaElement::play(ExceptionCode &ec)
0355 {
0356     // 3.14.9.7. Playing the media resource
0357     if (!m_player || networkState() == NETWORK_EMPTY) {
0358         ec = 0;
0359         load(ec);
0360         if (ec) {
0361             return;
0362         }
0363     }
0364     ExceptionCode unused;
0365     if (endedPlayback()) {
0366         // ### seek(effectiveStart(), unused);
0367     }
0368     setPlaybackRate(defaultPlaybackRate(), unused);
0369 
0370     if (m_paused) {
0371         m_paused = false;
0372         // ### dispatchEventAsync(playEvent);
0373     }
0374 
0375     m_autoplaying = false;
0376 
0377     updatePlayState();
0378 }
0379 
0380 void HTMLMediaElement::pause(ExceptionCode &ec)
0381 {
0382     // 3.14.9.7. Playing the media resource
0383     if (!m_player || networkState() == NETWORK_EMPTY) {
0384         ec = 0;
0385         load(ec);
0386         if (ec) {
0387             return;
0388         }
0389     }
0390 
0391     if (!m_paused) {
0392         m_paused = true;
0393         // ### dispatchEventAsync(timeupdateEvent);
0394         // ### dispatchEventAsync(pauseEvent);
0395     }
0396 
0397     m_autoplaying = false;
0398 
0399     updatePlayState();
0400 }
0401 
0402 bool HTMLMediaElement::controls() const
0403 {
0404     return hasAttribute(ATTR_CONTROLS);
0405 }
0406 
0407 void HTMLMediaElement::setControls(bool b)
0408 {
0409     setBooleanAttribute(ATTR_CONTROLS, b);
0410 }
0411 
0412 float HTMLMediaElement::volume() const
0413 {
0414     return m_volume;
0415 }
0416 
0417 void HTMLMediaElement::setVolume(float vol, ExceptionCode &ec)
0418 {
0419     if (vol < 0.0f || vol > 1.0f) {
0420         ec = DOMException::INDEX_SIZE_ERR;
0421         return;
0422     }
0423 
0424     if (m_volume != vol) {
0425         m_volume = vol;
0426         updateVolume();
0427         // ### dispatchEventAsync(volumechangeEvent);
0428     }
0429 }
0430 
0431 bool HTMLMediaElement::muted() const
0432 {
0433     return m_muted;
0434 }
0435 
0436 void HTMLMediaElement::setMuted(bool muted)
0437 {
0438     if (m_muted != muted) {
0439         m_muted = muted;
0440         updateVolume();
0441         // ### dispatchEventAsync(volumechangeEvent);
0442     }
0443 }
0444 
0445 String HTMLMediaElement::pickMedia()
0446 {
0447     if (!document()) {
0448         return String();
0449     }
0450     // 3.14.9.2. Location of the media resource
0451     String mediaSrc = getAttribute(ATTR_SRC);
0452     String maybeSrc;
0453     if (mediaSrc.isEmpty()) {
0454         for (NodeImpl *n = firstChild(); n; n = n->nextSibling()) {
0455             if (n->id() == ID_SOURCE) {
0456                 String match = "maybe";
0457                 HTMLSourceElement *source = static_cast<HTMLSourceElement *>(n);
0458                 if (!source->hasAttribute(ATTR_SRC)) {
0459                     continue;
0460                 }
0461                 if (source->hasAttribute(ATTR_TYPE)) {
0462                     String type = source->type();
0463                     match = canPlayType(type);
0464                 }
0465                 if (match == "maybe" && maybeSrc.isEmpty()) {
0466                     maybeSrc = source->src().string();
0467                 } else if (match == "probably") {
0468                     mediaSrc = source->src().string();
0469                     break;
0470                 }
0471             }
0472         }
0473     }
0474     if (mediaSrc.isEmpty()) {
0475         mediaSrc = maybeSrc;
0476     }
0477     if (mediaSrc.isEmpty()) {
0478         return mediaSrc;
0479     }
0480     DocLoader *loader = document()->docLoader();
0481     if (!loader || !loader->willLoadMediaElement(mediaSrc)) {
0482         return String();
0483     }
0484     mediaSrc = document()->completeURL(mediaSrc.string());
0485     return mediaSrc;
0486 }
0487 
0488 void HTMLMediaElement::checkIfSeekNeeded()
0489 {
0490     // ###
0491 }
0492 
0493 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
0494 {
0495     // FIXME real ranges support
0496 #if 0
0497     if (!m_player || !m_player->maxTimeBuffered()) {
0498         return new TimeRanges;
0499     }
0500     return new TimeRanges(0, m_player->maxTimeBuffered());
0501 #endif
0502     return new TimeRanges(0, 0.0f); // stub
0503 }
0504 
0505 PassRefPtr<TimeRanges> HTMLMediaElement::played() const
0506 {
0507     // FIXME track played
0508     return new TimeRanges;
0509 }
0510 
0511 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
0512 {
0513 #if 0
0514     // FIXME real ranges support
0515     if (!m_player || !m_player->maxTimeSeekable()) {
0516         return new TimeRanges;
0517     }
0518     return new TimeRanges(0, m_player->maxTimeSeekable());
0519 #endif
0520     return new TimeRanges(0, 0.0f); // stub
0521 }
0522 
0523 bool HTMLMediaElement::endedPlayback() const
0524 {
0525 #if 0
0526     return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == playCount() - 1;
0527 #endif
0528     return m_player && m_player->mediaObject()->remainingTime() == 0;
0529 }
0530 
0531 void HTMLMediaElement::updateVolume()
0532 {
0533     if (!m_player) {
0534         return;
0535     }
0536 
0537     m_player->setVolume(m_muted ? 0 : m_volume);
0538 
0539     if (renderer()) {
0540         renderer()->updateFromElement();
0541     }
0542 }
0543 
0544 void HTMLMediaElement::updatePlayState()
0545 {
0546     if (!m_player) {
0547         return;
0548     }
0549     if (m_autoplaying) {
0550         return;
0551     }
0552     if (m_paused && !m_player->isPaused()) {
0553         m_player->pause();
0554     }
0555     if (!m_paused && !m_player->isPlaying()) {
0556         m_player->play();
0557     }
0558 }
0559 
0560 }