MythTV  0.28pre
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 
25 /**********************************************************************/
26 
27 QEvent::Type DecoderHandlerEvent::Ready = (QEvent::Type) QEvent::registerEventType();
28 QEvent::Type DecoderHandlerEvent::Meta = (QEvent::Type) QEvent::registerEventType();
29 QEvent::Type DecoderHandlerEvent::BufferStatus = (QEvent::Type) QEvent::registerEventType();
30 QEvent::Type DecoderHandlerEvent::OperationStart = (QEvent::Type) QEvent::registerEventType();
31 QEvent::Type DecoderHandlerEvent::OperationStop = (QEvent::Type) QEvent::registerEventType();
32 QEvent::Type DecoderHandlerEvent::Error = (QEvent::Type) QEvent::registerEventType();
33 
35  : MythEvent(t), m_msg(NULL), m_meta(NULL), m_available(0), m_maxSize(0)
36 {
37  m_meta = new MusicMetadata(meta);
38 }
39 
41 {
42  if (m_msg)
43  delete m_msg;
44 
45  if (m_meta)
46  delete m_meta;
47 }
48 
50 {
51  DecoderHandlerEvent *result = new DecoderHandlerEvent(*this);
52 
53  if (m_msg)
54  result->m_msg = new QString(*m_msg);
55 
56  if (m_meta)
57  result->m_meta = new MusicMetadata(*m_meta);
58 
59  result->m_available = m_available;
60  result->m_maxSize = m_maxSize;
61 
62  return result;
63 }
64 
65 void DecoderHandlerEvent::getBufferStatus(int *available, int *maxSize) const
66 {
67  *available = m_available;
68  *maxSize = m_maxSize;
69 }
70 
71 /**********************************************************************/
72 
74  m_state(STOPPED),
75  m_playlist_pos(0),
76  m_decoder(NULL),
77  m_op(false),
78  m_redirects(0)
79 {
80 }
81 
83 {
84  stop();
85 }
86 
88 {
89  m_state = LOADING;
90 
91  m_playlist.clear();
92  m_meta = *mdata;
93  m_playlist_pos = -1;
94  m_redirects = 0;
95 
96  if (QFileInfo(mdata->Filename()).isAbsolute())
97  m_url = QUrl::fromLocalFile(mdata->Filename());
98  else
99  m_url.setUrl(mdata->Filename());
100 
102 }
103 
104 void DecoderHandler::doStart(bool result)
105 {
106  doOperationStop();
107 
108  if (QFileInfo(m_meta.Filename()).isAbsolute())
109  m_url = QUrl::fromLocalFile(m_meta.Filename());
110  else
111  m_url.setUrl(m_meta.Filename());
112 
113  if (m_state == LOADING && result)
114  {
115  for (int ii = 0; ii < m_playlist.size(); ii++)
116  LOG(VB_PLAYBACK, LOG_INFO, QString("Track %1 = %2")
117  .arg(ii) .arg(m_playlist.get(ii)->File()));
118  next();
119  }
120  else
121  {
122  if (m_state == STOPPED)
123  {
124  doFailed(m_url, "Could not get playlist");
125  }
126  }
127 }
128 
129 void DecoderHandler::error(const QString &e)
130 {
131  QString *str = new QString(e);
132  DecoderHandlerEvent ev(DecoderHandlerEvent::Error, str);
133  dispatch(ev);
134 }
135 
137 {
138  if (m_state == STOPPED)
139  return true;
140 
141  if (m_playlist_pos + 1 >= m_playlist.size())
142  {
143  m_state = STOPPED;
144  return true;
145  }
146 
147  return false;
148 }
149 
151 {
152  if (done())
153  return false;
154 
155  if (m_meta.Format() == "cast")
156  {
158  }
159  else
160  {
161  m_playlist_pos++;
162  }
163 
165 
166  if (QFileInfo(entry->File()).isAbsolute())
167  m_url = QUrl::fromLocalFile(entry->File());
168  else
169  m_url.setUrl(entry->File());
170 
171  LOG(VB_PLAYBACK, LOG_INFO, QString("Now playing '%1'").arg(m_url.toString()));
172 
173  // we use the avfdecoder for everything except CD tracks
174  if (m_url.toString().endsWith(".cda"))
175  doConnectDecoder(m_url, ".cda");
176  else
177  {
178  // we don't know what format radio stations are so fake a format
179  // and hope avfdecoder can decode it
180  doConnectDecoder(m_url, ".mp3");
181  }
182 
183  m_state = ACTIVE;
184 
185  return true;
186 }
187 
189 {
190  LOG(VB_PLAYBACK, LOG_INFO, QString("DecoderHandler: Stopping decoder"));
191 
192  if (m_decoder && m_decoder->isRunning())
193  {
194  m_decoder->lock();
195  m_decoder->stop();
196  m_decoder->unlock();
197  }
198 
199  if (m_decoder)
200  {
201  m_decoder->lock();
202  m_decoder->cond()->wakeAll();
203  m_decoder->unlock();
204  }
205 
206  if (m_decoder)
207  {
208  m_decoder->wait();
209  delete m_decoder;
210  m_decoder = NULL;
211  }
212 
213  doOperationStop();
214 
215  m_state = STOPPED;
216 }
217 
218 void DecoderHandler::customEvent(QEvent *event)
219 {
220  if (DecoderHandlerEvent *dhe = dynamic_cast<DecoderHandlerEvent*>(event))
221  {
222  // Proxy all DecoderHandlerEvents
223  return dispatch(*dhe);
224  }
225  else if (event->type() == MythEvent::MythEventMessage)
226  {
227  MythEvent *me = (MythEvent *)event;
228  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
229 
230  if (tokens.isEmpty())
231  return;
232 
233  if (tokens[0] == "DOWNLOAD_FILE")
234  {
235  QStringList args = me->ExtraDataList();
236 
237  if (tokens[1] == "UPDATE")
238  {
239  }
240  else if (tokens[1] == "FINISHED")
241  {
242  QString downloadUrl = args[0];
243  int fileSize = args[2].toInt();
244  int errorCode = args[4].toInt();
245  QString filename = args[1];
246 
247  if ((errorCode != 0) || (fileSize == 0))
248  {
249  LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler: failed to download playlist from '%1'")
250  .arg(downloadUrl));
251  QUrl url(downloadUrl);
252  m_state = STOPPED;
253  doOperationStop();
254  doFailed(url, "Could not get playlist");
255  }
256  else
257  {
258  QUrl fileUrl = QUrl::fromLocalFile(filename);
259  createPlaylistFromFile(fileUrl);
260  }
261  }
262  }
263  }
264 }
265 
266 void DecoderHandler::createPlaylist(const QUrl &url)
267 {
268  QString extension = QFileInfo(url.path()).suffix();
269  LOG(VB_NETWORK, LOG_INFO,
270  QString("File %1 has extension %2")
271  .arg(QFileInfo(url.path()).fileName()).arg(extension));
272 
273  if (extension == "pls" || extension == "m3u" || extension == "asx")
274  {
275  if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
277  else
279 
280  return;
281  }
282 
284 }
285 
287 {
289 
290  if (url.scheme() == "file" || QFileInfo(url.toString()).isAbsolute())
291  entry->setFile(url.toLocalFile());
292  else
293  entry->setFile(url.toString());
294 
295  m_playlist.add(entry);
296 
297  doStart((m_playlist.size() > 0));
298 }
299 
301 {
302  QString file = url.toLocalFile();
303 
305 
306  doStart((m_playlist.size() > 0));
307 }
308 
310 {
311  LOG(VB_NETWORK, LOG_INFO,
312  QString("Retrieving playlist from '%1'").arg(url.toString()));
313 
314  doOperationStart(tr("Retrieving playlist"));
315 
316  QString extension = QFileInfo(url.path()).suffix().toLower();
317  QString saveFilename = GetConfDir() + "/MythMusic/playlist." + extension;
318  GetMythDownloadManager()->queueDownload(url.toString(), saveFilename, this);
319 
320  //TODO should find a better way to do this
321  QTime time;
322  time.start();
323  while (m_state == LOADING)
324  {
325  if (time.elapsed() > 30000)
326  {
327  doOperationStop();
328  GetMythDownloadManager()->cancelDownload(url.toString());
329  LOG(VB_GENERAL, LOG_ERR, QString("DecoderHandler:: Timed out trying to download playlist from: %1")
330  .arg(url.toString()));
331  m_state = STOPPED;
332  }
333 
334  qApp->processEvents();
335  usleep(500);
336  }
337 }
338 
339 void DecoderHandler::doConnectDecoder(const QUrl &url, const QString &format)
340 {
341  if (m_decoder && !m_decoder->factory()->supports(format))
342  {
343  delete m_decoder;
344  m_decoder = NULL;
345  }
346 
347  if (!m_decoder)
348  {
349  if ((m_decoder = Decoder::create(format, NULL, true)) == NULL)
350  {
351  doFailed(url, QString("No decoder for this format '%1'").arg(format));
352  return;
353  }
354  }
355 
356  m_decoder->setURL(url.toString());
357 
359  dispatch(ev);
360 }
361 
362 void DecoderHandler::doFailed(const QUrl &url, const QString &message)
363 {
364  LOG(VB_NETWORK, LOG_ERR,
365  QString("DecoderHandler error: '%1' - %2").arg(message).arg(url.toString()));
366  DecoderHandlerEvent ev(DecoderHandlerEvent::Error, new QString(message));
367  dispatch(ev);
368 }
369 
371 {
372  m_op = true;
373  DecoderHandlerEvent ev(DecoderHandlerEvent::OperationStart, new QString(name));
374  dispatch(ev);
375 }
376 
378 {
379  if (!m_op)
380  return;
381 
382  m_op = false;
383  DecoderHandlerEvent ev(DecoderHandlerEvent::OperationStop);
384  dispatch(ev);
385 }
void createPlaylistForSingleFile(const QUrl &url)
void customEvent(QEvent *e)
void add(PlayListFileEntry *e)
Add a entry to the playlist.
Definition: pls.h:80
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
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
virtual void unlock(void)
Definition: decoder.h:83
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:308
QString File(void)
Definition: pls.h:25
void clear(void)
Clear out all the entries.
Definition: pls.h:83
const char * filename
Definition: ioapi.h:135
MusicMetadata m_meta
Events sent by the DecoderHandler and it's helper classes.
void getBufferStatus(int *available, int *maxSize) const
void doOperationStop(void)
virtual ~DecoderHandler(void)
MusicMetadata * m_meta
QString GetConfDir(void)
Definition: mythdirs.cpp:140
virtual void lock(void)
Definition: decoder.h:82
bool isRunning(void) const
Definition: mthread.cpp:271
This class is used as a container for messages.
Definition: mythevent.h:15
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
static Decoder * create(const QString &, AudioOutput *, bool=false)
Definition: decoder.cpp:113
virtual bool supports(const QString &source) const =0
unsigned char t
Definition: ParseText.cpp:339
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
void doOperationStart(const QString &name)
static Type OperationStart
void setFile(const QString &f)
Definition: pls.h:29
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:86
int size(void) const
Get the number of entries in the pls file.
Definition: pls.h:54
PlayListFileEntry * get(int i)
Get a file entry.
Definition: pls.h:60
virtual MythEvent * clone(void) const
void doFailed(const QUrl &url, const QString &message)
virtual void stop()=0
void doStart(bool result)
Class for representing entries in a pls file.
Definition: pls.h:19
GLint GLenum GLsizei GLint GLenum format
void doConnectDecoder(const QUrl &url, const QString &format)
QString Filename(bool find=true)
static Type BufferStatus
const QStringList & ExtraDataList() const
Definition: mythevent.h:59
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:143
static int parse(PlayListFile *pls, const QString &filename)
Parse a pls, m3u or asx playlist file.
Definition: pls.cpp:41
Decoder * m_decoder
DecoderFactory * factory() const
Definition: decoder.h:76
const QString & Message() const
Definition: mythevent.h:57
void createPlaylist(const QUrl &url)
QString Format() const
void start(MusicMetadata *mdata)
void setURL(const QString &url)
Definition: decoder.h:80