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 }