KHtml

HTMLMediaElement.cpp
1 /*
2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3  * (C) 2009 Michael Howell <[email protected]>.
4  * (C) 2010 Allan Sandfeld Jensen <[email protected]>.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <wtf/Platform.h>
29 
30 #include "HTMLMediaElement.h"
31 #include "html_element.h"
32 #include "HTMLSourceElement.h"
33 #include "HTMLDocument.h"
34 #include "MediaError.h"
35 #include "TimeRanges.h"
36 #include "css/cssstyleselector.h"
37 #include "css/cssproperties.h"
38 #include "css/cssvalues.h"
39 #include "css/csshelper.h"
40 #include <phonon/mediaobject.h>
41 #include <phonon/backendcapabilities.h>
42 #include <rendering/render_media.h>
43 #include <rendering/render_style.h>
44 
45 const double doubleMax = 999999999.8; // ### numeric_limits<double>::max()
46 const double doubleInf = 999999999.0; // ### numeric_limits<double>::infinity()
47 
48 using namespace DOM;
49 namespace khtml
50 {
51 
52 HTMLMediaElement::HTMLMediaElement(Document *doc)
53  : HTMLElement(doc)
54  , m_defaultPlaybackRate(1.0f)
55  , m_networkState(NETWORK_EMPTY)
56  , m_readyState(HAVE_NOTHING)
57  , m_begun(false)
58  , m_loadedFirstFrame(false)
59  , m_autoplaying(true)
60  , m_autobuffer(true)
61  , m_volume(0.5f)
62  , m_muted(false)
63  , m_paused(true)
64  , m_seeking(false)
65  , m_currentTimeDuringSeek(0)
66  , m_previousProgress(0)
67  , m_previousProgressTime(doubleMax)
68  , m_sentStalledEvent(false)
69  , m_player(new MediaPlayer())
70 {
71 }
72 
73 void HTMLMediaElement::attach()
74 {
75  assert(!attached());
76  assert(!m_render);
77  assert(parentNode());
78 
79  RenderStyle *_style = document()->styleSelector()->styleForElement(this);
80  _style->ref();
81  if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() &&
82  _style->display() != NONE) {
83  m_render = new(document()->renderArena()) RenderMedia(this);
84  static_cast<RenderMedia *>(m_render)->setPlayer(m_player.data());
85  m_render->setStyle(_style);
86  parentNode()->renderer()->addChild(m_render, nextRenderer());
87  }
88  _style->deref();
89 
90  NodeBaseImpl::attach();
91  if (m_render) {
92  m_render->updateFromElement();
93  }
94  setRenderer(m_render);
95  updateLoadState();
96 }
97 
98 void HTMLMediaElement::close()
99 {
100  HTMLElement::close();
101  updateLoadState();
102  if (renderer()) {
103  renderer()->updateFromElement();
104  }
105 }
106 
107 HTMLMediaElement::~HTMLMediaElement()
108 {
109  if (m_player) {
110  m_player->deleteLater();
111  }
112 }
113 
114 void HTMLMediaElement::attributeChanged(NodeImpl::Id attrId)
115 {
116  HTMLElement::attributeChanged(attrId);
117 
118  if (attrId == ATTR_SRC) {
119  // 3.14.9.2.
120  // change to src attribute triggers load()
121  if (inDocument() && m_networkState == NETWORK_EMPTY) {
122  scheduleLoad();
123  }
124  updateLoadState();
125  } if (attrId == ATTR_CONTROLS) {
126  /*if (!isVideo() && attached() && (controls() != (renderer() != 0))) {
127  detach();
128  attach();
129  }*/
130  if (renderer()) {
131  renderer()->updateFromElement();
132  }
133  }
134 }
135 
136 void HTMLMediaElement::scheduleLoad()
137 {
138  // qCDebug(KHTML_LOG) << "not implemented";
139 }
140 
141 String serializeTimeOffset(float time)
142 {
143  QString timeString = QString::number(time);
144  // FIXME serialize time offset values properly (format not specified yet)
145  timeString.append("s");
146  return timeString;
147 }
148 
149 PassRefPtr<MediaError> HTMLMediaElement::error() const
150 {
151  return m_error;
152 }
153 
154 String HTMLMediaElement::src() const
155 {
156  return document()->completeURL(getAttribute(ATTR_SRC).string());
157 }
158 
159 void HTMLMediaElement::setSrc(const String &url)
160 {
161  setAttribute(ATTR_SRC, url);
162 }
163 
164 String HTMLMediaElement::currentSrc() const
165 {
166  return m_currentSrc;
167 }
168 
169 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
170 {
171  return m_networkState;
172 }
173 
174 bool HTMLMediaElement::autobuffer() const
175 {
176  return m_autobuffer;
177 }
178 
179 void HTMLMediaElement::setAutobuffer(bool b)
180 {
181  m_autobuffer = b;
182 }
183 
184 void HTMLMediaElement::load(ExceptionCode &)
185 {
186  loadResource(m_currentSrc);
187 }
188 
189 void HTMLMediaElement::loadResource(String &url)
190 {
191  QUrl kurl(url.string());
192  if (!m_player) {
193  return;
194  }
195  if (autoplay()) {
196  m_player->play(kurl);
197  } else {
198  m_player->load(kurl);
199  }
200 }
201 
202 void HTMLMediaElement::updateLoadState()
203 {
204  String url = pickMedia();
205  if (currentSrc() != url) {
206  m_currentSrc = url;
207  if (m_autobuffer) {
208  loadResource(url);
209  }
210  }
211 }
212 
213 String HTMLMediaElement::canPlayType(String type)
214 {
215  QString theType = type.string().simplified();
216  int paramsIdx = theType.indexOf(';');
217  bool hasParams = (paramsIdx > 0);
218  // FIXME: Phonon doesn't provide the API to handle codec parameters yet
219  if (hasParams) {
220  theType.truncate(paramsIdx);
221  }
222  if (theType == QLatin1String("audio/ogg") || theType == QLatin1String("video/ogg")) {
223  theType = QLatin1String("application/ogg");
224  }
226  return "probably";
227  }
228  if (theType == QLatin1String("application/octet-stream") && hasParams) {
229  return "";
230  }
231  return "maybe";
232 }
233 
234 void HTMLMediaElement::setReadyState(ReadyState state)
235 {
236  // 3.14.9.6. The ready states
237  if (m_readyState == state) {
238  return;
239  }
240 
241  // ###
242 
243  updatePlayState();
244 }
245 
246 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
247 {
248  return m_readyState;
249 }
250 
251 bool HTMLMediaElement::seeking() const
252 {
253  return m_seeking;
254 }
255 
256 // playback state
257 float HTMLMediaElement::currentTime() const
258 {
259  if (!m_player) {
260  return 0;
261  }
262  if (m_seeking) {
263  return m_currentTimeDuringSeek;
264  }
265  return m_player->currentTime();
266 }
267 
268 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode &ec)
269 {
270  Q_UNUSED(time);
271  Q_UNUSED(ec);
272  // seek(time, ec);
273 }
274 
275 float HTMLMediaElement::startTime() const
276 {
277  return 0.0f;
278 }
279 
280 float HTMLMediaElement::duration() const
281 {
282  return m_player ? m_player->totalTime() : 0;
283 }
284 
285 bool HTMLMediaElement::paused() const
286 {
287  return m_paused;
288 }
289 
290 float HTMLMediaElement::defaultPlaybackRate() const
291 {
292  return m_defaultPlaybackRate;
293 }
294 
295 void HTMLMediaElement::setDefaultPlaybackRate(float rate, ExceptionCode &ec)
296 {
297  if (rate == 0.0f) {
298  ec = DOMException::NOT_SUPPORTED_ERR;
299  return;
300  }
301  if (m_defaultPlaybackRate != rate) {
302  m_defaultPlaybackRate = rate;
303  // ### dispatchEventAsync(ratechangeEvent);
304  }
305 }
306 
307 float HTMLMediaElement::playbackRate() const
308 {
309  return 0; // stub...
310 }
311 
312 void HTMLMediaElement::setPlaybackRate(float rate, ExceptionCode &ec)
313 {
314  Q_UNUSED(rate);
315  Q_UNUSED(ec);
316  // stub
317 #if 0
318  if (rate == 0.0f) {
319  ec = DOMException::NOT_SUPPORTED_ERR;
320  return;
321  }
322  if (m_player && m_player->rate() != rate) {
323  m_player->setRate(rate);
324  // ### dispatchEventAsync(ratechangeEvent);
325  }
326 #endif
327 }
328 
329 bool HTMLMediaElement::ended() const
330 {
331  return endedPlayback();
332 }
333 
334 bool HTMLMediaElement::autoplay() const
335 {
336  return hasAttribute(ATTR_AUTOPLAY);
337 }
338 
339 void HTMLMediaElement::setAutoplay(bool b)
340 {
341  setBooleanAttribute(ATTR_AUTOPLAY, b);
342 }
343 
344 bool HTMLMediaElement::loop() const
345 {
346  return hasAttribute(ATTR_LOOP);
347 }
348 
349 void HTMLMediaElement::setLoop(bool b)
350 {
351  setBooleanAttribute(ATTR_LOOP, b);
352 }
353 
354 void HTMLMediaElement::play(ExceptionCode &ec)
355 {
356  // 3.14.9.7. Playing the media resource
357  if (!m_player || networkState() == NETWORK_EMPTY) {
358  ec = 0;
359  load(ec);
360  if (ec) {
361  return;
362  }
363  }
364  ExceptionCode unused;
365  if (endedPlayback()) {
366  // ### seek(effectiveStart(), unused);
367  }
368  setPlaybackRate(defaultPlaybackRate(), unused);
369 
370  if (m_paused) {
371  m_paused = false;
372  // ### dispatchEventAsync(playEvent);
373  }
374 
375  m_autoplaying = false;
376 
377  updatePlayState();
378 }
379 
380 void HTMLMediaElement::pause(ExceptionCode &ec)
381 {
382  // 3.14.9.7. Playing the media resource
383  if (!m_player || networkState() == NETWORK_EMPTY) {
384  ec = 0;
385  load(ec);
386  if (ec) {
387  return;
388  }
389  }
390 
391  if (!m_paused) {
392  m_paused = true;
393  // ### dispatchEventAsync(timeupdateEvent);
394  // ### dispatchEventAsync(pauseEvent);
395  }
396 
397  m_autoplaying = false;
398 
399  updatePlayState();
400 }
401 
402 bool HTMLMediaElement::controls() const
403 {
404  return hasAttribute(ATTR_CONTROLS);
405 }
406 
407 void HTMLMediaElement::setControls(bool b)
408 {
409  setBooleanAttribute(ATTR_CONTROLS, b);
410 }
411 
412 float HTMLMediaElement::volume() const
413 {
414  return m_volume;
415 }
416 
417 void HTMLMediaElement::setVolume(float vol, ExceptionCode &ec)
418 {
419  if (vol < 0.0f || vol > 1.0f) {
420  ec = DOMException::INDEX_SIZE_ERR;
421  return;
422  }
423 
424  if (m_volume != vol) {
425  m_volume = vol;
426  updateVolume();
427  // ### dispatchEventAsync(volumechangeEvent);
428  }
429 }
430 
431 bool HTMLMediaElement::muted() const
432 {
433  return m_muted;
434 }
435 
436 void HTMLMediaElement::setMuted(bool muted)
437 {
438  if (m_muted != muted) {
439  m_muted = muted;
440  updateVolume();
441  // ### dispatchEventAsync(volumechangeEvent);
442  }
443 }
444 
445 String HTMLMediaElement::pickMedia()
446 {
447  if (!document()) {
448  return String();
449  }
450  // 3.14.9.2. Location of the media resource
451  String mediaSrc = getAttribute(ATTR_SRC);
452  String maybeSrc;
453  if (mediaSrc.isEmpty()) {
454  for (NodeImpl *n = firstChild(); n; n = n->nextSibling()) {
455  if (n->id() == ID_SOURCE) {
456  String match = "maybe";
457  HTMLSourceElement *source = static_cast<HTMLSourceElement *>(n);
458  if (!source->hasAttribute(ATTR_SRC)) {
459  continue;
460  }
461  if (source->hasAttribute(ATTR_TYPE)) {
462  String type = source->type();
463  match = canPlayType(type);
464  }
465  if (match == "maybe" && maybeSrc.isEmpty()) {
466  maybeSrc = source->src().string();
467  } else if (match == "probably") {
468  mediaSrc = source->src().string();
469  break;
470  }
471  }
472  }
473  }
474  if (mediaSrc.isEmpty()) {
475  mediaSrc = maybeSrc;
476  }
477  if (mediaSrc.isEmpty()) {
478  return mediaSrc;
479  }
480  DocLoader *loader = document()->docLoader();
481  if (!loader || !loader->willLoadMediaElement(mediaSrc)) {
482  return String();
483  }
484  mediaSrc = document()->completeURL(mediaSrc.string());
485  return mediaSrc;
486 }
487 
488 void HTMLMediaElement::checkIfSeekNeeded()
489 {
490  // ###
491 }
492 
493 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
494 {
495  // FIXME real ranges support
496 #if 0
497  if (!m_player || !m_player->maxTimeBuffered()) {
498  return new TimeRanges;
499  }
500  return new TimeRanges(0, m_player->maxTimeBuffered());
501 #endif
502  return new TimeRanges(0, 0.0f); // stub
503 }
504 
505 PassRefPtr<TimeRanges> HTMLMediaElement::played() const
506 {
507  // FIXME track played
508  return new TimeRanges;
509 }
510 
511 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
512 {
513 #if 0
514  // FIXME real ranges support
515  if (!m_player || !m_player->maxTimeSeekable()) {
516  return new TimeRanges;
517  }
518  return new TimeRanges(0, m_player->maxTimeSeekable());
519 #endif
520  return new TimeRanges(0, 0.0f); // stub
521 }
522 
523 bool HTMLMediaElement::endedPlayback() const
524 {
525 #if 0
526  return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == playCount() - 1;
527 #endif
528  return m_player && m_player->mediaObject()->remainingTime() == 0;
529 }
530 
531 void HTMLMediaElement::updateVolume()
532 {
533  if (!m_player) {
534  return;
535  }
536 
537  m_player->setVolume(m_muted ? 0 : m_volume);
538 
539  if (renderer()) {
540  renderer()->updateFromElement();
541  }
542 }
543 
544 void HTMLMediaElement::updatePlayState()
545 {
546  if (!m_player) {
547  return;
548  }
549  if (m_autoplaying) {
550  return;
551  }
552  if (m_paused && !m_player->isPaused()) {
553  m_player->pause();
554  }
555  if (!m_paused && !m_player->isPlaying()) {
556  m_player->play();
557  }
558 }
559 
560 }
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
void truncate(int position)
This file is part of the HTML rendering engine for KDE.
QString simplified() const const
PHONON_EXPORT bool isMimeTypeAvailable(const QString &mimeType)
void setAttribute(const DOMString &name, const DOMString &value)
Adds a new attribute.
QAction * load(const QObject *recvr, const char *slot, QObject *parent)
QString number(int n, int base)
Node parentNode() const
The parent of this node.
Definition: dom_node.cpp:250
The Document interface represents the entire HTML or XML document.
Definition: dom_doc.h:246
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
Node firstChild() const
The first child of this node.
Definition: dom_node.cpp:266
This library provides a full-featured HTML parser and widget.
DOMString getAttribute(const DOMString &name)
Retrieves an attribute value by name.
bool hasAttribute(const DOMString &name)
Returns true when an attribute with a given name is specified on this element or has a default value...
Node nextSibling() const
The node immediately following this node.
Definition: dom_node.cpp:290
All HTML element interfaces derive from this class.
Definition: html_element.h:70
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 22:48:16 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.