MythTV master
cddecoder.cpp
Go to the documentation of this file.
1#define DO_NOT_WANT_PARANOIA_COMPATIBILITY
2#include "cddecoder.h"
3
4// C
5#include <cstdlib>
6#include <cstring>
7#include <unistd.h>
8
9// Qt
10#include <QFile>
11#include <QIODevice>
12#include <QObject>
13#include <QString>
14
15// libcdio
16// cdda already included via cddecoder.h
17#include <cdio/logging.h>
18
19// MythTV
23
24extern "C" {
25#include <libavcodec/avcodec.h>
26}
27
28// MythMusic
29#include "constants.h"
30static constexpr const char* CDEXT { ".cda" };
31static constexpr long kSamplesPerSec { 44100 };
32
33// Handle cdio log output
34static void logger(cdio_log_level_t level, const char *message)
35{
36 switch (level)
37 {
38 case CDIO_LOG_DEBUG:
39 break;
40 case CDIO_LOG_INFO:
41 LOG(VB_MEDIA, LOG_DEBUG, QString("INFO cdio: %1").arg(message));
42 break;
43 case CDIO_LOG_WARN:
44 LOG(VB_MEDIA, LOG_DEBUG, QString("WARN cdio: %1").arg(message));
45 break;
46 case CDIO_LOG_ERROR:
47 case CDIO_LOG_ASSERT:
48 LOG(VB_GENERAL, LOG_ERR, QString("ERROR cdio: %1").arg(message));
49 break;
50 }
51}
52
53// Open a cdio device
54static CdIo_t * openCdio(const QString& name)
55{
56 // Setup log handler
57 static int s_logging;
58 if (!s_logging)
59 {
60 s_logging = 1;
61 cdio_log_set_handler(&logger);
62 }
63
64 CdIo_t *cdio = cdio_open(name.toLatin1(), DRIVER_DEVICE);
65 if (!cdio)
66 {
67 LOG(VB_MEDIA, LOG_INFO, QString("CdDecoder: cdio_open(%1) failed").
68 arg(name));
69 }
70 return cdio;
71}
72
73// Stack-based cdio device open
75{
76 CdIo_t* m_cdio {nullptr};
77
78 void* operator new(std::size_t); // Stack only
79 // No copying
82
83public:
84 explicit StCdioDevice(const QString& dev) : m_cdio(openCdio(dev)) { }
85 ~StCdioDevice() { if (m_cdio) cdio_destroy(m_cdio); }
86
87 operator CdIo_t*() const { return m_cdio; } // NOLINT(google-explicit-constructor)
88};
89
90
92 Decoder(d, o)
93{
94 setURL(file);
95}
96
97// virtual
99{
100 if (m_inited)
101 deinit();
102}
103
104void CdDecoder::setDevice(const QString &dev)
105{
106 m_deviceName = dev;
107#ifdef WIN32
108 // libcdio needs the drive letter with no path
109 if (m_deviceName.endsWith('\\'))
110 m_deviceName.chop(1);
111#endif
112}
113
114// pure virtual
116{
117 m_userStop = true;
118}
119
120// private
122{
123 while (m_seekTime <= +0.)
124 {
125 if(output()->AddFrames(m_outputBuf, m_bksFrames, -1ms))
126 {
127 if (m_outputAt >= m_bks)
128 {
129 m_outputAt -= m_bks;
130 std::memmove(m_outputBuf, m_outputBuf + m_bks, m_outputAt);
131 }
132 break;
133 }
134 ::usleep(output()->GetAudioBufferedTime().count()<<9);
135 }
136}
137
138//static
139QRecursiveMutex& CdDecoder::getCdioMutex()
140{
141 static QRecursiveMutex s_mtx;
142 return s_mtx;
143}
144
145// pure virtual
147{
148 if (m_inited)
149 return true;
150
151 m_inited = m_userStop = m_finish = false;
152 m_freq = m_bitrate = 0L;
154 m_chan = 0;
155 m_seekTime = -1.;
156
157 if (output())
159
160 m_trackNum = getURL().section('.', 0, 0).toUInt();
161
162 QMutexLocker lock(&getCdioMutex());
163
164 m_cdio = openCdio(m_deviceName);
165 if (!m_cdio)
166 return false;
167
168 m_start = cdio_get_track_lsn(m_cdio, m_trackNum);
169 m_end = cdio_get_track_last_lsn(m_cdio, m_trackNum);
170 if (CDIO_INVALID_LSN == m_start ||
171 CDIO_INVALID_LSN == m_end)
172 {
173 LOG(VB_MEDIA, LOG_INFO, "CdDecoder: No tracks on " + m_deviceName);
174 cdio_destroy(m_cdio), m_cdio = nullptr;
175 return false;
176 }
177
178 LOG(VB_MEDIA, LOG_DEBUG, QString("CdDecoder track=%1 lsn start=%2 end=%3")
179 .arg(m_trackNum).arg(m_start).arg(m_end));
180 m_curPos = m_start;
181
182 m_device = cdio_cddap_identify_cdio(m_cdio, 0, nullptr);
183 if (nullptr == m_device)
184 {
185 LOG(VB_GENERAL, LOG_ERR,
186 QString("Error: CdDecoder: cdio_cddap_identify(%1) failed")
187 .arg(m_deviceName));
188 cdio_destroy(m_cdio), m_cdio = nullptr;
189 return false;
190 }
191
192 cdio_cddap_verbose_set(m_device,
193 VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_ANY) ? CDDA_MESSAGE_PRINTIT :
194 CDDA_MESSAGE_FORGETIT,
195 VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_DEBUG) ? CDDA_MESSAGE_PRINTIT :
196 CDDA_MESSAGE_FORGETIT);
197
198 if (DRIVER_OP_SUCCESS == cdio_cddap_open(m_device))
199 {
200 // cdio_get_track_last_lsn is unreliable on discs with data at end
201 lsn_t end2 = cdio_cddap_track_lastsector(m_device, m_trackNum);
202 if (end2 < m_end)
203 {
204 LOG(VB_MEDIA, LOG_INFO, QString("CdDecoder: trim last lsn from %1 to %2")
205 .arg(m_end).arg(end2));
206 m_end = end2;
207 }
208
209 // FIXME can't use cdio_paranoia until we find a way to cleanly
210 // detect when the user has ejected a CD otherwise we enter a
211 // recursive loop in cdio_paranoia_read_limited()
212 //m_paranoia = cdio_paranoia_init(m_device);
213 if (nullptr != m_paranoia)
214 {
215 cdio_paranoia_modeset(m_paranoia, PARANOIA_MODE_DISABLE);
216 (void)cdio_paranoia_seek(m_paranoia, m_start, SEEK_SET);
217 }
218 else
219 {
220 LOG(VB_GENERAL, LOG_WARNING, "CD reading with paranoia is disabled");
221 }
222 }
223 else
224 {
225 LOG(VB_GENERAL, LOG_ERR,
226 QString("Warn: drive '%1' is not cdda capable").
227 arg(m_deviceName));
228 }
229
230 int chnls = cdio_get_track_channels(m_cdio, m_trackNum);
231 m_chan = chnls > 0 ? chnls : 2;
233
234 if (output())
235 {
236 const AudioSettings settings(FORMAT_S16, m_chan,
237 AV_CODEC_ID_PCM_S16LE, m_freq, false /* AC3/DTS passthru */);
238 output()->Reconfigure(settings);
240 }
241
242 // 20ms worth
243 m_bks = (m_freq * m_chan * 2) / 50;
244 m_bksFrames = m_freq / 50;
245 // decode 8 bks worth of samples each time we need more
246 m_decodeBytes = m_bks << 3;
247
248 m_outputBuf = reinterpret_cast< char* >(
249 ::av_malloc(m_decodeBytes + CDIO_CD_FRAMESIZE_RAW * 2));
250 m_outputAt = 0;
251
252 setCDSpeed(2);
253
254 m_inited = true;
255
256 return m_inited;
257}
258
259// pure virtual
260void CdDecoder::seek(double pos)
261{
262 m_seekTime = pos;
263 if (output())
265}
266
267// private
269{
270 setCDSpeed(-1);
271
272 QMutexLocker lock(&getCdioMutex());
273
274 if (m_paranoia)
275 cdio_paranoia_free(m_paranoia), m_paranoia = nullptr;
276 if (m_device)
277 cdio_cddap_close(m_device), m_device = nullptr, m_cdio = nullptr;
278 if (m_cdio)
279 cdio_destroy(m_cdio), m_cdio = nullptr;
280
281 if (m_outputBuf)
282 ::av_free(m_outputBuf), m_outputBuf = nullptr;
283
284 m_inited = m_userStop = m_finish = false;
285 m_freq = m_bitrate = 0L;
287 m_chan = 0;
288 setOutput(nullptr);
289}
290
291// private virtual
293{
294 RunProlog();
295
296 if (!m_inited)
297 {
298 RunEpilog();
299 return;
300 }
301
303 // NB block scope required to prevent re-entrancy
304 {
306 dispatch(e);
307 }
308
309 // account for possible frame expansion in aobase (upmix, float conv)
310 const std::size_t thresh = m_bks * 6;
311
312 while (!m_finish && !m_userStop)
313 {
314 if (m_seekTime >= +0.)
315 {
316 m_curPos = m_start + static_cast< lsn_t >(
317 (m_seekTime * kSamplesPerSec) / CD_FRAMESAMPLES);
318 if (m_paranoia)
319 {
320 QMutexLocker lock(&getCdioMutex());
321 cdio_paranoia_seek(m_paranoia, m_curPos, SEEK_SET);
322 }
323
324 m_outputAt = 0;
325 m_seekTime = -1.;
326 }
327
328 if (m_outputAt < m_bks)
329 {
330 while (m_outputAt < m_decodeBytes &&
331 !m_finish && !m_userStop && m_seekTime <= +0.)
332 {
333 if (m_curPos < m_end)
334 {
335 QMutexLocker lock(&getCdioMutex());
336 if (m_paranoia)
337 {
338 int16_t *cdbuffer = cdio_paranoia_read_limited(
339 m_paranoia, nullptr, 10);
340 if (cdbuffer)
341 memcpy(&m_outputBuf[m_outputAt],
342 cdbuffer, CDIO_CD_FRAMESIZE_RAW);
343 }
344 else
345 {
346 driver_return_code_t c = cdio_read_audio_sector(
347 m_cdio, &m_outputBuf[m_outputAt], m_curPos);
348 if (DRIVER_OP_SUCCESS != c)
349 {
350 LOG(VB_MEDIA, LOG_DEBUG,
351 QString("cdio_read_audio_sector(%1) error %2").
352 arg(m_curPos).arg(c));
353 memset( &m_outputBuf[m_outputAt],
354 0, CDIO_CD_FRAMESIZE_RAW);
355
356 // stop if we got an error
357 m_userStop = true;
358 }
359 }
360
361 m_outputAt += CDIO_CD_FRAMESIZE_RAW;
362 ++(m_curPos);
363 }
364 else
365 {
366 m_finish = true;
367 }
368 }
369 }
370
371 if (!output())
372 continue;
373
374 // Wait until we need to decode or supply more samples
375 uint fill = 0;
376 uint total = 0;
377 while (!m_finish && !m_userStop && m_seekTime <= +0.)
378 {
379 output()->GetBufferStatus(fill, total);
380 // Make sure we have decoded samples ready and that the
381 // audiobuffer is reasonably populated
382 if (fill < (thresh << 6))
383 break;
384 // Wait for half of the buffer to drain
385 ::usleep(output()->GetAudioBufferedTime().count()<<9);
386 }
387
388 // write a block if there's sufficient space for it
389 if (!m_userStop &&
390 m_outputAt >= m_bks &&
391 fill <= total - thresh)
392 {
393 writeBlock();
394 }
395 }
396
397 if (m_userStop)
398 m_inited = false;
399 else if (output())
400 {
401 // Drain our buffer
402 while (m_outputAt >= m_bks)
403 writeBlock();
404
405 // Drain ao buffer
406 output()->Drain();
407 }
408
409 if (m_finish)
411 else if (m_userStop)
413 else
415
416 // NB block scope required to step onto next track
417 {
419 dispatch(e);
420 }
421
422 deinit();
423
424 RunEpilog();
425}
426
427//public
429{
430 QMutexLocker lock(&getCdioMutex());
431
433 if (cdio)
434 {
435 driver_return_code_t c = cdio_set_speed(cdio, speed >= 0 ? speed : 1);
436 if (DRIVER_OP_SUCCESS != c)
437 {
438 LOG(VB_MEDIA, LOG_INFO,
439 QString("Error: cdio_set_speed('%1',%2) failed").
440 arg(m_deviceName).arg(speed));
441 }
442 }
443}
444
445//public
447{
448 QMutexLocker lock(&getCdioMutex());
449
451 if (!cdio)
452 return 0;
453
454 track_t tracks = cdio_get_num_tracks(cdio);
455 if (CDIO_INVALID_TRACK != tracks)
456 LOG(VB_MEDIA, LOG_DEBUG, QString("getNumTracks = %1").arg(tracks));
457 else
458 tracks = -1;
459
460 return tracks;
461}
462
463//public
465{
466 QMutexLocker lock(&getCdioMutex());
467
469 if (!cdio)
470 return 0;
471
472 int nAudio = 0;
473 const track_t last = cdio_get_last_track_num(cdio);
474 if (CDIO_INVALID_TRACK != last)
475 {
476 for (track_t t = cdio_get_first_track_num(cdio) ; t <= last; ++t)
477 {
478 if (TRACK_FORMAT_AUDIO == cdio_get_track_format(cdio, t))
479 ++nAudio;
480 }
481 LOG(VB_MEDIA, LOG_DEBUG, QString("getNumCDAudioTracks = %1").arg(nAudio));
482 }
483
484 return nAudio;
485}
486
487//public
489{
490 m_setTrackNum = track;
491 return getMetadata();
492}
493
494
495// Create a TOC
496static lsn_t s_lastAudioLsn;
497
498//virtual
500{
501 QString artist;
502 QString album;
503 QString compilation_artist;
504 QString title;
505 QString genre;
506 int year = 0;
507 std::chrono::milliseconds length = 0s;
508 track_t tracknum = 0;
509
510 if (-1 == m_setTrackNum)
511 tracknum = getURL().toUInt();
512 else
513 {
514 tracknum = m_setTrackNum;
515 setURL(QString("%1%2").arg(tracknum).arg(CDEXT));
516 }
517
518 QMutexLocker lock(&getCdioMutex());
519
521 if (!cdio)
522 return nullptr;
523
524 const track_t lastTrack = cdio_get_last_track_num(cdio);
525 if (CDIO_INVALID_TRACK == lastTrack)
526 return nullptr;
527
528 if (TRACK_FORMAT_AUDIO != cdio_get_track_format(cdio, tracknum))
529 return nullptr;
530
531 // Assume disc changed if max LSN different
532 bool isDiscChanged = false;
533 static lsn_t s_totalSectors;
534 lsn_t totalSectors = cdio_get_track_lsn(cdio, CDIO_CDROM_LEADOUT_TRACK);
535 if (s_totalSectors != totalSectors)
536 {
537 s_totalSectors = totalSectors;
538 isDiscChanged = true;
539 }
540
541 // NB cdio_get_track_last_lsn is unreliable for the last audio track
542 // of discs with data tracks beyond
543 lsn_t end = cdio_get_track_last_lsn(cdio, tracknum);
544 if (isDiscChanged)
545 {
546 const track_t audioTracks = getNumCDAudioTracks();
547 s_lastAudioLsn = cdio_get_track_last_lsn(cdio, audioTracks);
548
549 if (audioTracks < lastTrack)
550 {
551 cdrom_drive_t *dev = cdio_cddap_identify_cdio(cdio, 0, nullptr);
552 if (nullptr != dev)
553 {
554 if (DRIVER_OP_SUCCESS == cdio_cddap_open(dev))
555 {
556 // NB this can be S L O W but is reliable
557 lsn_t end2 = cdio_cddap_track_lastsector(dev,
559 if (CDIO_INVALID_LSN != end2)
560 s_lastAudioLsn = end2;
561 }
562 cdio_cddap_close_no_free_cdio(dev);
563 }
564 }
565 }
566
567 if (s_lastAudioLsn && s_lastAudioLsn < end)
568 end = s_lastAudioLsn;
569
570 const lsn_t start = cdio_get_track_lsn(cdio, tracknum);
571 if (CDIO_INVALID_LSN != start && CDIO_INVALID_LSN != end)
572 {
573 length = std::chrono::milliseconds(((end - start + 1) * 1000 + CDIO_CD_FRAMES_PER_SEC/2) /
574 CDIO_CD_FRAMES_PER_SEC);
575 }
576
577 bool isCompilation = false;
578
579// Disabled - cd-text access on discs without it is S L O W
580#define CDTEXT 0 // NOLINT(cppcoreguidelines-macro-usage)
581#if CDTEXT
582 static int s_iCdtext;
583 if (isDiscChanged)
584 s_iCdtext = -1;
585
586 if (s_iCdtext)
587 {
588 // cdio_get_cdtext can't take >5 seconds on some CD's without cdtext
589 if (s_iCdtext < 0)
590 LOG(VB_MEDIA, LOG_INFO,
591 QString("Getting cdtext for track %1...").arg(tracknum));
592 cdtext_t * cdtext = cdio_get_cdtext(m_cdio, tracknum);
593 if (nullptr != cdtext)
594 {
595 genre = cdtext_get_const(CDTEXT_GENRE, cdtext);
596 artist = cdtext_get_const(CDTEXT_PERFORMER, cdtext);
597 title = cdtext_get_const(CDTEXT_TITLE, cdtext);
598 const char* isrc = cdtext_get_const(CDTEXT_ISRC, cdtext);
599 /* ISRC codes are 12 characters long, in the form CCXXXYYNNNNN
600 * CC = country code
601 * XXX = registrant e.g. BMG
602 * CC = year withou century
603 * NNNNN = unique ID
604 */
605 if (isrc && strlen(isrc) >= 7)
606 {
607 year = (isrc[5] - '0') * 10 + (isrc[6] - '0');
608 year += (year <= 30) ? 2000 : 1900;
609 }
610
611 cdtext_destroy(cdtext);
612
613 if (!title.isNull())
614 {
615 if (s_iCdtext < 0)
616 LOG(VB_MEDIA, LOG_INFO, "Found cdtext track title");
617 s_iCdtext = 1;
618
619 // Get disc info
620 cdtext = cdio_get_cdtext(cdio, 0);
621 if (nullptr != cdtext)
622 {
623 compilation_artist = cdtext_get_const(
624 CDTEXT_PERFORMER, cdtext);
625 if (!compilation_artist.isEmpty() &&
626 artist != compilation_artist)
627 isCompilation = true;
628
629 album = cdtext_get_const(CDTEXT_TITLE, cdtext);
630
631 if (genre.isNull())
632 genre = cdtext_get_const(CDTEXT_GENRE, cdtext);
633
634 cdtext_destroy(cdtext);
635 }
636 }
637 else
638 {
639 if (s_iCdtext < 0)
640 LOG(VB_MEDIA, LOG_INFO, "No cdtext title for track");
641 s_iCdtext = 0;
642 }
643 }
644 else
645 {
646 if (s_iCdtext < 0)
647 LOG(VB_MEDIA, LOG_INFO, "No cdtext");
648 s_iCdtext = 0;
649 }
650 }
651
652 if (title.isEmpty() || artist.isEmpty() || album.isEmpty())
653#endif // CDTEXT
654 {
655#ifdef HAVE_MUSICBRAINZ
656 if (isDiscChanged)
657 {
658 // lazy load whole CD metadata
659 getMusicBrainz().queryForDevice(m_deviceName);
660 }
661 if (getMusicBrainz().hasMetadata(m_setTrackNum))
662 {
663 auto *metadata = getMusicBrainz().getMetadata(m_setTrackNum);
664 if (metadata)
665 {
666 metadata->setFilename(getURL());
667 return metadata;
668 }
669 }
670#endif // HAVE_MUSICBRAINZ
671 }
672
673 if (compilation_artist.toLower().left(7) == "various")
674 compilation_artist = tr("Various Artists");
675
676 if (artist.isEmpty())
677 {
678 artist = compilation_artist;
679 compilation_artist.clear();
680 }
681
682 if (title.isEmpty())
683 title = tr("Track %1").arg(tracknum);
684
685 auto *m = new MusicMetadata(getURL(), artist, compilation_artist, album,
686 title, genre, year, tracknum, length);
687 if (m)
688 m->setCompilation(isCompilation);
689
690 return m;
691}
692
693#ifdef HAVE_MUSICBRAINZ
694
695MusicBrainz & CdDecoder::getMusicBrainz()
696{
697 static MusicBrainz s_musicBrainz;
698 return s_musicBrainz;
699}
700
701#endif // HAVE_MUSICBRAINZ
702
703
704// pure virtual
705bool CdDecoderFactory::supports(const QString &source) const
706{
707 return (source.right(extension().length()).toLower() == extension());
708}
709
710// pure virtual
711const QString &CdDecoderFactory::extension() const
712{
713 static QString s_ext(CDEXT);
714 return s_ext;
715}
716
717// pure virtual
718const QString &CdDecoderFactory::description() const
719{
720 static QString s_desc(tr("Audio CD parser"));
721 return s_desc;
722}
723
724// pure virtual
725Decoder *CdDecoderFactory::create(const QString &file, AudioOutput *output, bool deletable)
726{
727 if (deletable)
728 return new CdDecoder(file, this, output);
729
730 static CdDecoder *s_decoder;
731 if (! s_decoder)
732 {
733 s_decoder = new CdDecoder(file, this, output);
734 }
735 else
736 {
737 s_decoder->setURL(file);
738 s_decoder->setOutput(output);
739 }
740
741 return s_decoder;
742}
743
@ FORMAT_S16
static lsn_t s_lastAudioLsn
Definition: cddecoder.cpp:496
static CdIo_t * openCdio(const QString &name)
Definition: cddecoder.cpp:54
static constexpr long kSamplesPerSec
Definition: cddecoder.cpp:31
static constexpr const char * CDEXT
Definition: cddecoder.cpp:30
static void logger(cdio_log_level_t level, const char *message)
Definition: cddecoder.cpp:34
virtual void Drain(void)=0
virtual void PauseUntilBuffered(void)=0
virtual void Reconfigure(const AudioSettings &settings)=0
virtual void GetBufferStatus(uint &fill, uint &total)
Definition: audiooutput.h:149
virtual void SetSourceBitrate(int)
Definition: audiooutput.h:144
const QString & description() const override
Definition: cddecoder.cpp:718
bool supports(const QString &source) const override
Definition: cddecoder.cpp:705
Decoder * create(const QString &file, AudioOutput *output, bool deletable) override
Definition: cddecoder.cpp:725
const QString & extension() const override
Definition: cddecoder.cpp:711
char * m_outputBuf
Definition: cddecoder.h:64
int m_chan
Definition: cddecoder.h:73
int m_setTrackNum
Definition: cddecoder.h:76
volatile bool m_userStop
Definition: cddecoder.h:57
void setDevice(const QString &dev)
Definition: cddecoder.cpp:104
CdDecoder(const QString &file, DecoderFactory *d, AudioOutput *o)
Definition: cddecoder.cpp:91
void setCDSpeed(int speed)
Definition: cddecoder.cpp:428
bool initialize() override
Definition: cddecoder.cpp:146
long m_freq
Definition: cddecoder.h:71
void stop() override
Definition: cddecoder.cpp:115
void seek(double pos) override
Definition: cddecoder.cpp:260
long m_bitrate
Definition: cddecoder.h:72
std::size_t m_bksFrames
Definition: cddecoder.h:68
std::size_t m_bks
Definition: cddecoder.h:67
MusicMetadata * getMetadata(void)
Definition: cddecoder.cpp:499
QString m_deviceName
Definition: cddecoder.h:59
~CdDecoder() override
Definition: cddecoder.cpp:98
bool m_finish
Definition: cddecoder.h:70
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: cddecoder.cpp:292
std::size_t m_decodeBytes
Definition: cddecoder.h:69
void deinit()
Definition: cddecoder.cpp:268
static QRecursiveMutex & getCdioMutex()
Definition: cddecoder.cpp:139
std::size_t m_outputAt
Definition: cddecoder.h:65
int getNumTracks()
Definition: cddecoder.cpp:446
volatile bool m_inited
Definition: cddecoder.h:56
int getNumCDAudioTracks()
Definition: cddecoder.cpp:464
void writeBlock()
Definition: cddecoder.cpp:121
double m_seekTime
Definition: cddecoder.h:74
DecoderEvent::Type m_stat
Definition: cddecoder.h:63
int m_trackNum
Definition: cddecoder.h:77
static const Type kStopped
Definition: decoder.h:46
static const Type kDecoding
Definition: decoder.h:45
static const Type kFinished
Definition: decoder.h:47
static const Type kError
Definition: decoder.h:48
virtual void lock(void)
Definition: decoder.h:85
QString getURL(void) const
Definition: decoder.h:91
void setURL(const QString &url)
Definition: decoder.h:83
AudioOutput * output()
Definition: decoder.h:81
void setOutput(AudioOutput *o)
Definition: decoder.cpp:44
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
CdIo_t * m_cdio
Definition: cddecoder.cpp:76
StCdioDevice & operator=(const StCdioDevice &)
StCdioDevice(const StCdioDevice &)
StCdioDevice(const QString &dev)
Definition: cddecoder.cpp:84
unsigned int uint
Definition: freesurround.h:24
static const iso6937table * d
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static QMutex s_mtx
Definition: netstream.cpp:53
#define output