File indexing completed on 2024-04-21 15:38:24
0001 /** 0002 This file belong to the KMPlayer project, a movie player plugin for Konqueror 0003 Copyright (C) 2007 Koos Vriezen <koos.vriezen@gmail.com> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Lesser General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Lesser General Public License for more details. 0014 0015 You should have received a copy of the GNU Lesser General Public 0016 License along with this library; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0018 **/ 0019 0020 #include <qtextstream.h> 0021 #include <qapplication.h> 0022 #include <qmovie.h> 0023 #include <QBuffer> 0024 #include <QPainter> 0025 #include <QtSvg/QSvgRenderer> 0026 #include <qimage.h> 0027 #include <qfile.h> 0028 #include <qurl.h> 0029 #include <qtextcodec.h> 0030 #include <qtextstream.h> 0031 0032 #include <kdebug.h> 0033 #include <kmimetype.h> 0034 #include <klocalizedstring.h> 0035 #include <kio/job.h> 0036 #include <kio/jobclasses.h> 0037 #include <kmimetype.h> 0038 #include <kurlauthorized.h> 0039 0040 #include "mediaobject.h" 0041 #include "kmplayerprocess.h" 0042 #include "kmplayerview.h" 0043 #include "expression.h" 0044 #include "viewarea.h" 0045 #include "kmplayerpartbase.h" 0046 0047 using namespace KMPlayer; 0048 0049 namespace { 0050 0051 typedef QMap <QString, ImageDataPtrW> ImageDataMap; 0052 0053 static DataCache *memory_cache; 0054 static ImageDataMap *image_data_map; 0055 0056 struct GlobalMediaData : public GlobalShared<GlobalMediaData> { 0057 GlobalMediaData (GlobalMediaData **gb) 0058 : GlobalShared<GlobalMediaData> (gb) { 0059 memory_cache = new DataCache; 0060 image_data_map = new ImageDataMap; 0061 } 0062 ~GlobalMediaData (); 0063 }; 0064 0065 static GlobalMediaData *global_media; 0066 0067 GlobalMediaData::~GlobalMediaData () { 0068 delete memory_cache; 0069 delete image_data_map; 0070 global_media = NULL; 0071 } 0072 } 0073 0074 //------------------------%<---------------------------------------------------- 0075 0076 MediaManager::MediaManager (PartBase *player) : m_player (player) { 0077 if (!global_media) 0078 (void) new GlobalMediaData (&global_media); 0079 else 0080 global_media->ref (); 0081 0082 m_process_infos ["mplayer"] = new MPlayerProcessInfo (this); 0083 m_process_infos ["phonon"] = new PhononProcessInfo (this); 0084 //XineProcessInfo *xpi = new XineProcessInfo (this); 0085 //m_process_infos ["xine"] = xpi; 0086 //m_process_infos ["gstreamer"] = new GStreamer (this, m_settings);, i18n ("&GStreamer") 0087 #ifdef KMPLAYER_WITH_NPP 0088 m_process_infos ["npp"] = new NppProcessInfo (this); 0089 #endif 0090 m_record_infos ["mencoder"] = new MEncoderProcessInfo (this); 0091 m_record_infos ["mplayerdumpstream"] = new MPlayerDumpProcessInfo (this); 0092 m_record_infos ["ffmpeg"] = new FFMpegProcessInfo (this); 0093 //m_record_infos ["xine"] = xpi; 0094 } 0095 0096 MediaManager::~MediaManager () { 0097 for (ProcessList::iterator i = m_processes.begin (); 0098 i != m_processes.end (); 0099 i = m_processes.begin () /*~Process removes itself from this list*/) 0100 { 0101 kDebug() << "~MediaManager " << *i << endl; 0102 delete *i; 0103 } 0104 for (ProcessList::iterator i = m_recorders.begin (); 0105 i != m_recorders.end (); 0106 i = m_recorders.begin ()) 0107 { 0108 kDebug() << "~MediaManager " << *i << endl; 0109 delete *i; 0110 } 0111 const ProcessInfoMap::iterator ie = m_process_infos.end (); 0112 for (ProcessInfoMap::iterator i = m_process_infos.begin (); i != ie; ++i) 0113 if (!m_record_infos.contains (i.key ())) 0114 delete i.value (); 0115 0116 const ProcessInfoMap::iterator rie = m_record_infos.end (); 0117 for (ProcessInfoMap::iterator i = m_record_infos.begin (); i != rie; ++i) 0118 delete i.value (); 0119 0120 if (m_media_objects.size ()) { 0121 kError () << "~MediaManager media list not empty " << m_media_objects.size () << endl; 0122 // bug elsewere, but don't crash 0123 const MediaList::iterator me = m_media_objects.end (); 0124 for (MediaList::iterator i = m_media_objects.begin (); i != me; ) { 0125 if (*i && (*i)->mrl () && 0126 (*i)->mrl ()->document ()->active ()) { 0127 (*i)->mrl ()->document ()->deactivate (); 0128 i = m_media_objects.begin (); 0129 } else { 0130 ++i; 0131 } 0132 } 0133 if (m_media_objects.size ()) 0134 kError () << "~MediaManager media list still not empty" << m_media_objects.size () << endl; 0135 } 0136 global_media->unref (); 0137 } 0138 0139 MediaObject *MediaManager::createAVMedia (Node *node, const QByteArray &) { 0140 RecordDocument *rec = id_node_record_document == node->id 0141 ? convertNode <RecordDocument> (node) 0142 : NULL; 0143 if (!rec && !m_player->source()->authoriseUrl ( 0144 node->mrl()->absolutePath ())) 0145 return NULL; 0146 0147 AudioVideoMedia *av = new AudioVideoMedia (this, node); 0148 if (rec) { 0149 av->process = m_record_infos[rec->recorder]->create (m_player, av); 0150 m_recorders.push_back (av->process); 0151 kDebug() << "Adding recorder " << endl; 0152 } else { 0153 av->process = m_process_infos[m_player->processName ( 0154 av->mrl ())]->create (m_player, av); 0155 m_processes.push_back (av->process); 0156 } 0157 av->process->user = av; 0158 av->setViewer (!rec 0159 ? m_player->viewWidget ()->viewArea ()->createVideoWidget () 0160 : NULL); 0161 0162 if (av->process->state () <= IProcess::Ready) 0163 av->process->ready (); 0164 return av; 0165 } 0166 0167 static const QString statemap [] = { 0168 i18n ("Not Running"), i18n ("Ready"), i18n ("Buffering"), i18n ("Playing"), i18n ("Paused") 0169 }; 0170 0171 void MediaManager::stateChange (AudioVideoMedia *media, 0172 IProcess::State olds, IProcess::State news) { 0173 //p->viewer()->view()->controlPanel()->setPlaying(news > Process::Ready); 0174 Mrl *mrl = media->mrl (); 0175 kDebug () << "processState " << media->process->process_info->name << " " 0176 << statemap[olds] << " -> " << statemap[news]; 0177 0178 if (!mrl) { // document dispose 0179 if (IProcess::Ready < news) 0180 media->process->quit (); 0181 else 0182 delete media; 0183 return; 0184 } 0185 0186 if (!m_player->view ()) // part destruction 0187 return; 0188 0189 bool is_rec = id_node_record_document == mrl->id; 0190 m_player->updateStatus (i18n ("Player %1 %2", 0191 media->process->process_info->name, statemap[news])); 0192 if (IProcess::Playing == news) { 0193 if (Element::state_deferred == mrl->state) 0194 mrl->undefer (); 0195 bool has_video = !is_rec; 0196 if (is_rec && m_recorders.contains(media->process)) 0197 m_player->recorderPlaying (); 0198 if (has_video) { 0199 if (m_player->view ()) { 0200 if (media->viewer ()) { 0201 media->viewer ()->setAspect (mrl->aspect); 0202 media->viewer ()->map (); 0203 } 0204 if (Mrl::SingleMode == mrl->view_mode) 0205 m_player->viewWidget ()->viewArea ()->resizeEvent (NULL); 0206 } 0207 } 0208 } else if (IProcess::NotRunning == news) { 0209 if (AudioVideoMedia::ask_delete == media->request) { 0210 delete media; 0211 } else if (mrl->unfinished ()) { 0212 mrl->document ()->post (mrl, new Posting (mrl, MsgMediaFinished)); 0213 } 0214 } else if (IProcess::Ready == news) { 0215 if (AudioVideoMedia::ask_play == media->request) { 0216 playAudioVideo (media); 0217 } else if (AudioVideoMedia::ask_grab == media->request) { 0218 grabPicture (media); 0219 } else { 0220 if (!is_rec && Mrl::SingleMode == mrl->view_mode) { 0221 ProcessList::ConstIterator i, e = m_processes.constEnd (); 0222 for (i = m_processes.constBegin(); i != e; ++i) 0223 if (*i != media->process && 0224 (*i)->state () == IProcess::Ready) 0225 (*i)->play (); // delayed playing 0226 e = m_recorders.constEnd (); 0227 for (i = m_recorders.constBegin (); i != e; ++i) 0228 if (*i != media->process && 0229 (*i)->state () == IProcess::Ready) 0230 (*i)->play (); // delayed recording 0231 } 0232 if (AudioVideoMedia::ask_delete == media->request) { 0233 delete media; 0234 } else if (olds > IProcess::Ready) { 0235 if (is_rec) 0236 mrl->message (MsgMediaFinished, NULL); // FIXME 0237 else 0238 mrl->document()->post(mrl, new Posting (mrl, MsgMediaFinished)); 0239 } 0240 } 0241 } else if (IProcess::Buffering == news) { 0242 if (AudioVideoMedia::ask_pause == media->request) { 0243 media->pause (); 0244 } else if (mrl->view_mode != Mrl::SingleMode) { 0245 mrl->defer (); // paused the SMIL 0246 } 0247 } 0248 } 0249 0250 void MediaManager::playAudioVideo (AudioVideoMedia *media) { 0251 Mrl *mrl = media->mrl (); 0252 media->request = AudioVideoMedia::ask_nothing; 0253 if (!mrl ||!m_player->view ()) 0254 return; 0255 if (Mrl::SingleMode == mrl->view_mode) { 0256 ProcessList::ConstIterator i, e = m_processes.constEnd (); 0257 for (i = m_processes.constBegin(); i != e; ++i) 0258 if (*i != media->process && (*i)->state () > IProcess::Ready) 0259 return; // delay, avoiding two overlaping widgets 0260 } 0261 media->process->play (); 0262 } 0263 0264 void MediaManager::grabPicture (AudioVideoMedia *media) { 0265 Mrl *mrl = media->mrl (); 0266 media->request = AudioVideoMedia::ask_nothing; 0267 if (!mrl) 0268 return; 0269 media->process->grabPicture (media->m_grab_file, media->m_frame); 0270 } 0271 0272 void MediaManager::processDestroyed (IProcess *process) { 0273 kDebug() << "processDestroyed " << process << endl; 0274 m_processes.removeAll (process); 0275 m_recorders.removeAll (process); 0276 } 0277 0278 //------------------------%<---------------------------------------------------- 0279 0280 MediaObject::MediaObject (MediaManager *manager, Node *node) 0281 : m_manager (manager), m_node (node) { 0282 manager->medias ().push_back (this); 0283 } 0284 0285 MediaObject::~MediaObject () { 0286 m_manager->medias ().removeAll (this); 0287 } 0288 0289 KDE_NO_EXPORT void MediaObject::destroy () { 0290 delete this; 0291 } 0292 0293 Mrl *MediaObject::mrl () { 0294 return m_node ? m_node->mrl () : NULL; 0295 } 0296 0297 //------------------------%<---------------------------------------------------- 0298 0299 void DataCache::add (const QString & url, const QString &mime, const QByteArray & data) { 0300 QByteArray bytes; 0301 bytes = data; 0302 cache_map.insert (url, qMakePair (mime, bytes)); 0303 preserve_map.remove (url); 0304 emit preserveRemoved (url); 0305 } 0306 0307 bool DataCache::get (const QString & url, QString &mime, QByteArray & data) { 0308 DataMap::const_iterator it = cache_map.constFind (url); 0309 if (it != cache_map.constEnd ()) { 0310 mime = it.value ().first; 0311 data = it.value ().second; 0312 return true; 0313 } 0314 return false; 0315 } 0316 0317 bool DataCache::preserve (const QString & url) { 0318 PreserveMap::const_iterator it = preserve_map.constFind (url); 0319 if (it == preserve_map.constEnd ()) { 0320 preserve_map.insert (url, true); 0321 return true; 0322 } 0323 return false; 0324 } 0325 0326 bool DataCache::isPreserved (const QString & url) { 0327 return preserve_map.find (url) != preserve_map.end (); 0328 } 0329 0330 bool DataCache::unpreserve (const QString & url) { 0331 const PreserveMap::iterator it = preserve_map.find (url); 0332 if (it == preserve_map.end ()) 0333 return false; 0334 preserve_map.erase (it); 0335 emit preserveRemoved (url); 0336 return true; 0337 } 0338 0339 //------------------------%<---------------------------------------------------- 0340 0341 static bool isPlayListMime (const QString & mime) { 0342 QString m (mime); 0343 int plugin_pos = m.indexOf ("-plugin"); 0344 if (plugin_pos > 0) 0345 m.truncate (plugin_pos); 0346 QByteArray ba = m.toAscii (); 0347 const char * mimestr = ba.data (); 0348 kDebug() << "isPlayListMime " << mimestr; 0349 return mimestr && (!strcmp (mimestr, "audio/mpegurl") || 0350 !strcmp (mimestr, "audio/x-mpegurl") || 0351 !strncmp (mimestr, "video/x-ms", 10) || 0352 !strncmp (mimestr, "audio/x-ms", 10) || 0353 //!strcmp (mimestr, "video/x-ms-wmp") || 0354 //!strcmp (mimestr, "video/x-ms-asf") || 0355 //!strcmp (mimestr, "video/x-ms-wmv") || 0356 //!strcmp (mimestr, "video/x-ms-wvx") || 0357 //!strcmp (mimestr, "video/x-msvideo") || 0358 !strcmp (mimestr, "audio/x-scpls") || 0359 !strcmp (mimestr, "audio/x-shoutcast-stream") || 0360 !strcmp (mimestr, "audio/x-pn-realaudio") || 0361 !strcmp (mimestr, "audio/vnd.rn-realaudio") || 0362 !strcmp (mimestr, "audio/m3u") || 0363 !strcmp (mimestr, "audio/x-m3u") || 0364 !strncmp (mimestr, "text/", 5) || 0365 (!strncmp (mimestr, "application/", 12) && 0366 strstr (mimestr + 12,"+xml")) || 0367 !strncasecmp (mimestr, "application/smil", 16) || 0368 !strncasecmp (mimestr, "application/xml", 15) || 0369 //!strcmp (mimestr, "application/rss+xml") || 0370 //!strcmp (mimestr, "application/atom+xml") || 0371 !strcmp (mimestr, "image/svg+xml") || 0372 !strcmp (mimestr, "image/vnd.rn-realpix") || 0373 !strcmp (mimestr, "application/x-mplayer2")); 0374 } 0375 0376 static QString mimeByContent (const QByteArray &data) 0377 { 0378 int accuraty; 0379 KMimeType::Ptr mimep = KMimeType::findByContent (data, &accuraty); 0380 if (mimep) 0381 return mimep->name (); 0382 return QString (); 0383 } 0384 0385 MediaInfo::MediaInfo (Node *n, MediaManager::MediaType t) 0386 : media (NULL), type (t), node (n), job (NULL), 0387 preserve_wait (false), check_access (false) { 0388 } 0389 0390 MediaInfo::~MediaInfo () { 0391 clearData (); 0392 } 0393 0394 KDE_NO_EXPORT void MediaInfo::killWGet () { 0395 if (job) { 0396 job->kill (); // quiet, no result signal 0397 job = 0L; 0398 memory_cache->unpreserve (url); 0399 } else if (preserve_wait) { 0400 disconnect (memory_cache, SIGNAL (preserveRemoved (const QString &)), 0401 this, SLOT (cachePreserveRemoved (const QString &))); 0402 preserve_wait = false; 0403 } 0404 } 0405 0406 /** 0407 * Gets contents from url and puts it in m_data 0408 */ 0409 bool MediaInfo::wget(const QString& str, const QString& domain) { 0410 clearData (); 0411 url = str; 0412 0413 if (MediaManager::Any == type || MediaManager::Image == type) { 0414 ImageDataMap::iterator i = image_data_map->find (str); 0415 if (i != image_data_map->end ()) { 0416 media = new ImageMedia (node, i.value ()); 0417 type = MediaManager::Image; 0418 ready (); 0419 return true; 0420 } 0421 } 0422 0423 Mrl *mrl = node->mrl (); 0424 if (mrl && (MediaManager::Any == type || MediaManager::AudioVideo == type)) 0425 { 0426 if (!mrl->mimetype.isEmpty ()) 0427 setMimetype (mrl->mimetype); 0428 if (mrl && (MediaManager::Any == type || MediaManager::AudioVideo == type)) 0429 if (mime == "application/x-shockwave-flash" || 0430 mime == "application/futuresplash" || 0431 str.startsWith ("tv:")) { 0432 ready (); 0433 return true; // FIXME 0434 } 0435 } 0436 0437 KUrl kurl (str); 0438 if (!mrl || !mrl->access_granted) 0439 for (Node *p = node->parentNode (); p; p = p->parentNode ()) { 0440 Mrl *m = p->mrl (); 0441 if (m && !m->src.isEmpty () && 0442 m->src != "Playlist://" && 0443 !KUrlAuthorized::authorizeUrlAction ("redirect", m->src, kurl)) { 0444 kWarning () << "redirect access denied"; 0445 ready (); 0446 return true; 0447 } 0448 } 0449 0450 bool only_playlist = false; 0451 bool maybe_playlist = false; 0452 if (MediaManager::Audio == type 0453 || MediaManager::AudioVideo == type 0454 || MediaManager::Any == type) { 0455 only_playlist = true; 0456 maybe_playlist = isPlayListMime (mime); 0457 } 0458 0459 if (kurl.isLocalFile ()) { 0460 QFile file (kurl.path ()); 0461 if (file.exists ()) { 0462 if (MediaManager::Data != type && mime.isEmpty ()) { 0463 KMimeType::Ptr mimeptr = KMimeType::findByUrl (kurl); 0464 if (mrl && mimeptr) { 0465 mrl->mimetype = mimeptr->name (); 0466 setMimetype (mrl->mimetype); 0467 } 0468 kDebug () << "wget2 " << str << " " << mime; 0469 } else { 0470 setMimetype (mime); 0471 } 0472 only_playlist = MediaManager::Audio == type || 0473 MediaManager::AudioVideo == type; 0474 maybe_playlist = isPlayListMime (mime); // get new mime 0475 if (file.open (QIODevice::ReadOnly)) { 0476 if (only_playlist) { 0477 maybe_playlist &= file.size () < 2000000; 0478 if (maybe_playlist) { 0479 char databuf [512]; 0480 int nr_bytes = file.read (databuf, 512); 0481 if (nr_bytes > 3 && 0482 (KMimeType::isBufferBinaryData (QByteArray (databuf, nr_bytes)) || 0483 !strncmp (databuf, "RIFF", 4))) 0484 maybe_playlist = false; 0485 } 0486 if (!maybe_playlist) { 0487 ready (); 0488 return true; 0489 } 0490 file.reset (); 0491 } 0492 data = file.readAll (); 0493 file.close (); 0494 } 0495 } 0496 ready (); 0497 return true; 0498 } 0499 QString protocol = kurl.protocol (); 0500 if (!domain.isEmpty ()) { 0501 QString get_from = protocol + "://" + kurl.host (); 0502 if (get_from != domain) { 0503 check_access = true; 0504 access_from = domain; 0505 cross_domain = get_from + "/crossdomain.xml"; 0506 kurl = KUrl (cross_domain); 0507 } 0508 } 0509 if (!check_access) { 0510 if (MediaManager::Data != type && 0511 (memory_cache->get (str, mime, data) || 0512 protocol == "mms" || protocol == "rtsp" || 0513 protocol == "rtp" || protocol == "rtmp" || 0514 (only_playlist && !maybe_playlist && !mime.isEmpty ()))) { 0515 setMimetype (mime); 0516 if (MediaManager::Any == type) 0517 type = MediaManager::AudioVideo; 0518 ready (); 0519 return true; 0520 } 0521 } 0522 if (check_access || memory_cache->preserve (str)) { 0523 //kDebug () << "downloading " << str; 0524 job = KIO::get (kurl, KIO::NoReload, KIO::HideProgressInfo); 0525 job->addMetaData ("PropagateHttpHeader", "true"); 0526 job->addMetaData ("errorPage", "false"); 0527 connect (job, SIGNAL (data (KIO::Job *, const QByteArray &)), 0528 this, SLOT (slotData (KIO::Job *, const QByteArray &))); 0529 connect (job, SIGNAL (result (KJob *)), 0530 this, SLOT (slotResult (KJob *))); 0531 if (!check_access) 0532 connect (job, SIGNAL (mimetype (KIO::Job *, const QString &)), 0533 this, SLOT (slotMimetype (KIO::Job *, const QString &))); 0534 } else { 0535 //kDebug () << "download preserved " << str; 0536 connect (memory_cache, SIGNAL (preserveRemoved (const QString &)), 0537 this, SLOT (cachePreserveRemoved (const QString &))); 0538 preserve_wait = true; 0539 } 0540 return false; 0541 } 0542 0543 KDE_NO_EXPORT bool MediaInfo::readChildDoc () { 0544 QTextStream textstream (data, QIODevice::ReadOnly); 0545 QString line; 0546 NodePtr cur_elm = node; 0547 do { 0548 line = textstream.readLine (); 0549 } while (!line.isNull () && line.trimmed ().isEmpty ()); 0550 if (!line.isNull ()) { 0551 bool pls_groupfound = 0552 line.startsWith ("[") && line.endsWith ("]") && 0553 line.mid (1, line.size () - 2).trimmed () == "playlist"; 0554 if ((pls_groupfound && 0555 cur_elm->mrl ()->mimetype.startsWith ("audio/")) || 0556 cur_elm->mrl ()->mimetype == QString ("audio/x-scpls")) { 0557 int nr = -1; 0558 struct Entry { 0559 QString url, title; 0560 } * entries = 0L; 0561 do { 0562 line = line.trimmed (); 0563 if (!line.isEmpty ()) { 0564 if (line.startsWith ("[") && line.endsWith ("]")) { 0565 if (line.mid (1, line.size () - 2).trimmed () == "playlist") 0566 pls_groupfound = true; 0567 else 0568 break; 0569 kDebug () << "Group found: " << line; 0570 } else if (pls_groupfound) { 0571 int eq_pos = line.indexOf (QChar ('=')); 0572 if (eq_pos > 0) { 0573 if (line.toLower ().startsWith (QString ("numberofentries"))) { 0574 nr = line.mid (eq_pos + 1).trimmed ().toInt (); 0575 kDebug () << "numberofentries : " << nr; 0576 if (nr > 0 && nr < 1024) 0577 entries = new Entry[nr]; 0578 else 0579 nr = 0; 0580 } else if (nr > 0) { 0581 QString ll = line.toLower (); 0582 if (ll.startsWith (QString ("file"))) { 0583 int i = line.mid (4, eq_pos-4).toInt (); 0584 if (i > 0 && i <= nr) 0585 entries[i-1].url = line.mid (eq_pos + 1).trimmed (); 0586 } else if (ll.startsWith (QString ("title"))) { 0587 int i = line.mid (5, eq_pos-5).toInt (); 0588 if (i > 0 && i <= nr) 0589 entries[i-1].title = line.mid (eq_pos + 1).trimmed (); 0590 } 0591 } 0592 } 0593 } 0594 } 0595 line = textstream.readLine (); 0596 } while (!line.isNull ()); 0597 NodePtr doc = node->document (); 0598 for (int i = 0; i < nr; i++) 0599 if (!entries[i].url.isEmpty ()) 0600 cur_elm->appendChild (new GenericURL (doc, 0601 QUrl::fromPercentEncoding (entries[i].url.toUtf8 ()), 0602 entries[i].title)); 0603 delete [] entries; 0604 } else if (line.trimmed ().startsWith (QChar ('<'))) { 0605 readXML (cur_elm, textstream, line); 0606 //cur_elm->normalize (); 0607 } else if (line.toLower () != QString ("[reference]")) { 0608 bool extm3u = line.startsWith ("#EXTM3U"); 0609 QString title; 0610 NodePtr doc = node->document (); 0611 if (extm3u) 0612 line = textstream.readLine (); 0613 while (!line.isNull ()) { 0614 /* TODO && m_document.size () < 1024 / * support 1k entries * /);*/ 0615 QString mrl = line.trimmed (); 0616 if (line == QString ("--stop--")) 0617 break; 0618 if (mrl.toLower ().startsWith (QString ("asf "))) 0619 mrl = mrl.mid (4).trimmed (); 0620 if (!mrl.isEmpty ()) { 0621 if (extm3u && mrl.startsWith (QChar ('#'))) { 0622 if (line.startsWith ("#EXTINF:")) 0623 title = line.mid (9); 0624 else 0625 title = mrl.mid (1); 0626 } else if (!line.startsWith (QChar ('#'))) { 0627 cur_elm->appendChild (new GenericURL (doc, mrl, title)); 0628 title.truncate (0); 0629 } 0630 } 0631 line = textstream.readLine (); 0632 } 0633 } 0634 } 0635 return !cur_elm->isPlayable (); 0636 } 0637 0638 void MediaInfo::setMimetype (const QString &mt) 0639 { 0640 mime = mt; 0641 0642 Mrl *mrl = node ? node->mrl () : NULL; 0643 if (mrl && mrl->mimetype.isEmpty ()) 0644 mrl->mimetype = mt; 0645 0646 if (MediaManager::Any == type) { 0647 if (mimetype ().startsWith ("image/")) 0648 type = MediaManager::Image; 0649 else if (mime.startsWith ("audio/")) 0650 type = MediaManager::Audio; 0651 else 0652 type = MediaManager::AudioVideo; 0653 } 0654 } 0655 0656 KDE_NO_EXPORT QString MediaInfo::mimetype () { 0657 if (data.size () > 0 && mime.isEmpty ()) 0658 setMimetype (mimeByContent (data)); 0659 return mime; 0660 } 0661 0662 KDE_NO_EXPORT void MediaInfo::clearData () { 0663 killWGet (); 0664 if (media) { 0665 media->destroy (); 0666 media = NULL; 0667 } 0668 url.truncate (0); 0669 mime.truncate (0); 0670 access_from.truncate (0); 0671 data.resize (0); 0672 } 0673 0674 KDE_NO_EXPORT bool MediaInfo::downloading () const { 0675 return !!job; 0676 } 0677 0678 void MediaInfo::create () { 0679 MediaManager *mgr = (MediaManager*)node->document()->role(RoleMediaManager); 0680 if (!media && mgr) { 0681 switch (type) { 0682 case MediaManager::Audio: 0683 case MediaManager::AudioVideo: 0684 kDebug() << data.size (); 0685 if (!data.size () || !readChildDoc ()) 0686 media = mgr->createAVMedia (node, data); 0687 break; 0688 case MediaManager::Image: 0689 if (data.size () && mime == "image/svg+xml") { 0690 readChildDoc (); 0691 if (node->firstChild () && 0692 id_node_svg == node->lastChild ()->id) { 0693 media = new ImageMedia (node); 0694 break; 0695 } 0696 } 0697 if (data.size () && 0698 (!(mimetype ().startsWith ("text/") || 0699 mime == "image/vnd.rn-realpix") || 0700 !readChildDoc ())) 0701 media = new ImageMedia (mgr, node, url, data); 0702 break; 0703 case MediaManager::Text: 0704 if (data.size ()) 0705 media = new TextMedia (mgr, node, data); 0706 break; 0707 default: // Any 0708 break; 0709 } 0710 } 0711 } 0712 0713 KDE_NO_EXPORT void MediaInfo::ready () { 0714 if (MediaManager::Data != type) { 0715 create (); 0716 if (id_node_record_document == node->id) 0717 node->message (MsgMediaReady); 0718 else 0719 node->document()->post (node, new Posting (node, MsgMediaReady)); 0720 } else { 0721 node->message (MsgMediaReady); 0722 } 0723 } 0724 0725 static bool validDataFormat (MediaManager::MediaType type, const QByteArray &ba) 0726 { 0727 switch (type) { 0728 case MediaManager::Audio: 0729 case MediaManager::AudioVideo: 0730 return !(ba.size () > 2000000 || ba.size () < 4 || 0731 KMimeType::isBufferBinaryData (ba) || 0732 !strncmp (ba.data (), "RIFF", 4)); 0733 default: 0734 return true; 0735 } 0736 } 0737 0738 KDE_NO_EXPORT void MediaInfo::slotResult (KJob *kjob) { 0739 job = 0L; // signal KIO::Job::result deletes itself 0740 if (check_access) { 0741 check_access = false; 0742 0743 bool success = false; 0744 if (!kjob->error () && data.size () > 0) { 0745 QTextStream ts (data, QIODevice::ReadOnly); 0746 NodePtr doc = new Document (QString ()); 0747 readXML (doc, ts, QString ()); 0748 0749 Expression *expr = evaluateExpr ( 0750 "//cross-domain-policy/allow-access-from/@domain"); 0751 if (expr) { 0752 expr->setRoot (doc); 0753 Expression::iterator it, e = expr->end(); 0754 for (it = expr->begin(); it != e; ++it) { 0755 QRegExp match (it->value(), Qt::CaseInsensitive, QRegExp::Wildcard); 0756 if (match.exactMatch (access_from)) { 0757 success = true; 0758 break; 0759 } 0760 } 0761 delete expr; 0762 } 0763 doc->document ()->dispose (); 0764 } 0765 if (success) { 0766 wget (QString (url)); 0767 } else { 0768 data.resize (0); 0769 ready (); 0770 } 0771 } else { 0772 if (MediaManager::Data != type && !kjob->error ()) { 0773 if (data.size () && data.size () < 512) { 0774 setMimetype (mimeByContent (data)); 0775 if (!validDataFormat (type, data)) 0776 data.resize (0); 0777 } 0778 memory_cache->add (url, mime, data); 0779 } else { 0780 memory_cache->unpreserve (url); 0781 if (MediaManager::Data != type) 0782 data.resize (0); 0783 } 0784 ready (); 0785 } 0786 } 0787 0788 KDE_NO_EXPORT void MediaInfo::cachePreserveRemoved (const QString & str) { 0789 if (str == url && !memory_cache->isPreserved (str)) { 0790 preserve_wait = false; 0791 disconnect (memory_cache, SIGNAL (preserveRemoved (const QString &)), 0792 this, SLOT (cachePreserveRemoved (const QString &))); 0793 wget (str); 0794 } 0795 } 0796 0797 KDE_NO_EXPORT void MediaInfo::slotData (KIO::Job *, const QByteArray &qb) { 0798 if (qb.size ()) { 0799 int old_size = data.size (); 0800 int newsize = old_size + qb.size (); 0801 data.resize (newsize); 0802 memcpy (data.data () + old_size, qb.constData (), qb.size ()); 0803 if (!check_access && old_size < 512 && newsize >= 512) { 0804 setMimetype (mimeByContent (data)); 0805 if (!validDataFormat (type, data)) { 0806 data.resize (0); 0807 job->kill (KJob::EmitResult); 0808 return; 0809 } 0810 } 0811 } 0812 } 0813 0814 KDE_NO_EXPORT void MediaInfo::slotMimetype (KIO::Job *, const QString & m) { 0815 Mrl *mrl = node->mrl (); 0816 mime = m; 0817 if (mrl) 0818 mrl->mimetype = m; 0819 switch (type) { 0820 case MediaManager::Any: 0821 //fall through 0822 break; 0823 case MediaManager::Audio: 0824 case MediaManager::AudioVideo: 0825 if (!isPlayListMime (m)) 0826 job->kill (KJob::EmitResult); 0827 break; 0828 default: 0829 //TODO 0830 break; 0831 } 0832 } 0833 0834 //------------------------%<---------------------------------------------------- 0835 0836 IProcess::IProcess (ProcessInfo *pinfo) : 0837 user (NULL), 0838 process_info (pinfo), 0839 m_state (NotRunning) {} 0840 0841 AudioVideoMedia::AudioVideoMedia (MediaManager *manager, Node *node) 0842 : MediaObject (manager, node), 0843 process (NULL), 0844 m_viewer (NULL), 0845 request (ask_nothing) { 0846 kDebug() << "AudioVideoMedia::AudioVideoMedia" << endl; 0847 } 0848 0849 AudioVideoMedia::~AudioVideoMedia () { 0850 stop (); 0851 // delete m_process; 0852 if (m_viewer) { //nicer with QObject destroy signal, but preventing unmap on destruction 0853 View *view = m_manager->player ()->viewWidget (); 0854 if (view) 0855 view->viewArea ()->destroyVideoWidget (m_viewer); 0856 } 0857 if (process) { 0858 request = ask_nothing; 0859 delete process; 0860 } 0861 kDebug() << "AudioVideoMedia::~AudioVideoMedia"; 0862 } 0863 0864 bool AudioVideoMedia::play () { 0865 kDebug() << process; 0866 if (process) { 0867 kDebug() << process->state (); 0868 if (process->state () > IProcess::Ready) { 0869 kError() << "already playing" << endl; 0870 return true; 0871 } 0872 if (process->state () != IProcess::Ready) { 0873 request = ask_play; 0874 return true; // FIXME add Launching state 0875 } 0876 m_manager->playAudioVideo (this); 0877 return true; 0878 } 0879 return false; 0880 } 0881 0882 bool AudioVideoMedia::grabPicture (const QString &file, int frame) { 0883 if (process) { 0884 kDebug() << "AudioVideoMedia::grab " << file << endl; 0885 m_grab_file = file; 0886 m_frame = frame; 0887 if (process->state () < IProcess::Ready) { 0888 request = ask_grab; 0889 return true; // FIXME add Launching state 0890 } 0891 m_manager->grabPicture (this); 0892 return true; 0893 } 0894 return false; 0895 } 0896 0897 void AudioVideoMedia::stop () { 0898 if (ask_delete != request) 0899 request = ask_stop; 0900 if (process) 0901 process->stop (); 0902 if (m_manager->player ()->view () && m_viewer) 0903 m_viewer->unmap (); 0904 } 0905 0906 void AudioVideoMedia::pause () { 0907 if (process) { 0908 if (process->state () > IProcess::Ready) { 0909 request = ask_nothing; 0910 process->pause (); 0911 } else { 0912 request = ask_pause; 0913 } 0914 } 0915 } 0916 0917 void AudioVideoMedia::unpause () { 0918 if (process) { 0919 if (request == ask_pause) { 0920 request = ask_nothing; 0921 } else { 0922 process->unpause (); 0923 } 0924 } 0925 } 0926 0927 void AudioVideoMedia::destroy () { 0928 if (m_manager->player ()->view () && m_viewer) 0929 m_viewer->unmap (); 0930 if (!process || IProcess::Ready >= process->state ()) { 0931 delete this; 0932 } else { 0933 stop (); 0934 request = ask_delete; 0935 } 0936 } 0937 0938 void AudioVideoMedia::starting (IProcess*) { 0939 request = AudioVideoMedia::ask_nothing; 0940 } 0941 0942 void AudioVideoMedia::stateChange (IProcess *, 0943 IProcess::State os, IProcess::State ns) { 0944 m_manager->stateChange (this, os, ns); 0945 } 0946 0947 void AudioVideoMedia::processDestroyed (IProcess *p) { 0948 m_manager->processDestroyed (p); 0949 process = NULL; 0950 if (ask_delete == request) 0951 delete this; 0952 } 0953 0954 IViewer *AudioVideoMedia::viewer () { 0955 return m_viewer; 0956 } 0957 0958 Mrl *AudioVideoMedia::getMrl () { 0959 return mrl (); 0960 } 0961 0962 //------------------------%<---------------------------------------------------- 0963 0964 #ifdef KMPLAYER_WITH_CAIRO 0965 # include <cairo.h> 0966 #endif 0967 0968 ImageData::ImageData( const QString & img) 0969 : width (0), 0970 height (0), 0971 flags (0), 0972 has_alpha (false), 0973 image (0L), 0974 #ifdef KMPLAYER_WITH_CAIRO 0975 surface (NULL), 0976 #endif 0977 url (img) { 0978 //if (img.isEmpty ()) 0979 // //kDebug() << "New ImageData for " << this << endl; 0980 //else 0981 // //kDebug() << "New ImageData for " << img << endl; 0982 } 0983 0984 ImageData::~ImageData() { 0985 if (!url.isEmpty ()) 0986 image_data_map->remove (url); 0987 #ifdef KMPLAYER_WITH_CAIRO 0988 if (surface) 0989 cairo_surface_destroy (surface); 0990 #endif 0991 delete image; 0992 } 0993 0994 void ImageData::setImage (QImage *img) { 0995 if (image != img) { 0996 delete image; 0997 #ifdef KMPLAYER_WITH_CAIRO 0998 if (surface) { 0999 cairo_surface_destroy (surface); 1000 surface = NULL; 1001 } 1002 #endif 1003 image = img; 1004 if (img) { 1005 width = img->width (); 1006 height = img->height (); 1007 has_alpha = img->hasAlphaChannel (); 1008 } else { 1009 width = height = 0; 1010 } 1011 } 1012 } 1013 1014 ImageMedia::ImageMedia (MediaManager *manager, Node *node, 1015 const QString &url, const QByteArray &ba) 1016 : MediaObject (manager, node), data (ba), buffer (NULL), 1017 img_movie (NULL), 1018 svg_renderer (NULL), 1019 update_render (false), 1020 paused (false) { 1021 setupImage (url); 1022 } 1023 1024 ImageMedia::ImageMedia (Node *node, ImageDataPtr id) 1025 : MediaObject ((MediaManager *)node->document()->role (RoleMediaManager), 1026 node), 1027 buffer (NULL), 1028 img_movie (NULL), 1029 svg_renderer (NULL), 1030 update_render (false) { 1031 if (!id) { 1032 Node *c = findChildWithId (node, id_node_svg); 1033 if (c) { 1034 svg_renderer = new QSvgRenderer (c->outerXML().toUtf8 ()); 1035 if (svg_renderer->isValid ()) { 1036 cached_img = new ImageData (QString ()); 1037 cached_img->flags = ImageData::ImageScalable; 1038 if (svg_renderer->animated()) 1039 connect(svg_renderer, SIGNAL(repaintNeeded()), 1040 this, SLOT(svgUpdated())); 1041 } else { 1042 delete svg_renderer; 1043 svg_renderer = NULL; 1044 } 1045 } 1046 } else { 1047 cached_img = id; 1048 } 1049 } 1050 1051 ImageMedia::~ImageMedia () { 1052 delete img_movie; 1053 delete svg_renderer; 1054 delete buffer; 1055 } 1056 1057 KDE_NO_EXPORT bool ImageMedia::play () { 1058 if (!img_movie) 1059 return false; 1060 if (img_movie->state () == QMovie::Paused) 1061 img_movie->setPaused (false); 1062 else if (img_movie->state () != QMovie::Running) 1063 img_movie->start (); 1064 return true; 1065 } 1066 1067 KDE_NO_EXPORT void ImageMedia::stop () { 1068 pause (); 1069 } 1070 1071 void ImageMedia::pause () { 1072 if (!paused && svg_renderer && svg_renderer->animated()) 1073 disconnect(svg_renderer, SIGNAL(repaintNeeded()), 1074 this, SLOT(svgUpdated())); 1075 if (img_movie && img_movie->state () != QMovie::Paused) 1076 img_movie->setPaused (true); 1077 paused = true; 1078 } 1079 1080 void ImageMedia::unpause () { 1081 if (paused && svg_renderer && svg_renderer->animated()) 1082 connect(svg_renderer, SIGNAL(repaintNeeded()), 1083 this, SLOT(svgUpdated())); 1084 if (img_movie && QMovie::Paused == img_movie->state ()) 1085 img_movie->setPaused (false); 1086 paused = false; 1087 } 1088 1089 KDE_NO_EXPORT void ImageMedia::setupImage (const QString &url) { 1090 if (isEmpty () && data.size ()) { 1091 QImage *pix = new QImage; 1092 if (pix->loadFromData((data))) { 1093 cached_img = ImageDataPtr (new ImageData (url)); 1094 cached_img->setImage (pix); 1095 } else { 1096 delete pix; 1097 } 1098 } 1099 if (!isEmpty ()) { 1100 buffer = new QBuffer (&data); 1101 img_movie = new QMovie (buffer); 1102 //kDebug() << img_movie->frameCount (); 1103 if (img_movie->frameCount () > 1) { 1104 cached_img->flags |= (short)ImageData::ImagePixmap | ImageData::ImageAnimated; 1105 connect (img_movie, SIGNAL (updated (const QRect &)), 1106 this, SLOT (movieUpdated (const QRect &))); 1107 connect (img_movie, SIGNAL (stateChanged (QMovie::MovieState)), 1108 this, SLOT (movieStatus (QMovie::MovieState))); 1109 connect (img_movie, SIGNAL (resized (const QSize &)), 1110 this, SLOT (movieResize (const QSize &))); 1111 } else { 1112 delete img_movie; 1113 img_movie = 0L; 1114 delete buffer; 1115 buffer = 0L; 1116 frame_nr = 0; 1117 cached_img->flags |= (short)ImageData::ImagePixmap; 1118 image_data_map->insert (url, ImageDataPtrW (cached_img)); 1119 } 1120 } 1121 } 1122 1123 KDE_NO_EXPORT void ImageMedia::render (const ISize &sz) { 1124 if (svg_renderer && update_render) { 1125 delete svg_renderer; 1126 svg_renderer = NULL; 1127 Node *c = findChildWithId (m_node, id_node_svg); 1128 if (c) { 1129 QSvgRenderer *r = new QSvgRenderer (c->outerXML().toUtf8 ()); 1130 if (r->isValid ()) { 1131 cached_img->setImage (NULL); 1132 svg_renderer = r; 1133 } else { 1134 delete r; 1135 } 1136 } 1137 update_render = false; 1138 } 1139 if (svg_renderer && 1140 (cached_img->width != sz.width || cached_img->height != sz.height)) { 1141 QImage *img = new QImage (sz.width, sz.height, 1142 QImage::Format_ARGB32_Premultiplied); 1143 img->fill (0x0); 1144 QPainter paint (img); 1145 paint.setViewport (QRect (0, 0, sz.width, sz.height)); 1146 svg_renderer->render (&paint); 1147 cached_img->setImage (img); 1148 } 1149 } 1150 1151 KDE_NO_EXPORT void ImageMedia::updateRender () { 1152 update_render = true; 1153 if (m_node) 1154 m_node->document()->post(m_node, new Posting (m_node, MsgMediaUpdated)); 1155 } 1156 1157 KDE_NO_EXPORT void ImageMedia::sizes (SSize &size) { 1158 if (svg_renderer) { 1159 QSize s = svg_renderer->defaultSize (); 1160 size.width = s.width (); 1161 size.height = s.height (); 1162 } else if (cached_img) { 1163 size.width = cached_img->width; 1164 size.height = cached_img->height; 1165 } else { 1166 size.width = 0; 1167 size.height = 0; 1168 } 1169 } 1170 1171 bool ImageMedia::isEmpty () const { 1172 return !cached_img || (!svg_renderer && cached_img->isEmpty ()); 1173 } 1174 1175 KDE_NO_EXPORT void ImageMedia::svgUpdated() { 1176 cached_img->setImage (NULL); 1177 if (m_node) 1178 m_node->document ()->post (m_node, new Posting (m_node, MsgMediaUpdated)); 1179 } 1180 1181 KDE_NO_EXPORT void ImageMedia::movieResize (const QSize &) { 1182 //kDebug () << "movieResize" << endl; 1183 if (m_node) 1184 m_node->document ()->post (m_node, new Posting (m_node, MsgMediaUpdated)); 1185 } 1186 1187 KDE_NO_EXPORT void ImageMedia::movieUpdated (const QRect &) { 1188 if (frame_nr++) { 1189 ASSERT (cached_img && isEmpty ()); 1190 QImage *img = new QImage; 1191 *img = img_movie->currentImage (); 1192 cached_img->setImage (img); 1193 cached_img->flags = (int)(ImageData::ImagePixmap | ImageData::ImageAnimated); //TODO 1194 if (m_node) 1195 m_node->document ()->post (m_node, new Posting (m_node, MsgMediaUpdated)); 1196 } 1197 } 1198 1199 KDE_NO_EXPORT void ImageMedia::movieStatus (QMovie::MovieState status) { 1200 if (QMovie::NotRunning == status && m_node) 1201 m_node->document ()->post (m_node, new Posting (m_node, MsgMediaFinished)); 1202 } 1203 1204 //------------------------%<---------------------------------------------------- 1205 1206 static int default_font_size = -1; 1207 1208 TextMedia::TextMedia (MediaManager *manager, Node *node, const QByteArray &ba) 1209 : MediaObject (manager, node) { 1210 QByteArray data (ba); 1211 if (!data [data.size () - 1]) 1212 data.resize (data.size () - 1); // strip zero terminate char 1213 QTextStream ts (data, QIODevice::ReadOnly); 1214 QString val = convertNode <Element> (node)->getAttribute ("charset"); 1215 if (!val.isEmpty ()) { 1216 QTextCodec *codec = QTextCodec::codecForName (val.toAscii ()); 1217 if (codec) 1218 ts.setCodec (codec); 1219 } 1220 if (node->mrl() && node->mrl()->mimetype == "text/html") { 1221 Document *doc = new Document (QString ()); 1222 NodePtr store = doc; 1223 readXML (doc, ts, QString ()); 1224 text = doc->innerText (); 1225 doc->dispose (); 1226 } else { 1227 text = ts.readAll (); 1228 } 1229 } 1230 1231 TextMedia::~TextMedia () { 1232 } 1233 1234 bool TextMedia::play () { 1235 return !text.isEmpty (); 1236 } 1237 1238 int TextMedia::defaultFontSize () { 1239 if (default_font_size < 0) 1240 default_font_size = QApplication::font ().pointSize (); 1241 return default_font_size; 1242 } 1243 1244 #include "mediaobject.moc"