MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
decoderhandler.cpp
Go to the documentation of this file.
1 // c/c++
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <assert.h>
5 
6 // qt
7 #include <QApplication>
8 #include <QUrl>
9 #include <QFileInfo>
10 
11 // mythtv
12 #include <mythdownloadmanager.h>
13 #include <mythdirs.h>
14 #include <mythlogging.h>
15 #include <compat.h> // For random() on MINGW32
16 #include <remotefile.h>
17 #include <mythcorecontext.h>
18 #include <musicmetadata.h>
19 
20 
21 // mythmusic
22 #include "decoderhandler.h"
23 #include "decoder.h"
24 #include "shoutcast.h"
25 
26 /**********************************************************************/
27 
28 QEvent::Type DecoderHandlerEvent::Ready = (QEvent::Type) QEvent::registerEventType();
29 QEvent::Type DecoderHandlerEvent::Meta = (QEvent::Type) QEvent::registerEventType();
30 QEvent::Type DecoderHandlerEvent::BufferStatus = (QEvent::Type) QEvent::registerEventType();
31 QEvent::Type DecoderHandlerEvent::OperationStart = (QEvent::Type) QEvent::registerEventType();
32 QEvent::Type DecoderHandlerEvent::OperationStop = (QEvent::Type) QEvent::registerEventType();
33 QEvent::Type DecoderHandlerEvent::Error = (QEvent::Type) QEvent::registerEventType();
34 
36  : MythEvent(t), m_msg(NULL), m_meta(NULL), m_available(0), m_maxSize(0)
37 {
38  m_meta = new MusicMetadata(meta);
39 }
40 
42 {
43  if (m_msg)
44  delete m_msg;
45 
46  if (m_meta)
47  delete m_meta;
48 }
49 
51 {
52  DecoderHandlerEvent *result = new DecoderHandlerEvent(*this);
53 
54  if (m_msg)
55  result->m_msg = new QString(*m_msg);
56 
57  if (m_meta)
58  result->m_meta = new MusicMetadata(*m_meta);
59 
60  result->m_available = m_available;
61  result->m_maxSize = m_maxSize;
62 
63  return result;
64 }
65 
66 void DecoderHandlerEvent::getBufferStatus(int *available, int *maxSize) const
67 {
68  *available = m_available;
69  *maxSize = m_maxSize;
70 }
71 
72 /**********************************************************************/
73 
75 {
76  m_handler = parent;
77 }
78 
80 {
81 }
82 
84 {
86  m_handler->doConnectDecoder(getUrl(), format);
87 }
88 
90 {
91  return m_handler->getDecoder();
92 }
93 
94 void DecoderIOFactory::doFailed(const QString &message)
95 {
97  m_handler->doFailed(getUrl(), message);
98 }
99 
101 {
103 }
104 
106 {
108 }
109 
110 /**********************************************************************/
111 
113  : DecoderIOFactory(parent), m_input (NULL)
114 {
115 }
116 
118 {
119  if (m_input)
120  delete m_input;
121 }
122 
124 {
125  return m_input;
126 }
127 
129 {
130  QString sourcename = getMetadata().Filename();
131 
132  LOG(VB_PLAYBACK, LOG_INFO,
133  QString("DecoderIOFactory: Opening Local File %1").arg(sourcename));
134 
135  m_input = new QFile(sourcename);
136  doConnectDecoder(getUrl().toLocalFile());
137 }
138 
139 /**********************************************************************/
140 
142  : DecoderIOFactory(parent), m_input(NULL)
143 {
144 }
145 
147 {
148  if (m_input)
149  delete m_input;
150 }
151 
153 {
154  return m_input;
155 }
156 
158 {
159  QString url = getUrl().toString();
160  LOG(VB_PLAYBACK, LOG_INFO,
161  QString("DecoderIOFactorySG: Opening Myth URL %1").arg(url));
162  m_input = new MusicSGIODevice(url);
163  doConnectDecoder(getUrl().path());
164 }
165 
166 /**********************************************************************/
167 
169 {
170  m_accessManager = new QNetworkAccessManager(this);
171  m_input = new MusicIODevice();
172  connect(m_input, SIGNAL(freeSpaceAvailable()), SLOT(readyRead()));
173 
174  m_input->open(QIODevice::ReadWrite);
175 
176  m_bytesWritten = 0;
177  m_redirectCount = 0;
178 }
179 
181 {
182  doClose();
183 
184  m_accessManager->deleteLater();
185 
186  if (m_input)
187  delete m_input;
188 }
189 
191 {
192  return m_input;
193 }
194 
196 {
197  LOG(VB_PLAYBACK, LOG_INFO,
198  QString("DecoderIOFactory: Url %1").arg(getUrl().toString()));
199 
200  m_started = false;
201 
202  doOperationStart(tr("Fetching remote file"));
203 
204  m_reply = m_accessManager->get(QNetworkRequest(getUrl()));
205 
206  connect(m_reply, SIGNAL(readyRead()), this, SLOT(readyRead()));
207  connect(m_accessManager, SIGNAL(finished(QNetworkReply*)),
208  this, SLOT(replyFinished(QNetworkReply*)));
209 }
210 
212 {
213  doClose();
214 }
215 
216 void DecoderIOFactoryUrl::replyFinished(QNetworkReply *reply)
217 {
218  if (reply->error() != QNetworkReply::NoError)
219  {
220  doFailed("Cannot retrieve remote file.");
221  return;
222  }
223 
224  QUrl possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
225 
226  if (!possibleRedirectUrl.isEmpty() && (m_redirectedURL != possibleRedirectUrl))
227  {
228  LOG(VB_PLAYBACK, LOG_INFO,
229  QString("DecoderIOFactory: Got redirected to %1")
230  .arg(possibleRedirectUrl.toString()));
231 
232  m_redirectCount++;
233 
235  {
236  doFailed("Too many redirects");
237  }
238  else
239  {
240  setUrl(possibleRedirectUrl);
241  m_redirectedURL = possibleRedirectUrl;
242  start();
243  }
244 
245  return;
246  }
247 
248  m_redirectedURL.clear();
249 
250  if (!m_started)
251  doStart();
252 }
253 
255 {
257  QByteArray data = m_reply->read(available);
258 
259  m_bytesWritten += data.size();
260  m_input->writeData(data.data(), data.size());
261 
263  {
265  doStart();
266  }
267 }
268 
270 {
272  m_started = true;
273 }
274 
276 {
277  if (m_input && m_input->isOpen())
278  m_input->close();
279 }
280 
281 /**********************************************************************/
282 
284  m_state(STOPPED),
285  m_playlist_pos(0),
286  m_io_factory(NULL),
287  m_decoder(NULL),
288  m_meta(NULL),
289  m_op(false),
290  m_redirects(0)
291 {
292 }
293 
295 {
296  stop();
297 }
298 
300 {
301  m_state = LOADING;
302 
303  m_playlist.clear();
304  m_meta = mdata;
305  m_playlist_pos = -1;
306  m_redirects = 0;
307 
308  QUrl url;
309  if (QFileInfo(mdata->Filename()).isAbsolute())
310  url = QUrl::fromLocalFile(mdata->Filename());
311  else
312  url.setUrl(mdata->Filename());
313 
314  createPlaylist(url);
315 }
316 
317 void DecoderHandler::doStart(bool result)
318 {
319  doOperationStop();
320 
321  QUrl url;
322  if (QFileInfo(m_meta->Filename()).isAbsolute())
323  url = QUrl::fromLocalFile(m_meta->Filename());
324  else
325  url.setUrl(m_meta->Filename());
326 
327  if (m_state == LOADING && result)
328  {
329  for (int ii = 0; ii < m_playlist.size(); ii++)
330  LOG(VB_PLAYBACK, LOG_INFO, QString("Track %1 = %2")
331  .arg(ii) .arg(m_playlist.get(ii)->File()));
332  next();
333  }
334  else
335  {
336  if (m_state == STOPPED)
337  {
338  doFailed(url, "Could not get playlist");
339  }
340  }
341 }
342 
343 void DecoderHandler::error(const QString &e)
344 {
345  QString *str = new QString(e);
346  DecoderHandlerEvent ev(DecoderHandlerEvent::Error, str);
347  dispatch(ev);
348 }
349 
351 {
352  if (m_state == STOPPED)
353  return true;
354 
355  if (m_playlist_pos + 1 >= m_playlist.size())
356  {
357  m_state = STOPPED;
358  return true;
359  }
360 
361  return false;
362 }
363 
365 {
366  if (done())
367  return false;
368 
369  if (m_meta && m_meta->Format() == "cast")
370  {
372  }
373  else
374  {
375  m_playlist_pos++;
376  }
377 
379 
380  QUrl url;
381  if (QFileInfo(entry->File()).isAbsolute())
382  url = QUrl::fromLocalFile(entry->File());
383  else
384  url.setUrl(entry->File());
385 
386  LOG(VB_PLAYBACK, LOG_INFO, QString("Now playing '%1'").arg(url.toString()));
387 
388  deleteIOFactory();
389  createIOFactory(url);
390 
391  if (! haveIOFactory())
392  return false;
393 
394  getIOFactory()->addListener(this);
395  getIOFactory()->setUrl(url);
397  getIOFactory()->start();
398  m_state = ACTIVE;
399 
400  return true;
401 }
402 
404 {
405  LOG(VB_PLAYBACK, LOG_INFO, QString("DecoderHandler: Stopping decoder"));
406 
407  if (m_decoder && m_decoder->isRunning())
408  {
409  m_decoder->lock();
410  m_decoder->stop();
411  m_decoder->unlock();
412  }
413 
414  if (m_decoder)
415  {
416  m_decoder->lock();
417  m_decoder->cond()->wakeAll();
418  m_decoder->unlock();
419  }
420 
421  if (m_decoder)
422  {
423  m_decoder->wait();
424  delete m_decoder;
425  m_decoder = NULL;
426  }
427 
428  deleteIOFactory();
429  doOperationStop();
430 
431  m_state = STOPPED;
432 }
433 
434 void DecoderHandler::customEvent(QEvent *event)
435 {
436  if (DecoderHandlerEvent *dhe = dynamic_cast<DecoderHandlerEvent*>(event))
437  {
438  // Proxy all DecoderHandlerEvents
439  return dispatch(*dhe);
440  }
441  else if (event->type() == MythEvent::MythEventMessage)
442  {
443  MythEvent *me = (MythEvent *)event;
444  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
445 
446  if (tokens.isEmpty())
447  return;
448 
449  if (tokens[0] == "DOWNLOAD_FILE")
450  {
451  QStringList args = me->ExtraDataList();
452 
453  if (tokens[1] == "UPDATE")
454  {
455  }
456  else if (tokens[1] == "FINISHED")
457  {
458  QString downloadUrl = args[0];
459  int fileSize = args[2].toInt();
460  int errorCode = args[4].toInt();
461  QString filename = args[1];
462 
463  if ((errorCode != 0) || (fileSize == 0))
464  {
465  LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler: failed to download playlist from '%1'")
466  .arg(downloadUrl));
467  QUrl url(downloadUrl);
468  m_state = STOPPED;
469  doOperationStop();
470  doFailed(url, "Could not get playlist");
471  }
472  else
473  {
474  QUrl fileUrl(filename);
475  createPlaylistFromFile(fileUrl);
476  }
477  }
478  }
479  }
480 }
481 
483 {
484  QString extension = QFileInfo(url.path()).suffix();
485  LOG(VB_NETWORK, LOG_INFO,
486  QString("File %1 has extension %2")
487  .arg(QFileInfo(url.path()).fileName()).arg(extension));
488 
489  if (extension == "pls" || extension == "m3u")
490  {
491  if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
493  else
495 
496  return;
497  }
498 
500 }
501 
503 {
505 
506  if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
507  entry->setFile(url.toLocalFile());
508  else
509  entry->setFile(url.toString());
510 
511  m_playlist.add(entry);
512 
513  doStart((m_playlist.size() > 0));
514 }
515 
517 {
518  QString file = url.toLocalFile();
519 
521 
522  doStart((m_playlist.size() > 0));
523 }
524 
526 {
527  LOG(VB_NETWORK, LOG_INFO,
528  QString("Retrieving playlist from '%1'").arg(url.toString()));
529 
530  doOperationStart(tr("Retrieving playlist"));
531 
532  QString extension = QFileInfo(url.path()).suffix().toLower();
533  QString saveFilename = GetConfDir() + "/MythMusic/playlist." + extension;
534  GetMythDownloadManager()->queueDownload(url.toString(), saveFilename, this);
535 
536  //TODO should find a better way to do this
537  QTime time;
538  time.start();
539  while (m_state == LOADING)
540  {
541  if (time.elapsed() > 30000)
542  {
543  doOperationStop();
544  GetMythDownloadManager()->cancelDownload(url.toString());
545  LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler:: Timed out trying to download playlist from: %1")
546  .arg(url.toString()));
547  m_state = STOPPED;
548  }
549 
550  qApp->processEvents();
551  usleep(500);
552  }
553 }
554 
555 void DecoderHandler::doConnectDecoder(const QUrl &url, const QString &format)
556 {
557  if (m_decoder && !m_decoder->factory()->supports(format))
558  {
559  delete m_decoder;
560  m_decoder = NULL;
561  }
562 
563  if (!m_decoder)
564  {
565  if ((m_decoder = Decoder::create(format, NULL, true)) == NULL)
566  {
567  doFailed(url, QString("No decoder for this format '%1'").arg(format));
568  return;
569  }
570  }
571 
572  m_decoder->setFilename(url.toString());
573 
575  dispatch(ev);
576 }
577 
578 void DecoderHandler::doFailed(const QUrl &url, const QString &message)
579 {
580  LOG(VB_NETWORK, LOG_ERR,
581  QString("DecoderHandler error: '%1' - %2").arg(message).arg(url.toString()));
582  DecoderHandlerEvent ev(DecoderHandlerEvent::Error, new QString(message));
583  dispatch(ev);
584 }
585 
587 {
588  m_op = true;
589  DecoderHandlerEvent ev(DecoderHandlerEvent::OperationStart, new QString(name));
590  dispatch(ev);
591 }
592 
594 {
595  if (!m_op)
596  return;
597 
598  m_op = false;
599  DecoderHandlerEvent ev(DecoderHandlerEvent::OperationStop);
600  dispatch(ev);
601 }
602 
604 {
605  if (haveIOFactory())
606  deleteIOFactory();
607 
608  if (url.scheme() == "myth")
609  m_io_factory = new DecoderIOFactorySG(this);
610  else if (m_meta && m_meta->Format() == "cast")
612  else if (url.scheme() == "http")
613  m_io_factory = new DecoderIOFactoryUrl(this);
614  else
616 }
617 
619 {
620  if (!haveIOFactory())
621  return;
622 
623  if (m_state == ACTIVE)
624  m_io_factory->stop();
625 
627  m_io_factory->disconnect();
628  m_io_factory->deleteLater();
629  m_io_factory = NULL;
630 }
631 
632 /**********************************************************************/
633 
634 qint64 MusicBuffer::read(char *data, qint64 max, bool doRemove)
635 {
636  QMutexLocker holder (&m_mutex);
637  const char *buffer_data = m_buffer.data();
638 
639  if (max > m_buffer.size())
640  max = m_buffer.size();
641 
642  memcpy(data, buffer_data, max);
643 
644  if (doRemove)
645  m_buffer.remove(0, max);
646 
647  return max;
648 }
649 
650 qint64 MusicBuffer::read(QByteArray &data, qint64 max, bool doRemove)
651 {
652  QMutexLocker holder (&m_mutex);
653  const char *buffer_data = m_buffer.data();
654 
655  if (max > m_buffer.size())
656  max = m_buffer.size();
657 
658  data.append(buffer_data, max);
659 
660  if (doRemove)
661  m_buffer.remove(0, max);
662 
663  return max;
664 }
665 
666 void MusicBuffer::write(const char *data, uint sz)
667 {
668  if (sz == 0)
669  return;
670 
671  QMutexLocker holder(&m_mutex);
672  m_buffer.append(data, sz);
673 }
674 
675 void MusicBuffer::write(QByteArray &array)
676 {
677  if (array.size() == 0)
678  return;
679 
680  QMutexLocker holder(&m_mutex);
681  m_buffer.append(array);
682 }
683 
685 {
686  QMutexLocker holder(&m_mutex);
687  m_buffer.remove(index, len);
688 }
689 
690 /**********************************************************************/
691 
693 {
694  m_buffer = new MusicBuffer;
695  setOpenMode(ReadWrite);
696 }
697 
699 {
700  delete m_buffer;
701 }
702 
703 bool MusicIODevice::open(OpenMode)
704 {
705  return true;
706 }
707 
708 qint64 MusicIODevice::size(void) const
709 {
710  return m_buffer->readBufAvail();
711 }
712 
713 qint64 MusicIODevice::readData(char *data, qint64 maxlen)
714 {
715  qint64 res = m_buffer->read(data, maxlen);
716  emit freeSpaceAvailable();
717  return res;
718 }
719 
720 qint64 MusicIODevice::writeData(const char *data, qint64 sz)
721 {
722  m_buffer->write(data, sz);
723  return sz;
724 }
725 
727 {
728  return m_buffer->readBufAvail();
729 }
730 
732 {
733  assert(0);
734  return -1;
735 }
736 
738 {
739  assert(0);
740  return -1;
741 }
742 
744 {
745  assert(0);
746  return -1;
747 }
748 
749 /**********************************************************************/
750 
752 {
753  m_remotefile = new RemoteFile(url);
754  setOpenMode(ReadWrite);
755 }
756 
758 {
759  m_remotefile->Close();
760  delete m_remotefile;
761 }
762 
763 bool MusicSGIODevice::open(OpenMode)
764 {
765  return m_remotefile->isOpen();
766 }
767 
768 bool MusicSGIODevice::seek(qint64 pos)
769 {
770  long int newPos = -1;
771 
772  if (m_remotefile)
773  newPos = m_remotefile->Seek(pos, 0);
774 
775  return (newPos == pos);
776 }
777 
778 qint64 MusicSGIODevice::size(void) const
779 {
780  return m_remotefile->GetFileSize();
781 }
782 
783 qint64 MusicSGIODevice::readData(char *data, qint64 maxlen)
784 {
785  qint64 res = m_remotefile->Read(data, maxlen);
786  return res;
787 }
788 
789 qint64 MusicSGIODevice::writeData(const char *data, qint64 sz)
790 {
791  m_remotefile->Write(data, sz);
792  return sz;
793 }
794 
796 {
797  return m_remotefile->GetFileSize();
798 }
799 
801 {
802  assert(0);
803  return -1;
804 }
805 
807 {
808  assert(0);
809  return -1;
810 }
811 
813 {
814  assert(0);
815  return -1;
816 }