File indexing completed on 2024-04-21 15:38:19
0001 /** 0002 * Copyright (C) 2004 by Koos Vriezen <koos.vriezen@gmail.com> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Library General Public 0006 * License version 2 as published by the Free Software Foundation. 0007 * 0008 * This library is distributed in the hope that it will be useful, 0009 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0011 * Library General Public License for more details. 0012 * 0013 * You should have received a copy of the GNU Library General Public License 0014 * along with this library; see the file COPYING.LIB. If not, write to 0015 * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, 0016 * Boston, MA 02110-1301, USA. 0017 **/ 0018 0019 #include "config-kmplayer.h" 0020 #include <time.h> 0021 0022 #include <qtextstream.h> 0023 #include <kdebug.h> 0024 #ifdef KMPLAYER_WITH_EXPAT 0025 #include <expat.h> 0026 #endif 0027 #include "kmplayerplaylist.h" 0028 #include "kmplayer_asx.h" 0029 #include "kmplayer_atom.h" 0030 #include "kmplayer_opml.h" 0031 #include "kmplayer_rp.h" 0032 #include "kmplayer_rss.h" 0033 #include "kmplayer_smil.h" 0034 #include "kmplayer_xspf.h" 0035 #include "mediaobject.h" 0036 0037 #include <kurl.h> 0038 0039 #ifdef SHAREDPTR_DEBUG 0040 KMPLAYER_EXPORT int shared_data_count; 0041 #endif 0042 0043 using namespace KMPlayer; 0044 0045 //----------------------------------------------------------------------------- 0046 0047 Node *KMPlayer::fromXMLDocumentTag (NodePtr & d, const QString & tag) { 0048 const QByteArray ba = tag.toAscii (); 0049 const char * const name = ba.constData(); 0050 if (!strcmp (name, "smil")) 0051 return new SMIL::Smil (d); 0052 else if (!strcasecmp (name, "asx")) 0053 return new ASX::Asx (d); 0054 else if (!strcasecmp (name, "imfl")) 0055 return new RP::Imfl (d); 0056 else if (!strcasecmp (name, "rss")) 0057 return new RSS::Rss (d); 0058 else if (!strcasecmp (name, "feed")) 0059 return new ATOM::Feed (d); 0060 else if (!strcasecmp (name, "playlist")) 0061 return new XSPF::Playlist (d); 0062 else if (!strcasecmp (name, "opml")) 0063 return new OPML::Opml (d); 0064 else if (!strcasecmp (name, "url")) 0065 return new GenericURL (d, QString ()); 0066 else if (!strcasecmp (name, "mrl") || 0067 !strcasecmp (name, "document")) 0068 return new GenericMrl (d); 0069 return 0L; 0070 } 0071 0072 //----------------------------------------------------------------------------- 0073 0074 QTextStream &KMPlayer::operator << (QTextStream &out, const XMLStringlet &txt) { 0075 int len = int (txt.str.size ()); 0076 for (int i = 0; i < len; ++i) { 0077 if (txt.str [i] == QChar ('<')) { 0078 out << "<"; 0079 } else if (txt.str [i] == QChar ('>')) { 0080 out << ">"; 0081 } else if (txt.str [i] == QChar ('"')) { 0082 out << """; 0083 } else if (txt.str [i] == QChar ('&')) { 0084 out << "&"; 0085 } else 0086 out << txt.str [i]; 0087 } 0088 return out; 0089 } 0090 0091 //----------------------------------------------------------------------------- 0092 0093 Connection::Connection (Node *invoker, Node *receiver, VirtualVoid *pl) 0094 : connectee (invoker), connecter (receiver), payload (pl) { 0095 #ifdef KMPLAYER_TEST_CONNECTION 0096 connection_counter++; 0097 #endif 0098 } 0099 0100 ConnectionLink::ConnectionLink () : connection (NULL) {} 0101 0102 ConnectionLink::~ConnectionLink () { 0103 disconnect (); 0104 } 0105 0106 bool ConnectionLink::connect (Node *send, MessageType msg, Node *rec, 0107 VirtualVoid *payload) { 0108 disconnect (); 0109 ConnectionList *list = nodeMessageReceivers (send, msg); 0110 if (list) { 0111 connection = new Connection (send, rec, payload); 0112 connection->list = list; 0113 connection->link = &connection; 0114 connection->prev = list->link_last; 0115 connection->next = NULL; 0116 if (list->link_last) 0117 list->link_last->next = connection; 0118 else 0119 list->link_first = connection; 0120 list->link_last = connection; 0121 } 0122 return list; 0123 } 0124 0125 void ConnectionLink::disconnect () const { 0126 if (connection) { 0127 Connection *tmp = connection; 0128 if (tmp->prev) 0129 tmp->prev->next = tmp->next; 0130 else 0131 tmp->list->link_first = tmp->next; 0132 if (tmp->next) 0133 tmp->next->prev = tmp->prev; 0134 else 0135 tmp->list->link_last = tmp->prev; 0136 *tmp->link = NULL; 0137 if (tmp->list->link_next == tmp) 0138 tmp->list->link_next = tmp->next; 0139 delete tmp; 0140 } 0141 ASSERT (!connection); 0142 } 0143 0144 void ConnectionLink::assign (const ConnectionLink *link) const { 0145 disconnect (); 0146 connection = link->connection; 0147 link->connection = NULL; 0148 if (connection) 0149 connection->link = &connection; 0150 } 0151 0152 Node *ConnectionLink::signaler () const { 0153 return connection ? connection->connectee.ptr () : NULL; 0154 } 0155 0156 ConnectionList::ConnectionList () 0157 : link_first (NULL), link_last (NULL), link_next (NULL) {} 0158 0159 ConnectionList::~ConnectionList () { 0160 clear (); 0161 } 0162 0163 void ConnectionList::clear () { 0164 while (link_first) { 0165 Connection *tmp = link_first; 0166 link_first = tmp->next; 0167 *tmp->link = NULL; 0168 delete tmp; 0169 } 0170 link_last = link_next = NULL; 0171 } 0172 0173 //----------------------------------------------------------------------------- 0174 0175 KDE_NO_CDTOR_EXPORT TimerPosting::TimerPosting (int ms, unsigned eid) 0176 : Posting (NULL, MsgEventTimer), 0177 event_id (eid), 0178 milli_sec (ms), 0179 interval (false) {} 0180 0181 //----------------------------------------------------------------------------- 0182 0183 Matrix::Matrix () : a (1.0), b (0.0), c (0.0), d (1.0), tx (0), ty (0) {} 0184 0185 Matrix::Matrix (const Matrix & m) 0186 : a (m.a), b (m.b), c (m.c), d (m.d), tx (m.tx), ty (m.ty) {} 0187 0188 Matrix::Matrix (Single xoff, Single yoff, float xscale, float yscale) 0189 : a (xscale), b (0.0), c (0.0), d (yscale), tx (xoff), ty (yoff) {} 0190 0191 void Matrix::getXY (Single & x, Single & y) const { 0192 x = Single (x * a) + tx; 0193 y = Single (y * d) + ty; 0194 } 0195 0196 void Matrix::getWH (Single &w, Single &h) const { 0197 w *= a; 0198 h *= d; 0199 } 0200 0201 IRect Matrix::toScreen (const SRect &rect) const { 0202 return IRect ( 0203 (int) (Single (rect.x () * a) + tx), 0204 (int) (Single (rect.y () * d) + ty), 0205 (int) (rect.width () * a), 0206 (int) (rect.height () * d)); 0207 } 0208 0209 SRect Matrix::toUser (const IRect &rect) const { 0210 if (a > 0.00001 && d > 0.00001) { 0211 return SRect ( 0212 Single ((Single (rect.x ()) - tx) / a), 0213 Single ((Single (rect.y ()) - ty) / d), 0214 rect.width () / a, 0215 rect.height () / d); 0216 } else { 0217 kWarning () << "Not invering " << a << ", " << d << " scale"; 0218 return SRect (); 0219 } 0220 } 0221 0222 void Matrix::transform (const Matrix & matrix) { 0223 // TODO: rotate 0224 a *= matrix.a; 0225 d *= matrix.d; 0226 tx = Single (tx * matrix.a) + matrix.tx; 0227 ty = Single (ty * matrix.d) + matrix.ty; 0228 } 0229 0230 void Matrix::scale (float sx, float sy) { 0231 a *= sx; 0232 d *= sy; 0233 tx *= sx; 0234 ty *= sy; 0235 } 0236 0237 void Matrix::translate (Single x, Single y) { 0238 tx += x; 0239 ty += y; 0240 } 0241 0242 //----------------------------------------------------------------------------- 0243 0244 KDE_NO_CDTOR_EXPORT Node::Node (NodePtr & d, short _id) 0245 : m_doc (d), state (state_init), id (_id), 0246 auxiliary_node (false), open (false) {} 0247 0248 Node::~Node () { 0249 clear (); 0250 } 0251 0252 Document * Node::document () { 0253 return convertNode <Document> (m_doc); 0254 } 0255 0256 Mrl * Node::mrl () { 0257 return 0L; 0258 } 0259 0260 const char * Node::nodeName () const { 0261 return "node"; 0262 } 0263 0264 void Node::setState (State nstate) { 0265 if (state != nstate && (state_init == nstate || state != state_resetting)) { 0266 State old = state; 0267 state = nstate; 0268 if (document ()->notify_listener) 0269 document()->notify_listener->stateElementChanged (this, old, state); 0270 } 0271 } 0272 0273 void Node::activate () { 0274 //kDebug () << nodeName () << " Node::activate"; 0275 setState (state_activated); 0276 if (firstChild ()) 0277 firstChild ()->activate (); // activate only the first 0278 else 0279 finish (); // a quicky :-) 0280 } 0281 0282 void Node::begin () { 0283 if (active ()) { 0284 setState (state_began); 0285 } else 0286 kError () << nodeName() << " begin call on not active element" << endl; 0287 } 0288 0289 void Node::defer () { 0290 if (active ()) { 0291 setState (state_deferred); 0292 } else 0293 kError () << "Node::defer () call on not activated element" << endl; 0294 } 0295 0296 void Node::undefer () { 0297 if (state == state_deferred) { 0298 if (firstChild () && firstChild ()->state > state_init) { 0299 state = state_began; 0300 } else { 0301 setState (state_activated); 0302 activate (); 0303 } 0304 } else 0305 kWarning () << nodeName () << " call on not deferred element"; 0306 } 0307 0308 void Node::finish () { 0309 if (active ()) { 0310 setState (state_finished); 0311 if (m_parent) 0312 document ()->post (m_parent, new Posting (this, MsgChildFinished)); 0313 else 0314 deactivate (); // document deactivates itself on finish 0315 } else 0316 kWarning () <<"Node::finish () call on not active element"; 0317 } 0318 0319 void Node::deactivate () { 0320 //kDebug () << nodeName () << " Node::deactivate"; 0321 bool need_finish (unfinished ()); 0322 if (state_resetting != state) 0323 setState (state_deactivated); 0324 for (NodePtr e = firstChild (); e; e = e->nextSibling ()) { 0325 if (e->state > state_init && e->state < state_deactivated) 0326 e->deactivate (); 0327 else 0328 break; // remaining not yet activated 0329 } 0330 if (need_finish && m_parent && m_parent->active ()) 0331 document ()->post (m_parent, new Posting (this, MsgChildFinished)); 0332 } 0333 0334 void Node::reset () { 0335 //kDebug () << nodeName () << " Node::reset"; 0336 if (active ()) { 0337 setState (state_resetting); 0338 deactivate (); 0339 } 0340 setState (state_init); 0341 for (NodePtr e = firstChild (); e; e = e->nextSibling ()) { 0342 if (e->state != state_init) 0343 e->reset (); 0344 // else 0345 // break; // rest not activated yet 0346 } 0347 } 0348 0349 void Node::clear () { 0350 clearChildren (); 0351 } 0352 0353 void Node::clearChildren () { 0354 if (m_doc) 0355 document()->m_tree_version++; 0356 while (m_first_child != m_last_child) { 0357 // avoid stack abuse with 10k children derefing each other 0358 m_last_child->m_parent = 0L; 0359 m_last_child = m_last_child->m_prev; 0360 m_last_child->m_next = 0L; 0361 } 0362 if (m_first_child) 0363 m_first_child->m_parent = 0L; 0364 m_first_child = m_last_child = 0L; 0365 } 0366 0367 template <> 0368 void TreeNode<Node>::appendChild (Node *c) { 0369 static_cast <Node *> (this)->document()->m_tree_version++; 0370 ASSERT (!c->parentNode ()); 0371 appendChildImpl (c); 0372 } 0373 0374 template <> 0375 void TreeNode<Node>::insertBefore (Node *c, Node *b) { 0376 ASSERT (!c->parentNode ()); 0377 static_cast <Node *> (this)->document()->m_tree_version++; 0378 insertBeforeImpl (c, b); 0379 } 0380 0381 template <> 0382 void TreeNode<Node>::removeChild (NodePtr c) { 0383 static_cast <Node *> (this)->document()->m_tree_version++; 0384 removeChildImpl (c); 0385 } 0386 0387 KDE_NO_EXPORT void Node::replaceChild (NodePtr _new, NodePtr old) { 0388 document()->m_tree_version++; 0389 if (old->m_prev) { 0390 old->m_prev->m_next = _new; 0391 _new->m_prev = old->m_prev; 0392 old->m_prev = 0L; 0393 } else { 0394 _new->m_prev = 0L; 0395 m_first_child = _new; 0396 } 0397 if (old->m_next) { 0398 old->m_next->m_prev = _new; 0399 _new->m_next = old->m_next; 0400 old->m_next = 0L; 0401 } else { 0402 _new->m_next = 0L; 0403 m_last_child = _new; 0404 } 0405 _new->m_parent = this; 0406 old->m_parent = 0L; 0407 } 0408 0409 Node *Node::childFromTag (const QString &) { 0410 return NULL; 0411 } 0412 0413 KDE_NO_EXPORT void Node::characterData (const QString & s) { 0414 document()->m_tree_version++; 0415 if (!m_last_child || m_last_child->id != id_node_text) 0416 appendChild (new TextNode (m_doc, s)); 0417 else 0418 convertNode <TextNode> (m_last_child)->appendText (s); 0419 } 0420 0421 void Node::normalize () { 0422 Node *e = firstChild (); 0423 while (e) { 0424 Node *tmp = e->nextSibling (); 0425 if (!e->isElementNode () && e->id == id_node_text) { 0426 QString val = e->nodeValue ().simplified (); 0427 if (val.isEmpty ()) 0428 removeChild (e); 0429 else 0430 static_cast <TextNode *> (e)->setText (val); 0431 } else 0432 e->normalize (); 0433 e = tmp; 0434 } 0435 } 0436 0437 static void getInnerText (const Node *p, QTextStream & out) { 0438 for (Node *e = p->firstChild (); e; e = e->nextSibling ()) { 0439 if (e->id == id_node_text || e->id == id_node_cdata) 0440 out << e->nodeValue (); 0441 else 0442 getInnerText (e, out); 0443 } 0444 } 0445 0446 QString Node::innerText () const { 0447 QString buf; 0448 QTextStream out (&buf, QIODevice::WriteOnly); 0449 getInnerText (this, out); 0450 return buf; 0451 } 0452 0453 static void getOuterXML (const Node *p, QTextStream & out, int depth) { 0454 if (!p->isElementNode ()) { // #text or #cdata 0455 if (p->id == id_node_cdata) 0456 out << "<![CDATA[" << p->nodeValue () << "]]>" << QChar ('\n'); 0457 else 0458 out << XMLStringlet (p->nodeValue ()) << QChar ('\n'); 0459 } else { 0460 const Element *e = static_cast <const Element *> (p); 0461 QString indent (QString ().fill (QChar (' '), depth)); 0462 out << indent << QChar ('<') << XMLStringlet (e->nodeName ()); 0463 for (Attribute *a = e->attributes().first(); a; a = a->nextSibling()) 0464 out << " " << XMLStringlet (a->name ().toString ()) << 0465 "=\"" << XMLStringlet (a->value ()) << "\""; 0466 if (e->hasChildNodes ()) { 0467 out << QChar ('>') << QChar ('\n'); 0468 for (Node *c = e->firstChild (); c; c = c->nextSibling ()) 0469 getOuterXML (c, out, depth + 1); 0470 out << indent << QString("</") << XMLStringlet (e->nodeName()) << 0471 QChar ('>') << QChar ('\n'); 0472 } else 0473 out << QString ("/>") << QChar ('\n'); 0474 } 0475 } 0476 0477 QString Node::innerXML () const { 0478 QString buf; 0479 QTextStream out (&buf, QIODevice::WriteOnly); 0480 for (Node *e = firstChild (); e; e = e->nextSibling ()) 0481 getOuterXML (e, out, 0); 0482 return buf; 0483 } 0484 0485 QString Node::outerXML () const { 0486 QString buf; 0487 QTextStream out (&buf, QIODevice::WriteOnly); 0488 getOuterXML (this, out, 0); 0489 return buf; 0490 } 0491 0492 Node::PlayType Node::playType () { 0493 return play_type_none; 0494 } 0495 0496 void Node::opened () { 0497 open = true; 0498 } 0499 0500 void Node::closed () { 0501 open = false; 0502 } 0503 0504 void Node::message (MessageType msg, void *content) { 0505 switch (msg) { 0506 0507 case MsgChildFinished: { 0508 Posting *post = (Posting *) content; 0509 if (unfinished ()) { 0510 if (post->source && post->source->state == state_finished) 0511 post->source->deactivate (); 0512 if (post->source && post->source->nextSibling ()) 0513 post->source->nextSibling ()->activate (); 0514 else 0515 finish (); // we're done 0516 } 0517 break; 0518 } 0519 0520 default: 0521 break; 0522 } 0523 } 0524 0525 void *Node::role (RoleType msg, void *) { 0526 switch (msg) { 0527 case RoleReady: 0528 return MsgBool (true); 0529 default: 0530 break; 0531 } 0532 return NULL; 0533 } 0534 0535 KDE_NO_EXPORT void Node::deliver (MessageType msg, void *content) { 0536 ConnectionList *nl = nodeMessageReceivers (this, msg); 0537 if (nl) 0538 for (Connection *c = nl->first(); c; c = nl->next ()) 0539 if (c->connecter) 0540 c->connecter->message (msg, content); 0541 } 0542 0543 void Node::accept (Visitor * v) { 0544 v->visit (this); 0545 } 0546 0547 QString Node::nodeValue () const { 0548 return innerText ().trimmed (); 0549 } 0550 0551 //----------------------------------------------------------------------------- 0552 0553 namespace { 0554 struct KMPLAYER_NO_EXPORT ParamValue { 0555 QString val; 0556 QStringList * modifications; 0557 ParamValue (const QString & v) : val (v), modifications (0L) {} 0558 ~ParamValue () { delete modifications; } 0559 QString value (); 0560 void setValue (const QString & v) { val = v; } 0561 }; 0562 typedef QMap <TrieString, ParamValue *> ParamMap; 0563 } 0564 0565 namespace KMPlayer { 0566 class KMPLAYER_NO_EXPORT ElementPrivate { 0567 public: 0568 ~ElementPrivate (); 0569 ParamMap params; 0570 void clear (); 0571 }; 0572 } 0573 0574 QString ParamValue::value() { 0575 return modifications && modifications->size () 0576 ? modifications->back () : val; 0577 } 0578 0579 KDE_NO_CDTOR_EXPORT ElementPrivate::~ElementPrivate () { 0580 clear (); 0581 } 0582 0583 KDE_NO_EXPORT void ElementPrivate::clear () { 0584 const ParamMap::iterator e = params.end (); 0585 for (ParamMap::iterator i = params.begin (); i != e; ++i) 0586 delete i.value (); 0587 params.clear (); 0588 } 0589 0590 Element::Element (NodePtr & d, short id) 0591 : Node (d, id), d (new ElementPrivate) {} 0592 0593 Element::~Element () { 0594 delete d; 0595 } 0596 0597 void Element::setParam (const TrieString &name, const QString &val, int *mid) { 0598 ParamValue * pv = d->params [name]; 0599 if (!pv) { 0600 pv = new ParamValue (mid ? getAttribute (name) : val); 0601 d->params.insert (name, pv); 0602 } 0603 if (mid) { 0604 if (!pv->modifications) 0605 pv->modifications = new QStringList; 0606 if (*mid >= 0 && *mid < int (pv->modifications->size ())) { 0607 (*pv->modifications) [*mid] = val; 0608 } else { 0609 *mid = pv->modifications->size (); 0610 pv->modifications->push_back (val); 0611 } 0612 } else { 0613 pv->setValue (val); 0614 } 0615 parseParam (name, val); 0616 } 0617 0618 QString Element::param (const TrieString & name) { 0619 ParamValue * pv = d->params [name]; 0620 if (pv) 0621 return pv->value (); 0622 return getAttribute (name); 0623 } 0624 0625 void Element::resetParam (const TrieString &name, int mid) { 0626 ParamValue * pv = d->params [name]; 0627 if (pv && pv->modifications) { 0628 if (int (pv->modifications->size ()) > mid && mid > -1) { 0629 (*pv->modifications) [mid] = QString (); 0630 while (pv->modifications->size () > 0 && 0631 pv->modifications->back ().isNull ()) 0632 pv->modifications->pop_back (); 0633 } 0634 QString val = pv->value (); 0635 if (pv->modifications->size () == 0) { 0636 delete pv->modifications; 0637 pv->modifications = 0L; 0638 if (val.isNull ()) { 0639 delete pv; 0640 d->params.remove (name); 0641 } 0642 } 0643 parseParam (name, val); 0644 } else 0645 kError () << "resetting " << name.toString() << " that doesn't exists" << endl; 0646 } 0647 0648 void Element::setAttribute (const TrieString & name, const QString & value) { 0649 for (Attribute *a = m_attributes.first (); a; a = a->nextSibling ()) 0650 if (name == a->name ()) { 0651 if (value.isNull ()) 0652 m_attributes.remove (a); 0653 else 0654 a->setValue (value); 0655 return; 0656 } 0657 if (!value.isNull ()) 0658 m_attributes.append (new Attribute (TrieString (), name, value)); 0659 } 0660 0661 QString Element::getAttribute (const TrieString & name) { 0662 for (Attribute *a = m_attributes.first (); a; a = a->nextSibling ()) 0663 if (name == a->name ()) 0664 return a->value (); 0665 return QString (); 0666 } 0667 0668 void Element::init () { 0669 d->clear(); 0670 for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) { 0671 QString v = a->value (); 0672 int p = v.indexOf ('{'); 0673 if (p > -1) { 0674 int q = v.indexOf ('}', p + 1); 0675 if (q > -1) 0676 continue; 0677 } 0678 parseParam (a->name (), v); 0679 } 0680 } 0681 0682 void Element::reset () { 0683 d->clear(); 0684 Node::reset (); 0685 } 0686 0687 void Element::clear () { 0688 m_attributes = AttributeList (); // remove attributes 0689 d->clear(); 0690 Node::clear (); 0691 } 0692 0693 void Element::setAttributes (const AttributeList &attrs) { 0694 m_attributes = attrs; 0695 } 0696 0697 void Element::accept (Visitor * v) { 0698 v->visit (this); 0699 } 0700 0701 //----------------------------------------------------------------------------- 0702 0703 Attribute::Attribute (const TrieString &ns, const TrieString &n, const QString &v) 0704 : m_namespace (ns), m_name (n), m_value (v) {} 0705 0706 void Attribute::setName (const TrieString & n) { 0707 m_name = n; 0708 } 0709 0710 void Attribute::setValue (const QString & v) { 0711 m_value = v; 0712 } 0713 0714 //----------------------------------------------------------------------------- 0715 0716 QString PlaylistRole::caption () const { 0717 return title; 0718 } 0719 0720 void PlaylistRole::setCaption (const QString &s) { 0721 title = s; 0722 } 0723 0724 //----------------------------------------------------------------------------- 0725 0726 static bool hasMrlChildren (const NodePtr & e) { 0727 for (Node *c = e->firstChild (); c; c = c->nextSibling ()) 0728 if (c->isPlayable () || hasMrlChildren (c)) 0729 return true; 0730 return false; 0731 } 0732 0733 Mrl::Mrl (NodePtr & d, short id) 0734 : Element (d, id), cached_ismrl_version (~0), 0735 media_info (NULL), 0736 aspect (0), repeat (0), 0737 view_mode (SingleMode), 0738 resolved (false), bookmarkable (true), access_granted (false) {} 0739 0740 Mrl::~Mrl () { 0741 delete media_info; 0742 } 0743 0744 Node::PlayType Mrl::playType () { 0745 if (cached_ismrl_version != document()->m_tree_version) { 0746 bool ismrl = !hasMrlChildren (this); 0747 cached_play_type = ismrl ? play_type_unknown : play_type_none; 0748 cached_ismrl_version = document()->m_tree_version; 0749 } 0750 return cached_play_type; 0751 } 0752 0753 QString Mrl::absolutePath () { 0754 QString path = src; 0755 if (!path.isEmpty() && !path.startsWith ("tv:/")) { 0756 for (Node *e = parentNode (); e; e = e->parentNode ()) { 0757 Mrl * mrl = e->mrl (); 0758 if (mrl && !mrl->src.isEmpty () && mrl->src != src) { 0759 path = KURL (mrl->absolutePath (), src).url (); 0760 break; 0761 } 0762 } 0763 } 0764 return path; 0765 } 0766 0767 Node *Mrl::childFromTag (const QString & tag) { 0768 Node * elm = fromXMLDocumentTag (m_doc, tag); 0769 if (elm) 0770 return elm; 0771 return NULL; 0772 } 0773 0774 Mrl * Mrl::mrl () { 0775 return this; 0776 } 0777 0778 void Mrl::message (MessageType msg, void *content) { 0779 switch (msg) { 0780 case MsgMediaReady: 0781 resolved = true; 0782 if (state == state_deferred) { 0783 if (isPlayable ()) { 0784 setState (state_activated); 0785 begin (); 0786 } else { 0787 Element::activate (); 0788 } 0789 } 0790 break; 0791 0792 case MsgMediaFinished: 0793 if (state == state_deferred && 0794 !isPlayable () && firstChild ()) {//if backend added child links 0795 state = state_activated; 0796 firstChild ()->activate (); 0797 } else if (unfinished ()) { 0798 finish (); 0799 } 0800 break; 0801 0802 default: 0803 break; 0804 } 0805 Node::message (msg, content); 0806 } 0807 0808 void *Mrl::role (RoleType msg, void *content) { 0809 switch (msg) { 0810 0811 case RoleChildDisplay: 0812 for (Node *p = parentNode (); p; p = p->parentNode ()) 0813 if (p->mrl ()) 0814 return p->role (msg, content); 0815 return NULL; 0816 0817 case RolePlaylist: 0818 if (title.isEmpty ()) 0819 title = src; 0820 return !title.isEmpty () ? (PlaylistRole *) this : NULL; 0821 0822 default: 0823 break; 0824 } 0825 return Node::role (msg, content); 0826 } 0827 0828 void Mrl::activate () { 0829 if (!resolved && isPlayable ()) { 0830 setState (state_deferred); 0831 media_info = new MediaInfo (this, MediaManager::AudioVideo); 0832 resolved = media_info->wget (absolutePath ()); 0833 if (resolved && isPlayable ()) { 0834 // ignore the MsgMediaReady message redirection 0835 setState (state_activated); 0836 begin (); 0837 } 0838 } else if (isPlayable ()) { 0839 setState (state_activated); 0840 begin (); 0841 } else { 0842 Element::activate (); 0843 } 0844 } 0845 0846 void Mrl::begin () { 0847 kDebug () << nodeName () << src << this; 0848 if (!src.isEmpty ()) { 0849 if (!media_info) 0850 media_info = new MediaInfo (this, MediaManager::AudioVideo); 0851 if (!media_info->media) 0852 media_info->create (); 0853 if (media_info->media->play ()) 0854 setState (state_began); 0855 else 0856 deactivate (); 0857 } else { 0858 deactivate (); // nothing to activate 0859 } 0860 } 0861 0862 void Mrl::defer () { 0863 if (media_info && media_info->media) 0864 media_info->media->pause (); 0865 Node::defer (); 0866 } 0867 0868 void Mrl::undefer () { 0869 if (media_info && media_info->media) { 0870 media_info->media->unpause (); 0871 setState (state_began); 0872 } else { 0873 Node::undefer (); 0874 } 0875 } 0876 0877 void Mrl::deactivate () { 0878 delete media_info; 0879 media_info = NULL; 0880 Node::deactivate (); 0881 } 0882 0883 void Mrl::parseParam (const TrieString & para, const QString & val) { 0884 if (para == Ids::attr_src && !src.startsWith ("#")) { 0885 QString abs = absolutePath (); 0886 if (abs != src) 0887 src = val; 0888 else 0889 src = KURL (abs, val).url (); 0890 for (NodePtr c = firstChild (); c; c = c->nextSibling ()) 0891 if (c->mrl () && c->mrl ()->opener.ptr () == this) { 0892 removeChild (c); 0893 c->reset(); 0894 } 0895 resolved = false; 0896 } 0897 } 0898 0899 unsigned int Mrl::parseTimeString (const QString &ts) { 0900 QString s (ts); 0901 int multiply[] = { 1, 60, 60 * 60, 24 * 60 * 60, 0 }; 0902 int mpos = 0; 0903 double d = 0; 0904 while (!s.isEmpty () && multiply[mpos]) { 0905 int p = s.lastIndexOf (QChar (':')); 0906 QString t = p >= 0 ? s.mid (p + 1) : s; 0907 d += multiply[mpos++] * t.toDouble(); 0908 s = p >= 0 ? s.left (p) : QString (); 0909 } 0910 if (d > 0.01) 0911 return (unsigned int) (d * 100); 0912 return 0; 0913 } 0914 0915 //----------------------%<----------------------------------------------------- 0916 0917 EventData::EventData (Node *t, Posting *e, EventData *n) 0918 : target (t), event (e), next (n) {} 0919 0920 EventData::~EventData () { 0921 delete event; 0922 } 0923 //----------------------------------------------------------------------------- 0924 0925 Postpone::Postpone (NodePtr doc) : m_doc (doc) { 0926 if (m_doc) 0927 m_doc->document ()->timeOfDay (postponed_time); 0928 } 0929 0930 Postpone::~Postpone () { 0931 if (m_doc) 0932 m_doc->document ()->proceed (postponed_time); 0933 } 0934 0935 //----------------------------------------------------------------------------- 0936 0937 static NodePtr dummy_element; 0938 0939 Document::Document (const QString & s, PlayListNotify * n) 0940 : Mrl (dummy_element, id_node_document), 0941 notify_listener (n), 0942 m_tree_version (0), 0943 event_queue (NULL), 0944 paused_queue (NULL), 0945 cur_event (NULL), 0946 cur_timeout (-1) { 0947 m_doc = m_self; // just-in-time setting fragile m_self to m_doc 0948 src = s; 0949 } 0950 0951 Document::~Document () { 0952 kDebug () << "~Document " << src; 0953 } 0954 0955 static Node *getElementByIdImpl (Node *n, const QString & id, bool inter) { 0956 NodePtr elm; 0957 if (!n->isElementNode ()) 0958 return NULL; 0959 Element *e = static_cast <Element *> (n); 0960 if (e->getAttribute (Ids::attr_id) == id) 0961 return n; 0962 for (Node *c = e->firstChild (); c; c = c->nextSibling ()) { 0963 if (!inter && c->mrl () && c->mrl ()->opener.ptr () == n) 0964 continue; 0965 if ((elm = getElementByIdImpl (c, id, inter))) 0966 break; 0967 } 0968 return elm; 0969 } 0970 0971 Node *Document::getElementById (const QString & id) { 0972 return getElementByIdImpl (this, id, true); 0973 } 0974 0975 Node *Document::getElementById (Node *n, const QString & id, bool inter) { 0976 return getElementByIdImpl (n, id, inter); 0977 } 0978 0979 Node *Document::childFromTag (const QString & tag) { 0980 Node * elm = fromXMLDocumentTag (m_doc, tag); 0981 if (elm) 0982 return elm; 0983 return NULL; 0984 } 0985 0986 void Document::dispose () { 0987 clear (); 0988 m_doc = 0L; 0989 } 0990 0991 void Document::activate () { 0992 first_event_time.tv_sec = 0; 0993 last_event_time = 0; 0994 Mrl::activate (); 0995 } 0996 0997 void Document::defer () { 0998 if (resolved) 0999 postpone_lock = postpone (); 1000 Mrl::defer (); 1001 } 1002 1003 void Document::undefer () { 1004 postpone_lock = 0L; 1005 Mrl::undefer (); 1006 } 1007 1008 void Document::reset () { 1009 Mrl::reset (); 1010 if (event_queue) { 1011 if (notify_listener) 1012 notify_listener->setTimeout (-1); 1013 while (event_queue) { 1014 EventData *ed = event_queue; 1015 event_queue = ed->next; 1016 delete ed; 1017 } 1018 cur_timeout = -1; 1019 } 1020 postpone_lock = 0L; 1021 } 1022 1023 static inline 1024 int diffTime (const struct timeval & tv1, const struct timeval & tv2) { 1025 //kDebug () << "diffTime sec:" << ((tv1.tv_sec - tv2.tv_sec) * 1000) << " usec:" << ((tv1.tv_usec - tv2.tv_usec) /1000); 1026 return (tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) /1000; 1027 } 1028 1029 static inline void addTime (struct timeval & tv, int ms) { 1030 if (ms >= 1000) { 1031 tv.tv_sec += ms / 1000; 1032 ms %= 1000; 1033 } 1034 tv.tv_sec += (tv.tv_usec + ms*1000) / 1000000; 1035 tv.tv_usec = (tv.tv_usec + ms*1000) % 1000000; 1036 } 1037 1038 KDE_NO_CDTOR_EXPORT UpdateEvent::UpdateEvent (Document *doc, unsigned int skip) 1039 : skipped_time (skip) { 1040 struct timeval tv; 1041 doc->timeOfDay (tv); 1042 cur_event_time = doc->last_event_time; 1043 } 1044 1045 //----------------------------------------------------------------------------- 1046 /*static inline void subtractTime (struct timeval & tv, int ms) { 1047 int sec = ms / 1000; 1048 int msec = ms % 1000; 1049 tv.tv_sec -= sec; 1050 if (tv.tv_usec / 1000 >= msec) { 1051 tv.tv_usec -= msec * 1000; 1052 } else { 1053 tv.tv_sec--; 1054 tv.tv_usec = 1000000 - (msec - tv.tv_usec / 1000 ); 1055 } 1056 }*/ 1057 1058 void Document::timeOfDay (struct timeval & tv) { 1059 gettimeofday (&tv, 0L); 1060 if (!first_event_time.tv_sec) { 1061 first_event_time = tv; 1062 last_event_time = 0; 1063 } else { 1064 last_event_time = diffTime (tv, first_event_time); 1065 } 1066 } 1067 1068 static bool postponedSensible (MessageType msg) { 1069 return msg == MsgEventTimer || 1070 msg == MsgEventStarted || 1071 msg == MsgEventStopped; 1072 } 1073 1074 void Document::insertPosting (Node *n, Posting *e, const struct timeval &tv) { 1075 if (!notify_listener) 1076 return; 1077 bool postponed_sensible = postponedSensible (e->message); 1078 EventData *prev = NULL; 1079 EventData *ed = event_queue; 1080 for (; ed; ed = ed->next) { 1081 int diff = diffTime (ed->timeout, tv); 1082 bool psens = postponedSensible (ed->event->message); 1083 if ((diff > 0 && postponed_sensible == psens) || (!postponed_sensible && psens)) 1084 break; 1085 prev = ed; 1086 } 1087 ed = new EventData (n, e, ed); 1088 ed->timeout = tv; 1089 if (prev) 1090 prev->next = ed; 1091 else 1092 event_queue = ed; 1093 //kDebug () << "setTimeout " << ms << " at:" << pos << " tv:" << tv.tv_sec << "." << tv.tv_usec; 1094 } 1095 1096 void Document::setNextTimeout (const struct timeval &now) { 1097 if (!cur_event) { // if we're not processing events 1098 int timeout = 0x7FFFFFFF; 1099 if (event_queue && active () && 1100 (!postpone_ref || !postponedSensible (event_queue->event->message))) 1101 timeout = diffTime (event_queue->timeout, now); 1102 timeout = 0x7FFFFFFF != timeout ? (timeout > 0 ? timeout : 0) : -1; 1103 if (timeout != cur_timeout) { 1104 cur_timeout = timeout; 1105 notify_listener->setTimeout (cur_timeout); 1106 } 1107 } 1108 } 1109 1110 void Document::updateTimeout () { 1111 if (!postpone_ref && event_queue && notify_listener) { 1112 struct timeval now; 1113 if (cur_event) 1114 now = cur_event->timeout; 1115 else 1116 timeOfDay (now); 1117 setNextTimeout (now); 1118 } 1119 } 1120 1121 Posting *Document::post (Node *n, Posting *e) { 1122 int ms = e->message == MsgEventTimer 1123 ? static_cast<TimerPosting *>(e)->milli_sec 1124 : 0; 1125 struct timeval now, tv; 1126 if (cur_event) 1127 now = cur_event->timeout; 1128 else 1129 timeOfDay (now); 1130 tv = now; 1131 addTime (tv, ms); 1132 insertPosting (n, e, tv); 1133 if (postpone_ref || event_queue->event == e) 1134 setNextTimeout (now); 1135 return e; 1136 } 1137 1138 static EventData *findPosting (EventData *queue, EventData **prev, const Posting *e) { 1139 *prev = NULL; 1140 for (EventData *ed = queue; ed; ed = ed->next) { 1141 if (e == ed->event) 1142 return ed; 1143 *prev = ed; 1144 } 1145 return NULL; 1146 } 1147 1148 void Document::cancelPosting (Posting *e) { 1149 if (cur_event && cur_event->event == e) { 1150 delete cur_event->event; 1151 cur_event->event = NULL; 1152 } else { 1153 EventData *prev; 1154 EventData **queue = &event_queue; 1155 EventData *ed = findPosting (event_queue, &prev, e); 1156 if (!ed) { 1157 ed = findPosting (paused_queue, &prev, e); 1158 queue = &paused_queue; 1159 } 1160 if (ed) { 1161 if (prev) { 1162 prev->next = ed->next; 1163 } else { 1164 *queue = ed->next; 1165 if (!cur_event && queue == &event_queue) { 1166 struct timeval now; 1167 if (event_queue) // save a sys call 1168 timeOfDay (now); 1169 setNextTimeout (now); 1170 } 1171 } 1172 delete ed; 1173 } else { 1174 kError () << "Posting not found"; 1175 } 1176 } 1177 } 1178 1179 void Document::pausePosting (Posting *e) { 1180 if (cur_event && cur_event->event == e) { 1181 paused_queue = new EventData (cur_event->target, cur_event->event, paused_queue); 1182 paused_queue->timeout = cur_event->timeout; 1183 cur_event->event = NULL; 1184 } else { 1185 EventData *prev; 1186 EventData *ed = findPosting (event_queue, &prev, e); 1187 if (ed) { 1188 if (prev) 1189 prev->next = ed->next; 1190 else 1191 event_queue = ed->next; 1192 ed->next = paused_queue; 1193 paused_queue = ed; 1194 } else { 1195 kError () << "pauseEvent not found"; 1196 } 1197 } 1198 } 1199 1200 void Document::unpausePosting (Posting *e, int ms) { 1201 EventData *prev; 1202 EventData *ed = findPosting (paused_queue, &prev, e); 1203 if (ed) { 1204 if (prev) 1205 prev->next = ed->next; 1206 else 1207 paused_queue = ed->next; 1208 addTime (ed->timeout, ms); 1209 insertPosting (ed->target, ed->event, ed->timeout); 1210 ed->event = NULL; 1211 delete ed; 1212 } else { 1213 kError () << "pausePosting not found"; 1214 } 1215 } 1216 1217 void Document::timer () { 1218 struct timeval now; 1219 cur_event = event_queue; 1220 if (cur_event) { 1221 NodePtrW guard = this; 1222 struct timeval start = cur_event->timeout; 1223 timeOfDay (now); 1224 1225 // handle max 100 timeouts with timeout set to now 1226 for (int i = 0; i < 100 && active (); ++i) { 1227 if (postpone_ref && postponedSensible (cur_event->event->message)) 1228 break; 1229 // remove from queue 1230 event_queue = cur_event->next; 1231 1232 if (!cur_event->target) { 1233 // some part of document has gone and didn't remove timer 1234 kError () << "spurious timer" << endl; 1235 } else { 1236 EventData *ed = cur_event; 1237 cur_event->target->message (cur_event->event->message, cur_event->event); 1238 if (!guard) { 1239 delete ed; 1240 return; 1241 } 1242 if (cur_event->event && cur_event->event->message == MsgEventTimer) { 1243 TimerPosting *te = static_cast <TimerPosting *> (cur_event->event); 1244 if (te->interval) { 1245 te->interval = false; // reset interval 1246 addTime (cur_event->timeout, te->milli_sec); 1247 insertPosting (cur_event->target, 1248 cur_event->event, 1249 cur_event->timeout); 1250 cur_event->event = NULL; 1251 } 1252 } 1253 } 1254 delete cur_event; 1255 cur_event = event_queue; 1256 if (!cur_event || diffTime (cur_event->timeout, start) > 5) 1257 break; 1258 } 1259 cur_event = NULL; 1260 } 1261 setNextTimeout (now); 1262 } 1263 1264 PostponePtr Document::postpone () { 1265 if (postpone_ref) 1266 return postpone_ref; 1267 kDebug () << "postpone"; 1268 PostponePtr p = new Postpone (this); 1269 postpone_ref = p; 1270 PostponedEvent event (true); 1271 deliver (MsgEventPostponed, &event); 1272 if (notify_listener) 1273 notify_listener->enableRepaintUpdaters (false, 0); 1274 if (!cur_event) { 1275 struct timeval now; 1276 if (event_queue) // save a sys call 1277 timeOfDay (now); 1278 setNextTimeout (now); 1279 } 1280 return p; 1281 } 1282 1283 void Document::proceed (const struct timeval &postponed_time) { 1284 kDebug () << "proceed"; 1285 postpone_ref = NULL; 1286 struct timeval now; 1287 timeOfDay (now); 1288 int diff = diffTime (now, postponed_time); 1289 if (event_queue) { 1290 for (EventData *ed = event_queue; ed; ed = ed->next) 1291 if (ed->event && postponedSensible (ed->event->message)) 1292 addTime (ed->timeout, diff); 1293 setNextTimeout (now); 1294 } 1295 if (notify_listener) 1296 notify_listener->enableRepaintUpdaters (true, diff); 1297 PostponedEvent event (false); 1298 deliver (MsgEventPostponed, &event); 1299 } 1300 1301 void *Document::role (RoleType msg, void *content) { 1302 if (RoleReceivers == msg) { 1303 MessageType m = (MessageType) (long) content; 1304 if (MsgEventPostponed == m) 1305 return &m_PostponedListeners; 1306 } 1307 return Mrl::role (msg, content); 1308 } 1309 1310 //----------------------------------------------------------------------------- 1311 1312 KDE_NO_CDTOR_EXPORT TextNode::TextNode (NodePtr & d, const QString & s, short i) 1313 : Node (d, i), text (s) {} 1314 1315 void TextNode::appendText (const QString & s) { 1316 text += s; 1317 } 1318 1319 QString TextNode::nodeValue () const { 1320 return text; 1321 } 1322 1323 //----------------------------------------------------------------------------- 1324 1325 KDE_NO_CDTOR_EXPORT CData::CData (NodePtr & d, const QString & s) 1326 : TextNode (d, s, id_node_cdata) {} 1327 1328 //----------------------------------------------------------------------------- 1329 1330 DarkNode::DarkNode (NodePtr & d, const QByteArray &n, short id) 1331 : Element (d, id), name (n) { 1332 } 1333 1334 Node *DarkNode::childFromTag (const QString & tag) { 1335 return new DarkNode (m_doc, tag.toUtf8 ()); 1336 } 1337 1338 //----------------------------------------------------------------------------- 1339 1340 GenericURL::GenericURL (NodePtr & d, const QString & s, const QString & name) 1341 : Mrl (d, id_node_playlist_item) { 1342 src = s; 1343 if (!src.isEmpty ()) 1344 setAttribute (Ids::attr_src, src); 1345 title = name; 1346 } 1347 1348 KDE_NO_EXPORT void GenericURL::closed () { 1349 if (src.isEmpty ()) 1350 src = getAttribute (Ids::attr_src); 1351 Mrl::closed (); 1352 } 1353 1354 //----------------------------------------------------------------------------- 1355 1356 GenericMrl::GenericMrl (NodePtr & d, const QString &s, const QString &name, const QByteArray &tag) 1357 : Mrl (d, id_node_playlist_item), node_name (tag) { 1358 src = s; 1359 if (!src.isEmpty ()) 1360 setAttribute (Ids::attr_src, src); 1361 title = name; 1362 if (!name.isEmpty ()) 1363 setAttribute (Ids::attr_name, name); 1364 } 1365 1366 void GenericMrl::closed () { 1367 if (src.isEmpty ()) { 1368 src = getAttribute (Ids::attr_src); 1369 if (src.isEmpty ()) 1370 src = getAttribute (Ids::attr_url); 1371 } 1372 if (title.isEmpty ()) 1373 title = getAttribute (Ids::attr_name); 1374 Mrl::closed (); 1375 } 1376 1377 void *GenericMrl::role (RoleType msg, void *content) 1378 { 1379 if (RolePlaylist == msg) 1380 return !title.isEmpty () || //return false if no title and only one 1381 previousSibling () || nextSibling () 1382 ? (PlaylistRole *) this : NULL; 1383 return Mrl::role (msg, content); 1384 } 1385 1386 //----------------------------------------------------------------------------- 1387 1388 void Visitor::visit (Element *elm) { 1389 visit (static_cast <Node *> (elm)); 1390 } 1391 1392 void Visitor::visit (TextNode *text) { 1393 visit (static_cast <Node *> (text)); 1394 } 1395 1396 //----------------------------------------------------------------------------- 1397 1398 CacheAllocator::CacheAllocator (size_t s) 1399 : pool ((void**) malloc (10 * sizeof (void *))), size (s), count (0) {} 1400 1401 void *CacheAllocator::alloc () { 1402 return count ? pool[--count] : malloc (size); 1403 } 1404 1405 void CacheAllocator::dealloc (void *p) { 1406 if (count < 10) 1407 pool[count++] = p; 1408 else 1409 free (p); 1410 } 1411 1412 KMPLAYER_EXPORT CacheAllocator *KMPlayer::shared_data_cache_allocator = NULL; 1413 1414 //----------------------------------------------------------------------------- 1415 1416 namespace KMPlayer { 1417 1418 class KMPLAYER_NO_EXPORT DocumentBuilder { 1419 int m_ignore_depth; 1420 bool m_set_opener; 1421 bool m_root_is_first; 1422 NodePtr m_node; 1423 NodePtr m_root; 1424 public: 1425 DocumentBuilder (NodePtr d, bool set_opener); 1426 ~DocumentBuilder () {} 1427 bool startTag (const QString & tag, const AttributeList &attr); 1428 bool endTag (const QString & tag); 1429 bool characterData (const QString & data); 1430 bool cdataData (const QString & data); 1431 #ifdef KMPLAYER_WITH_EXPAT 1432 void cdataStart (); 1433 void cdataEnd (); 1434 private: 1435 bool in_cdata; 1436 QString cdata; 1437 #endif 1438 }; 1439 1440 } // namespace KMPlayer 1441 1442 DocumentBuilder::DocumentBuilder (NodePtr d, bool set_opener) 1443 : m_ignore_depth (0), m_set_opener (set_opener), m_root_is_first (false) 1444 , m_node (d), m_root (d) 1445 #ifdef KMPLAYER_WITH_EXPAT 1446 , in_cdata (false) 1447 #endif 1448 {} 1449 1450 bool DocumentBuilder::startTag(const QString &tag, const AttributeList &attr) { 1451 if (m_ignore_depth) { 1452 m_ignore_depth++; 1453 //kDebug () << "Warning: ignored tag " << tag.latin1 () << " ignore depth = " << m_ignore_depth; 1454 } else if (!m_node) { 1455 return false; // had underflow 1456 } else { 1457 NodePtr n = m_node->childFromTag (tag); 1458 if (!n) { 1459 kDebug () << "Warning: unknown tag " << tag.toLocal8Bit ().constData(); 1460 NodePtr doc = m_root->document (); 1461 n = new DarkNode (doc, tag.toUtf8 ()); 1462 } 1463 //kDebug () << "Found tag " << tag; 1464 if (n->isElementNode ()) 1465 convertNode <Element> (n)->setAttributes (attr); 1466 if (m_node == n && m_node == m_root) 1467 m_root_is_first = true; 1468 else 1469 m_node->appendChild (n); 1470 if (m_set_opener && m_node == m_root) { 1471 Mrl * mrl = n->mrl (); 1472 if (mrl) 1473 mrl->opener = m_root; 1474 } 1475 n->opened (); 1476 m_node = n; 1477 } 1478 return true; 1479 } 1480 1481 bool DocumentBuilder::endTag (const QString & tag) { 1482 if (m_ignore_depth) { // endtag to ignore 1483 m_ignore_depth--; 1484 kDebug () << "Warning: ignored end tag " << " ignore depth = " << m_ignore_depth; 1485 } else if (!m_node) { 1486 return false; // had underflow 1487 } else { // endtag 1488 NodePtr n = m_node; 1489 while (n) { 1490 if (!strcasecmp (n->nodeName (), tag.toLocal8Bit ().constData ()) && 1491 (m_root_is_first || n != m_root)) { 1492 while (n != m_node) { 1493 kWarning() << m_node->nodeName () << " not closed"; 1494 if (m_root == m_node->parentNode ()) 1495 break; 1496 m_node->closed (); 1497 m_node = m_node->parentNode (); 1498 } 1499 break; 1500 } 1501 if (n == m_root) { 1502 if (n == m_node) { 1503 kError () << "m_node == m_doc, stack underflow " << endl; 1504 return false; 1505 } 1506 kWarning () << "endtag: no match " << tag.toLocal8Bit ().constData(); 1507 break; 1508 } else 1509 kWarning () << "tag " << tag << " not " << n->nodeName (); 1510 n = n ->parentNode (); 1511 } 1512 //kDebug () << "end tag " << tag; 1513 m_node->closed (); 1514 m_node = m_node->parentNode (); 1515 } 1516 return true; 1517 } 1518 1519 bool DocumentBuilder::characterData (const QString & data) { 1520 if (!m_ignore_depth && m_node) { 1521 #ifdef KMPLAYER_WITH_EXPAT 1522 if (in_cdata) 1523 cdata += data; 1524 else 1525 #endif 1526 m_node->characterData (data); 1527 } 1528 //kDebug () << "characterData " << d.latin1(); 1529 return !!m_node; 1530 } 1531 1532 bool DocumentBuilder::cdataData (const QString & data) { 1533 if (!m_ignore_depth && m_node) { 1534 NodePtr d = m_node->document (); 1535 m_node->appendChild (new CData (d, data)); 1536 } 1537 //kDebug () << "cdataData " << d.latin1(); 1538 return !!m_node; 1539 } 1540 1541 #ifdef KMPLAYER_WITH_EXPAT 1542 1543 void DocumentBuilder::cdataStart () { 1544 cdata.truncate (0); 1545 in_cdata = true; 1546 } 1547 1548 void DocumentBuilder::cdataEnd () { 1549 cdataData (cdata); 1550 cdata.truncate (0); 1551 in_cdata = false; 1552 } 1553 1554 static void startTag (void *data, const char * tag, const char **attr) { 1555 DocumentBuilder * builder = static_cast <DocumentBuilder *> (data); 1556 AttributeList attributes; 1557 if (attr && attr [0]) { 1558 for (int i = 0; attr[i]; i += 2) 1559 attributes.append (new Attribute ( 1560 TrieString(), 1561 QString::fromUtf8 (attr [i]), 1562 QString::fromUtf8 (attr [i+1]))); 1563 } 1564 builder->startTag (QString::fromUtf8 (tag), attributes); 1565 } 1566 1567 static void endTag (void *data, const char * tag) { 1568 DocumentBuilder * builder = static_cast <DocumentBuilder *> (data); 1569 builder->endTag (QString::fromUtf8 (tag)); 1570 } 1571 1572 static void characterData (void *data, const char *s, int len) { 1573 DocumentBuilder * builder = static_cast <DocumentBuilder *> (data); 1574 char * buf = new char [len + 1]; 1575 strncpy (buf, s, len); 1576 buf[len] = 0; 1577 builder->characterData (QString::fromUtf8 (buf)); 1578 delete [] buf; 1579 } 1580 1581 static void cdataStart (void *data) { 1582 DocumentBuilder * builder = static_cast <DocumentBuilder *> (data); 1583 builder->cdataStart (); 1584 } 1585 1586 static void cdataEnd (void *data) { 1587 DocumentBuilder * builder = static_cast <DocumentBuilder *> (data); 1588 builder->cdataEnd (); 1589 } 1590 1591 KMPLAYER_EXPORT 1592 void KMPlayer::readXML (NodePtr root, QTextStream & in, const QString & firstline, bool set_opener) { 1593 bool ok = true; 1594 DocumentBuilder builder (root, set_opener); 1595 XML_Parser parser = XML_ParserCreate (0L); 1596 XML_SetUserData (parser, &builder); 1597 XML_SetElementHandler (parser, startTag, endTag); 1598 XML_SetCharacterDataHandler (parser, characterData); 1599 XML_SetCdataSectionHandler (parser, cdataStart, cdataEnd); 1600 if (!firstline.isEmpty ()) { 1601 QString str (firstline + QChar ('\n')); 1602 QByteArray ba = str.toUtf8(); 1603 char *buf = ba.data(); 1604 ok = XML_Parse(parser, buf, strlen (buf), false) != XML_STATUS_ERROR; 1605 if (!ok) 1606 kWarning () << XML_ErrorString(XML_GetErrorCode(parser)) << " at " << XML_GetCurrentLineNumber(parser) << " col " << XML_GetCurrentColumnNumber(parser); 1607 } 1608 if (ok && !in.atEnd ()) { 1609 QByteArray ba = in.readAll().toUtf8(); 1610 char *buf = ba.data(); 1611 ok = XML_Parse(parser, buf, strlen (buf), true) != XML_STATUS_ERROR; 1612 if (!ok) 1613 kWarning () << XML_ErrorString(XML_GetErrorCode(parser)) << " at " << XML_GetCurrentLineNumber(parser) << " col " << XML_GetCurrentColumnNumber(parser); 1614 } 1615 XML_ParserFree(parser); 1616 root->normalize (); 1617 //return ok; 1618 } 1619 1620 //----------------------------------------------------------------------------- 1621 #else // KMPLAYER_WITH_EXPAT 1622 1623 namespace { 1624 1625 class KMPLAYER_NO_EXPORT SimpleSAXParser { 1626 enum Token { tok_empty, tok_text, tok_white_space, tok_angle_open, 1627 tok_equal, tok_double_quote, tok_single_quote, tok_angle_close, 1628 tok_slash, tok_exclamation, tok_amp, tok_hash, tok_colon, 1629 tok_semi_colon, tok_question_mark, tok_cdata_start }; 1630 public: 1631 struct TokenInfo { 1632 TokenInfo () : token (tok_empty) {} 1633 void *operator new (size_t); 1634 void operator delete (void *); 1635 Token token; 1636 QString string; 1637 SharedPtr <TokenInfo> next; 1638 }; 1639 typedef SharedPtr <TokenInfo> TokenInfoPtr; 1640 SimpleSAXParser (DocumentBuilder & b) : builder (b), position (0), equal_seen (false), in_dbl_quote (false), in_sngl_quote (false), have_error (false), no_entitity_look_ahead (false), have_next_char (false) {} 1641 virtual ~SimpleSAXParser () {}; 1642 bool parse (QTextStream & d); 1643 private: 1644 QTextStream * data; 1645 DocumentBuilder & builder; 1646 int position; 1647 QChar next_char; 1648 enum State { 1649 InTag, InStartTag, InPITag, InDTDTag, InEndTag, InAttributes, InContent, InCDATA, InComment 1650 }; 1651 struct StateInfo { 1652 StateInfo (State s, SharedPtr <StateInfo> n) : state (s), next (n) {} 1653 State state; 1654 QString data; 1655 SharedPtr <StateInfo> next; 1656 }; 1657 SharedPtr <StateInfo> m_state; 1658 TokenInfoPtr next_token, token, prev_token; 1659 // for element reading 1660 QString tagname; 1661 AttributeList m_attributes; 1662 QString attr_namespace, attr_name, attr_value; 1663 QString cdata; 1664 bool equal_seen; 1665 bool in_dbl_quote; 1666 bool in_sngl_quote; 1667 bool have_error; 1668 bool no_entitity_look_ahead; 1669 bool have_next_char; 1670 1671 bool readTag (); 1672 bool readEndTag (); 1673 bool readAttributes (); 1674 bool readPI (); 1675 bool readDTD (); 1676 bool readCDATA (); 1677 bool readComment (); 1678 bool nextToken (); 1679 void push (); 1680 void push_attribute (); 1681 }; 1682 1683 } // namespace 1684 1685 static CacheAllocator token_pool (sizeof (SimpleSAXParser::TokenInfo)); 1686 1687 inline void *SimpleSAXParser::TokenInfo::operator new (size_t) { 1688 return token_pool.alloc (); 1689 } 1690 1691 inline void SimpleSAXParser::TokenInfo::operator delete (void *p) { 1692 token_pool.dealloc (p); 1693 } 1694 1695 KMPLAYER_EXPORT 1696 void KMPlayer::readXML (NodePtr root, QTextStream & in, const QString & firstline, bool set_opener) { 1697 DocumentBuilder builder (root, set_opener); 1698 root->opened (); 1699 SimpleSAXParser parser (builder); 1700 if (!firstline.isEmpty ()) { 1701 QString str (firstline + QChar ('\n')); 1702 QTextStream fl_in (&str, QIODevice::ReadOnly); 1703 parser.parse (fl_in); 1704 } 1705 if (!in.atEnd ()) 1706 parser.parse (in); 1707 if (root->open) // endTag may have closed it 1708 root->closed (); 1709 for (NodePtr e = root->parentNode (); e; e = e->parentNode ()) { 1710 if (e->open) 1711 break; 1712 e->closed (); 1713 } 1714 //doc->normalize (); 1715 //kDebug () << root->outerXML (); 1716 } 1717 1718 void SimpleSAXParser::push () { 1719 if (next_token->string.size ()) { 1720 prev_token = token; 1721 token = next_token; 1722 if (prev_token) 1723 prev_token->next = token; 1724 next_token = TokenInfoPtr (new TokenInfo); 1725 //kDebug () << "push " << token->string; 1726 } 1727 } 1728 1729 void SimpleSAXParser::push_attribute () { 1730 //kDebug () << "attribute " << attr_name.latin1 () << "=" << attr_value.latin1 (); 1731 m_attributes.append(new Attribute (attr_namespace, attr_name, attr_value)); 1732 attr_namespace.clear (); 1733 attr_name.truncate (0); 1734 attr_value.truncate (0); 1735 equal_seen = in_sngl_quote = in_dbl_quote = false; 1736 } 1737 1738 bool SimpleSAXParser::nextToken () { 1739 TokenInfoPtr cur_token = token; 1740 while (!data->atEnd () && cur_token == token && !(token && token->next)) { 1741 if (have_next_char) 1742 have_next_char = false; 1743 else 1744 *data >> next_char; 1745 bool append_char = true; 1746 if (next_char.isSpace ()) { 1747 if (next_token->token != tok_white_space) 1748 push (); 1749 next_token->token = tok_white_space; 1750 } else if (!next_char.isLetterOrNumber ()) { 1751 if (next_char == QChar ('#')) { 1752 //if (next_token->token == tok_empty) { // check last item on stack & 1753 push (); 1754 next_token->token = tok_hash; 1755 //} 1756 } else if (next_char == QChar ('/')) { 1757 push (); 1758 next_token->token = tok_slash; 1759 } else if (next_char == QChar ('!')) { 1760 push (); 1761 next_token->token = tok_exclamation; 1762 } else if (next_char == QChar ('?')) { 1763 push (); 1764 next_token->token = tok_question_mark; 1765 } else if (next_char == QChar ('<')) { 1766 push (); 1767 next_token->token = tok_angle_open; 1768 } else if (next_char == QChar ('>')) { 1769 push (); 1770 next_token->token = tok_angle_close; 1771 } else if (InAttributes == m_state->state && 1772 next_char == QChar (':')) { 1773 push (); 1774 next_token->token = tok_colon; 1775 } else if (next_char == QChar (';')) { 1776 push (); 1777 next_token->token = tok_semi_colon; 1778 } else if (next_char == QChar ('=')) { 1779 push (); 1780 next_token->token = tok_equal; 1781 } else if (next_char == QChar ('"')) { 1782 push (); 1783 next_token->token = tok_double_quote; 1784 } else if (next_char == QChar ('\'')) { 1785 push (); 1786 next_token->token = tok_single_quote; 1787 } else if (next_char == QChar ('&')) { 1788 push (); 1789 if (no_entitity_look_ahead) { 1790 have_next_char = true; 1791 break; 1792 } 1793 append_char = false; 1794 no_entitity_look_ahead = true; 1795 TokenInfoPtr tmp = token; 1796 TokenInfoPtr prev_tmp = prev_token; 1797 if (nextToken () && token->token == tok_text && 1798 nextToken () && token->token == tok_semi_colon) { 1799 if (prev_token->string == QString ("amp")) 1800 token->string = QChar ('&'); 1801 else if (prev_token->string == QString ("lt")) 1802 token->string = QChar ('<'); 1803 else if (prev_token->string == QString ("gt")) 1804 token->string = QChar ('>'); 1805 else if (prev_token->string == QString ("quot")) 1806 token->string = QChar ('"'); 1807 else if (prev_token->string == QString ("apos")) 1808 token->string = QChar ('\''); 1809 else if (prev_token->string == QString ("copy")) 1810 token->string = QChar (169); 1811 else 1812 token->string = QChar ('?');// TODO lookup more .. 1813 token->token = tok_text; 1814 if (tmp) { // cut out the & xxx ; tokens 1815 tmp->next = token; 1816 token = tmp; 1817 } 1818 //kDebug () << "entity found "<<prev_token->string; 1819 } else if (token->token == tok_hash && 1820 nextToken () && token->token == tok_text && 1821 nextToken () && token->token == tok_semi_colon) { 1822 //kDebug () << "char entity found " << prev_token->string << prev_token->string.toInt (0L, 16); 1823 token->token = tok_text; 1824 if (!prev_token->string.startsWith (QChar ('x'))) 1825 token->string = QChar (prev_token->string.toInt ()); 1826 else 1827 token->string = QChar (prev_token->string.mid (1).toInt (0L, 16)); 1828 if (tmp) { // cut out the '& # xxx ;' tokens 1829 tmp->next = token; 1830 token = tmp; 1831 } 1832 } else { 1833 token = tmp; // restore and insert the lost & token 1834 tmp = TokenInfoPtr (new TokenInfo); 1835 tmp->token = tok_amp; 1836 tmp->string += QChar ('&'); 1837 tmp->next = token->next; 1838 if (token) 1839 token->next = tmp; 1840 else 1841 token = tmp; // hmm 1842 } 1843 no_entitity_look_ahead = false; 1844 prev_token = prev_tmp; 1845 } else if (next_token->token != tok_text) { 1846 push (); 1847 next_token->token = tok_text; 1848 } 1849 } else if (next_token->token != tok_text) { 1850 push (); 1851 next_token->token = tok_text; 1852 } 1853 if (append_char) 1854 next_token->string += next_char; 1855 if (next_token->token == tok_text && 1856 next_char == QChar ('[' ) && next_token->string == "[CDATA[") { 1857 next_token->token = tok_cdata_start; 1858 break; 1859 } 1860 } 1861 if (token == cur_token) { 1862 if (token && token->next) { 1863 prev_token = token; 1864 token = token->next; 1865 } else if (next_token->string.size ()) { 1866 push (); // last token 1867 } else 1868 return false; 1869 return true; 1870 } 1871 return true; 1872 } 1873 1874 bool SimpleSAXParser::readAttributes () { 1875 bool closed = false; 1876 while (true) { 1877 if (!nextToken ()) return false; 1878 //kDebug () << "readAttributes " << token->string.latin1(); 1879 if ((in_dbl_quote && token->token != tok_double_quote) || 1880 (in_sngl_quote && token->token != tok_single_quote)) { 1881 attr_value += token->string; 1882 } else if (token->token == tok_equal) { 1883 if (attr_name.isEmpty ()) 1884 return false; 1885 if (equal_seen) 1886 attr_value += token->string; // EQ=a=2c ??? 1887 //kDebug () << "equal_seen"; 1888 equal_seen = true; 1889 } else if (token->token == tok_white_space) { 1890 if (!attr_value.isEmpty ()) 1891 push_attribute (); 1892 } else if (token->token == tok_single_quote) { 1893 if (!equal_seen) 1894 attr_name += token->string; // D'OH=xxx ??? 1895 else if (in_sngl_quote) { // found one 1896 push_attribute (); 1897 } else if (attr_value.isEmpty ()) 1898 in_sngl_quote = true; 1899 else 1900 attr_value += token->string; 1901 } else if (token->token == tok_colon) { 1902 if (equal_seen) { 1903 attr_value += token->string; 1904 } else { 1905 attr_namespace = attr_name; 1906 attr_name.clear(); 1907 } 1908 } else if (token->token == tok_double_quote) { 1909 if (!equal_seen) 1910 attr_name += token->string; // hmm 1911 else if (in_dbl_quote) { // found one 1912 push_attribute (); 1913 } else if (attr_value.isEmpty ()) 1914 in_dbl_quote = true; 1915 else 1916 attr_value += token->string; 1917 //kDebug () << "in_dbl_quote:"<< in_dbl_quote; 1918 } else if (token->token == tok_slash) { 1919 TokenInfoPtr mark_token = token; 1920 if (nextToken () && 1921 (token->token != tok_white_space || nextToken()) &&//<e / > 1922 token->token == tok_angle_close) { 1923 //kDebug () << "close mark:"; 1924 closed = true; 1925 break; 1926 } else { 1927 token = mark_token; 1928 //kDebug () << "not end mark:"<< equal_seen; 1929 if (equal_seen) 1930 attr_value += token->string; // ABBR=w/o ??? 1931 else 1932 attr_name += token->string; 1933 } 1934 } else if (token->token == tok_angle_close) { 1935 if (!attr_name.isEmpty ()) 1936 push_attribute (); 1937 break; 1938 } else if (equal_seen) { 1939 attr_value += token->string; 1940 } else { 1941 attr_name += token->string; 1942 } 1943 } 1944 m_state = m_state->next; 1945 if (m_state->state == InPITag) { 1946 if (tagname == QString ("xml")) { 1947 /*const AttributeMap::const_iterator e = attr.end (); 1948 for (AttributeMap::const_iterator i = attr.begin (); i != e; ++i) 1949 if (!strcasecmp (i.key ().latin1 (), "encoding")) 1950 kDebug () << "encodeing " << i.data().latin1();*/ 1951 } 1952 } else { 1953 have_error = !builder.startTag (tagname, m_attributes); 1954 if (closed) 1955 have_error &= !builder.endTag (tagname); 1956 //kDebug () << "readTag " << tagname << " closed:" << closed << " ok:" << have_error; 1957 } 1958 m_state = m_state->next; // pop Node or PI 1959 return !have_error; 1960 } 1961 1962 bool SimpleSAXParser::readPI () { 1963 // TODO: <?xml .. encoding="ENC" .. ?> 1964 if (!nextToken ()) return false; 1965 if (token->token == tok_text && !token->string.compare ("xml")) { 1966 m_state = new StateInfo (InAttributes, m_state); 1967 return readAttributes (); 1968 } else { 1969 while (nextToken ()) 1970 if (token->token == tok_angle_close) { 1971 m_state = m_state->next; 1972 return true; 1973 } 1974 } 1975 return false; 1976 } 1977 1978 bool SimpleSAXParser::readDTD () { 1979 //TODO: <!ENTITY ..> 1980 if (!nextToken ()) return false; 1981 if (token->token == tok_text && token->string.startsWith (QString ("--"))) { 1982 m_state = new StateInfo (InComment, m_state->next); // note: pop DTD 1983 return readComment (); 1984 } 1985 //kDebug () << "readDTD: " << token->string.latin1 (); 1986 if (token->token == tok_cdata_start) { 1987 m_state = new StateInfo (InCDATA, m_state->next); // note: pop DTD 1988 if (token->next) { 1989 cdata = token->next->string; 1990 token->next = 0; 1991 } else { 1992 cdata = next_token->string; 1993 next_token->string.truncate (0); 1994 next_token->token = tok_empty; 1995 } 1996 return readCDATA (); 1997 } 1998 while (nextToken ()) 1999 if (token->token == tok_angle_close) { 2000 m_state = m_state->next; 2001 return true; 2002 } 2003 return false; 2004 } 2005 2006 bool SimpleSAXParser::readCDATA () { 2007 while (!data->atEnd ()) { 2008 *data >> next_char; 2009 if (next_char == QChar ('>') && cdata.endsWith (QString ("]]"))) { 2010 cdata.truncate (cdata.size () - 2); 2011 m_state = m_state->next; 2012 if (m_state->state == InContent) 2013 have_error = !builder.cdataData (cdata); 2014 else if (m_state->state == InAttributes) { 2015 if (equal_seen) 2016 attr_value += cdata; 2017 else 2018 attr_name += cdata; 2019 } 2020 cdata.truncate (0); 2021 return true; 2022 } 2023 cdata += next_char; 2024 } 2025 return false; 2026 } 2027 2028 bool SimpleSAXParser::readComment () { 2029 while (nextToken ()) { 2030 if (token->token == tok_angle_close && prev_token) 2031 if (prev_token->string.endsWith (QString ("--"))) { 2032 m_state = m_state->next; 2033 return true; 2034 } 2035 } 2036 return false; 2037 } 2038 2039 bool SimpleSAXParser::readEndTag () { 2040 if (!nextToken ()) return false; 2041 if (token->token == tok_white_space) 2042 if (!nextToken ()) return false; 2043 tagname = token->string; 2044 if (!nextToken ()) return false; 2045 if (token->token == tok_white_space) 2046 if (!nextToken ()) return false; 2047 if (token->token != tok_angle_close) 2048 return false; 2049 have_error = !builder.endTag (tagname); 2050 m_state = m_state->next; 2051 return true; 2052 } 2053 2054 // TODO: <!ENTITY ..> Ӓ 2055 bool SimpleSAXParser::readTag () { 2056 if (!nextToken ()) return false; 2057 if (token->token == tok_exclamation) { 2058 m_state = new StateInfo (InDTDTag, m_state->next); 2059 //kDebug () << "readTag: " << token->string.latin1 (); 2060 return readDTD (); 2061 } 2062 if (token->token == tok_white_space) 2063 if (!nextToken ()) return false; // allow '< / foo', '< foo', '< ? foo' 2064 if (token->token == tok_question_mark) { 2065 m_state = new StateInfo (InPITag, m_state->next); 2066 return readPI (); 2067 } 2068 if (token->token == tok_slash) { 2069 m_state = new StateInfo (InEndTag, m_state->next); 2070 return readEndTag (); 2071 } 2072 if (token->token != tok_text) 2073 return false; // FIXME entities 2074 tagname = token->string; 2075 //kDebug () << "readTag " << tagname.latin1(); 2076 m_state = new StateInfo (InAttributes, m_state); 2077 return readAttributes (); 2078 } 2079 2080 bool SimpleSAXParser::parse (QTextStream & d) { 2081 data = &d; 2082 if (!next_token) { 2083 next_token = TokenInfoPtr (new TokenInfo); 2084 m_state = new StateInfo (InContent, m_state); 2085 } 2086 bool ok = true; 2087 bool in_character_data = false; 2088 QString white_space; 2089 while (ok) { 2090 switch (m_state->state) { 2091 case InTag: 2092 ok = readTag (); 2093 break; 2094 case InPITag: 2095 ok = readPI (); 2096 break; 2097 case InDTDTag: 2098 ok = readDTD (); 2099 break; 2100 case InEndTag: 2101 ok = readEndTag (); 2102 break; 2103 case InAttributes: 2104 ok = readAttributes (); 2105 break; 2106 case InCDATA: 2107 ok = readCDATA (); 2108 break; 2109 case InComment: 2110 ok = readComment (); 2111 break; 2112 default: 2113 if ((ok = nextToken ())) { 2114 if (token->token == tok_angle_open) { 2115 attr_name.truncate (0); 2116 attr_value.truncate (0); 2117 m_attributes = AttributeList (); 2118 equal_seen = in_sngl_quote = in_dbl_quote = false; 2119 m_state = new StateInfo (InTag, m_state); 2120 ok = readTag (); 2121 in_character_data = false; 2122 white_space.truncate (0); 2123 } else if (token->token == tok_white_space) { 2124 white_space += token->string; 2125 } else { 2126 if (!white_space.isEmpty ()) { 2127 if (!in_character_data) { 2128 int pos = white_space.lastIndexOf (QChar ('\n')); 2129 if (pos > -1) 2130 white_space = white_space.mid (pos + 1); 2131 } 2132 have_error = !builder.characterData (white_space); 2133 white_space.truncate (0); 2134 } 2135 have_error = !builder.characterData (token->string); 2136 in_character_data = true; 2137 } 2138 } 2139 } 2140 if (!m_state) 2141 return true; // end document 2142 } 2143 return false; // need more data 2144 } 2145 2146 #endif // KMPLAYER_WITH_EXPAT