MythTV  0.28pre
 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 {
87 }
88 
90 {
91  return m_handler->getDecoder();
92 }
93 
94 void DecoderIOFactory::doFailed(const QString &message)
95 {
97  m_handler->doFailed(m_handler->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 = m_handler->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(m_handler->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 = m_handler->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(m_handler->getUrl().path());
164 }
165 
166 /**********************************************************************/
167 
169  : DecoderIOFactory(parent), m_started(false),
170  m_accessManager(new QNetworkAccessManager(this)),
171  m_reply(NULL), m_input(new MusicIODevice()),
172  m_redirectCount(0), m_bytesWritten(0)
173 
174 {
175  connect(m_input, SIGNAL(freeSpaceAvailable()), SLOT(readyRead()));
176 
177  m_input->open(QIODevice::ReadWrite);
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(m_handler->getUrl().toString()));
199 
200  m_started = false;
201 
202  doOperationStart(tr("Fetching remote file"));
203 
204  m_reply = m_accessManager->get(QNetworkRequest(m_handler->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  m_handler->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 {
271  doConnectDecoder(m_handler->getUrl().toString());
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_op(false),
289  m_redirects(0)
290 {
291 }
292 
294 {
295  stop();
296 }
297 
299 {
300  m_state = LOADING;
301 
302  m_playlist.clear();
303  m_meta = *mdata;
304  m_playlist_pos = -1;
305  m_redirects = 0;
306 
307  if (QFileInfo(mdata->Filename()).isAbsolute())
308  m_url = QUrl::fromLocalFile(mdata->Filename());
309  else
310  m_url.setUrl(mdata->Filename());
311 
313 }
314 
315 void DecoderHandler::doStart(bool result)
316 {
317  doOperationStop();
318 
319  if (QFileInfo(m_meta.Filename()).isAbsolute())
320  m_url = QUrl::fromLocalFile(m_meta.Filename());
321  else
322  m_url.setUrl(m_meta.Filename());
323 
324  if (m_state == LOADING && result)
325  {
326  for (int ii = 0; ii < m_playlist.size(); ii++)
327  LOG(VB_PLAYBACK, LOG_INFO, QString("Track %1 = %2")
328  .arg(ii) .arg(m_playlist.get(ii)->File()));
329  next();
330  }
331  else
332  {
333  if (m_state == STOPPED)
334  {
335  doFailed(m_url, "Could not get playlist");
336  }
337  }
338 }
339 
340 void DecoderHandler::error(const QString &e)
341 {
342  QString *str = new QString(e);
343  DecoderHandlerEvent ev(DecoderHandlerEvent::Error, str);
344  dispatch(ev);
345 }
346 
348 {
349  if (m_state == STOPPED)
350  return true;
351 
352  if (m_playlist_pos + 1 >= m_playlist.size())
353  {
354  m_state = STOPPED;
355  return true;
356  }
357 
358  return false;
359 }
360 
362 {
363  if (done())
364  return false;
365 
366  if (m_meta.Format() == "cast")
367  {
369  }
370  else
371  {
372  m_playlist_pos++;
373  }
374 
376 
377  if (QFileInfo(entry->File()).isAbsolute())
378  m_url = QUrl::fromLocalFile(entry->File());
379  else
380  m_url.setUrl(entry->File());
381 
382  LOG(VB_PLAYBACK, LOG_INFO, QString("Now playing '%1'").arg(m_url.toString()));
383 
384  deleteIOFactory();
386 
387  if (! haveIOFactory())
388  return false;
389 
390  getIOFactory()->addListener(this);
391  getIOFactory()->start();
392  m_state = ACTIVE;
393 
394  return true;
395 }
396 
398 {
399  LOG(VB_PLAYBACK, LOG_INFO, QString("DecoderHandler: Stopping decoder"));
400 
401  if (m_decoder && m_decoder->isRunning())
402  {
403  m_decoder->lock();
404  m_decoder->stop();
405  m_decoder->unlock();
406  }
407 
408  if (m_decoder)
409  {
410  m_decoder->lock();
411  m_decoder->cond()->wakeAll();
412  m_decoder->unlock();
413  }
414 
415  if (m_decoder)
416  {
417  m_decoder->wait();
418  delete m_decoder;
419  m_decoder = NULL;
420  }
421 
422  deleteIOFactory();
423  doOperationStop();
424 
425  m_state = STOPPED;
426 }
427 
428 void DecoderHandler::customEvent(QEvent *event)
429 {
430  if (DecoderHandlerEvent *dhe = dynamic_cast<DecoderHandlerEvent*>(event))
431  {
432  // Proxy all DecoderHandlerEvents
433  return dispatch(*dhe);
434  }
435  else if (event->type() == MythEvent::MythEventMessage)
436  {
437  MythEvent *me = (MythEvent *)event;
438  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
439 
440  if (tokens.isEmpty())
441  return;
442 
443  if (tokens[0] == "DOWNLOAD_FILE")
444  {
445  QStringList args = me->ExtraDataList();
446 
447  if (tokens[1] == "UPDATE")
448  {
449  }
450  else if (tokens[1] == "FINISHED")
451  {
452  QString downloadUrl = args[0];
453  int fileSize = args[2].toInt();
454  int errorCode = args[4].toInt();
455  QString filename = args[1];
456 
457  if ((errorCode != 0) || (fileSize == 0))
458  {
459  LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler: failed to download playlist from '%1'")
460  .arg(downloadUrl));
461  QUrl url(downloadUrl);
462  m_state = STOPPED;
463  doOperationStop();
464  doFailed(url, "Could not get playlist");
465  }
466  else
467  {
468  QUrl fileUrl(filename);
469  createPlaylistFromFile(fileUrl);
470  }
471  }
472  }
473  }
474 }
475 
476 void DecoderHandler::createPlaylist(const QUrl &url)
477 {
478  QString extension = QFileInfo(url.path()).suffix();
479  LOG(VB_NETWORK, LOG_INFO,
480  QString("File %1 has extension %2")
481  .arg(QFileInfo(url.path()).fileName()).arg(extension));
482 
483  if (extension == "pls" || extension == "m3u")
484  {
485  if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
487  else
489 
490  return;
491  }
492 
494 }
495 
497 {
499 
500  if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
501  entry->setFile(url.toLocalFile());
502  else
503  entry->setFile(url.toString());
504 
505  m_playlist.add(entry);
506 
507  doStart((m_playlist.size() > 0));
508 }
509 
511 {
512  QString file = url.toLocalFile();
513 
515 
516  doStart((m_playlist.size() > 0));
517 }
518 
520 {
521  LOG(VB_NETWORK, LOG_INFO,
522  QString("Retrieving playlist from '%1'").arg(url.toString()));
523 
524  doOperationStart(tr("Retrieving playlist"));
525 
526  QString extension = QFileInfo(url.path()).suffix().toLower();
527  QString saveFilename = GetConfDir() + "/MythMusic/playlist." + extension;
528  GetMythDownloadManager()->queueDownload(url.toString(), saveFilename, this);
529 
530  //TODO should find a better way to do this
531  QTime time;
532  time.start();
533  while (m_state == LOADING)
534  {
535  if (time.elapsed() > 30000)
536  {
537  doOperationStop();
538  GetMythDownloadManager()->cancelDownload(url.toString());
539  LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler:: Timed out trying to download playlist from: %1")
540  .arg(url.toString()));
541  m_state = STOPPED;
542  }
543 
544  qApp->processEvents();
545  usleep(500);
546  }
547 }
548 
549 void DecoderHandler::doConnectDecoder(const QUrl &url, const QString &format)
550 {
551  if (m_decoder && !m_decoder->factory()->supports(format))
552  {
553  delete m_decoder;
554  m_decoder = NULL;
555  }
556 
557  if (!m_decoder)
558  {
559  if ((m_decoder = Decoder::create(format, NULL, true)) == NULL)
560  {
561  doFailed(url, QString("No decoder for this format '%1'").arg(format));
562  return;
563  }
564  }
565 
566  m_decoder->setFilename(url.toString());
567 
569  dispatch(ev);
570 }
571 
572 void DecoderHandler::doFailed(const QUrl &url, const QString &message)
573 {
574  LOG(VB_NETWORK, LOG_ERR,
575  QString("DecoderHandler error: '%1' - %2").arg(message).arg(url.toString()));
576  DecoderHandlerEvent ev(DecoderHandlerEvent::Error, new QString(message));
577  dispatch(ev);
578 }
579 
581 {
582  m_op = true;
583  DecoderHandlerEvent ev(DecoderHandlerEvent::OperationStart, new QString(name));
584  dispatch(ev);
585 }
586 
588 {
589  if (!m_op)
590  return;
591 
592  m_op = false;
593  DecoderHandlerEvent ev(DecoderHandlerEvent::OperationStop);
594  dispatch(ev);
595 }
596 
597 void DecoderHandler::createIOFactory(const QUrl &url)
598 {
599  if (haveIOFactory())
600  deleteIOFactory();
601 
602  if (url.scheme() == "myth")
603  m_io_factory = new DecoderIOFactorySG(this);
604  else if (m_meta.Format() == "cast")
606  else if (url.scheme() == "http")
607  m_io_factory = new DecoderIOFactoryUrl(this);
608  else
610 }
611 
613 {
614  if (!haveIOFactory())
615  return;
616 
617  if (m_state == ACTIVE)
618  m_io_factory->stop();
619 
621  m_io_factory->disconnect();
622  m_io_factory->deleteLater();
623  m_io_factory = NULL;
624 }
625 
626 /**********************************************************************/
627 
628 qint64 MusicBuffer::read(char *data, qint64 max, bool doRemove)
629 {
630  QMutexLocker holder (&m_mutex);
631  const char *buffer_data = m_buffer.data();
632 
633  if (max > m_buffer.size())
634  max = m_buffer.size();
635 
636  memcpy(data, buffer_data, max);
637 
638  if (doRemove)
639  m_buffer.remove(0, max);
640 
641  return max;
642 }
643 
644 qint64 MusicBuffer::read(QByteArray &data, qint64 max, bool doRemove)
645 {
646  QMutexLocker holder (&m_mutex);
647  const char *buffer_data = m_buffer.data();
648 
649  if (max > m_buffer.size())
650  max = m_buffer.size();
651 
652  data.append(buffer_data, max);
653 
654  if (doRemove)
655  m_buffer.remove(0, max);
656 
657  return max;
658 }
659 
660 void MusicBuffer::write(const char *data, uint sz)
661 {
662  if (sz == 0)
663  return;
664 
665  QMutexLocker holder(&m_mutex);
666  m_buffer.append(data, sz);
667 }
668 
669 void MusicBuffer::write(QByteArray &array)
670 {
671  if (array.size() == 0)
672  return;
673 
674  QMutexLocker holder(&m_mutex);
675  m_buffer.append(array);
676 }
677 
679 {
680  QMutexLocker holder(&m_mutex);
681  m_buffer.remove(index, len);
682 }
683 
684 /**********************************************************************/
685 
687 {
688  m_buffer = new MusicBuffer;
689  setOpenMode(ReadWrite);
690 }
691 
693 {
694  delete m_buffer;
695 }
696 
697 bool MusicIODevice::open(OpenMode)
698 {
699  return true;
700 }
701 
702 qint64 MusicIODevice::size(void) const
703 {
704  return m_buffer->readBufAvail();
705 }
706 
707 qint64 MusicIODevice::readData(char *data, qint64 maxlen)
708 {
709  qint64 res = m_buffer->read(data, maxlen);
710  emit freeSpaceAvailable();
711  return res;
712 }
713 
714 qint64 MusicIODevice::writeData(const char *data, qint64 sz)
715 {
716  m_buffer->write(data, sz);
717  return sz;
718 }
719 
721 {
722  return m_buffer->readBufAvail();
723 }
724 
726 {
727  assert(0);
728  return -1;
729 }
730 
732 {
733  assert(0);
734  return -1;
735 }
736 
738 {
739  assert(0);
740  return -1;
741 }
742 
743 /**********************************************************************/
744 
746 {
747  m_remotefile = new RemoteFile(url);
748  setOpenMode(ReadWrite);
749 }
750 
752 {
753  delete m_remotefile;
754 }
755 
756 bool MusicSGIODevice::open(OpenMode)
757 {
758  return m_remotefile->isOpen();
759 }
760 
761 bool MusicSGIODevice::seek(qint64 pos)
762 {
763  long int newPos = -1;
764 
765  if (m_remotefile)
766  newPos = m_remotefile->Seek(pos, 0);
767 
768  return (newPos == pos);
769 }
770 
771 qint64 MusicSGIODevice::size(void) const
772 {
773  return m_remotefile->GetFileSize();
774 }
775 
776 qint64 MusicSGIODevice::readData(char *data, qint64 maxlen)
777 {
778  qint64 res = m_remotefile->Read(data, maxlen);
779  return res;
780 }
781 
782 qint64 MusicSGIODevice::writeData(const char *data, qint64 sz)
783 {
784  m_remotefile->Write(data, sz);
785  return sz;
786 }
787 
789 {
790  return m_remotefile->GetFileSize();
791 }
792 
794 {
795  assert(0);
796  return -1;
797 }
798 
800 {
801  assert(0);
802  return -1;
803 }
804 
806 {
807  assert(0);
808  return -1;
809 }
qint64 bytesAvailable(void) const
void freeSpaceAvailable(void)
void createPlaylistForSingleFile(const QUrl &url)
MYTH_GLsizeiptr const GLvoid * data
DecoderHandler * m_handler
void customEvent(QEvent *e)
RemoteFile * m_remotefile
QNetworkAccessManager * m_accessManager
void remove(int index, int len)
Decoder * getDecoder(void)
static const uint DefaultPrebufferSize
qint64 writeData(const char *data, qint64 sz)
void add(PlayListFileEntry *e)
Add a entry to the playlist.
Definition: pls.h:80
qint64 size(void) const
virtual void close(void)
void setUrl(const QUrl &url)
void doConnectDecoder(const QString &format)
void replyFinished(QNetworkReply *reply)
DecoderIOFactorySG(DecoderHandler *parent)
void queueDownload(const QString &url, const QString &dest, QObject *caller, const bool reload=false)
Adds a url to the download queue.
static Type MythEventMessage
Definition: mythevent.h:65
void removeListener(QObject *listener)
Remove a listener to the observable.
GLuint index
virtual void unlock(void)
Definition: decoder.h:84
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:308
void write(const char *data, uint sz)
assert(size > 0)
bool seek(qint64 pos)
void doOperationStart(const QString &name)
static const uint MaxRedirects
MusicMetadata & getMetadata()
return memcpy(dest, src, len)
QString File(void)
Definition: pls.h:25
virtual bool open(OpenMode mode)
GLenum GLsizei len
AllMusic * parent
void clear(void)
Clear out all the entries.
Definition: pls.h:83
const char * filename
Definition: ioapi.h:135
unsigned int uint
Definition: compat.h:135
MusicMetadata m_meta
Events sent by the DecoderHandler and it's helper classes.
void addListener(QObject *listener)
Add a listener to the observable.
void getBufferStatus(int *available, int *maxSize) const
DecoderIOFactory(DecoderHandler *parent)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
unsigned char t
Definition: ParseText.cpp:339
void doOperationStop(void)
virtual ~DecoderHandler(void)
bool haveIOFactory(void)
MusicMetadata * m_meta
static const uint DefaultBufferSize
void doFailed(const QString &message)
QString GetConfDir(void)
Definition: mythdirs.cpp:147
virtual void lock(void)
Definition: decoder.h:83
qint64 read(char *data, qint64 max, bool doRemove=true)
virtual ~DecoderIOFactory()
DecoderIOFactoryUrl(DecoderHandler *parent)
bool isRunning(void) const
Definition: mthread.cpp:271
This class is used as a container for messages.
Definition: mythevent.h:15
QIODevice * getInput(void)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
static Decoder * create(const QString &, AudioOutput *, bool=false)
Definition: decoder.cpp:111
virtual bool supports(const QString &source) const =0
void deleteIOFactory(void)
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
QNetworkReply * m_reply
virtual void stop(void)=0
void doOperationStart(const QString &name)
int Read(void *data, int size)
Definition: remotefile.cpp:786
qint64 bytesAvailable(void) const
static Type OperationStart
void setFile(const QString &f)
Definition: pls.h:29
qint64 writeData(const char *data, qint64 sz)
void createPlaylistFromFile(const QUrl &url)
const char * name
Definition: ParseText.cpp:338
void error(const QString &msg)
void createPlaylistFromRemoteUrl(const QUrl &url)
QWaitCondition * cond()
Definition: decoder.h:88
int size(void) const
Get the number of entries in the pls file.
Definition: pls.h:54
virtual ~MusicSGIODevice(void)
PlayListFileEntry * get(int i)
Get a file entry.
Definition: pls.h:60
virtual void start(void)=0
MusicBuffer * m_buffer
virtual MythEvent * clone(void) const
bool isOpen(void) const
Definition: remotefile.cpp:227
MusicIODevice * m_input
void doFailed(const QUrl &url, const QString &message)
virtual void stop()=0
int putch(int c)
Decoder * getDecoder(void)
void doStart(bool result)
return strftime a b e
Definition: yrnoxml.pl:329
Class for representing entries in a pls file.
Definition: pls.h:19
void setFilename(const QString &newName)
Definition: decoder.h:81
GLint GLenum GLsizei GLint GLenum format
QByteArray m_buffer
qint64 readData(char *data, qint64 sz)
qint64 readData(char *data, qint64 sz)
void doConnectDecoder(const QUrl &url, const QString &format)
QIODevice * getInput(void)
virtual bool open(OpenMode mode)
void createIOFactory(const QUrl &url)
QString Filename(bool find=true)
static Type BufferStatus
const QStringList & ExtraDataList() const
Definition: mythevent.h:59
qint64 readBufAvail(void) const
Class for starting stream decoding.
DecoderIOFactory * getIOFactory(void)
static Type OperationStop
void cancelDownload(const QString &url, bool block=true)
Cancel a queued or current download.
PlayListFile m_playlist
static long int random(void)
Definition: compat.h:142
long long Seek(long long pos, int whence, long long curpos=-1)
Definition: remotefile.cpp:615
static int parse(PlayListFile *pls, const QString &filename)
Parse a pls or m3u playlist file.
Definition: pls.cpp:40
DecoderIOFactory * m_io_factory
Decoder * m_decoder
void doOperationStop(void)
MusicSGIODevice(const QString &url)
long long GetFileSize(void) const
GetFileSize: returns the remote file's size at the time it was first opened Will query the server in ...
Definition: remotefile.cpp:902
DecoderFactory * factory() const
Definition: decoder.h:76
int Write(const void *data, int size)
Definition: remotefile.cpp:686
const QString & Message() const
Definition: mythevent.h:57
QIODevice * getInput(void)
qint64 size(void) const
void createPlaylist(const QUrl &url)
QString Format() const
DecoderIOFactoryFile(DecoderHandler *parent)
void start(MusicMetadata *mdata)
The glue between the DecoderHandler and the Decoder.