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