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 <thread>
8
9// Qt
10#include <QtGlobal>
11#if QT_VERSION >= QT_VERSION_CHECK(6,5,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().constData(), 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 std::this_thread::sleep_for(output()->GetAudioBufferedTime() / 2);
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 = false;
156 m_userStop = false;
157 m_finish = false;
158 m_freq = m_bitrate = 0L;
160 m_chan = 0;
161 m_seekTime = -1.;
162
163 if (output())
165
166 m_trackNum = getURL().section('.', 0, 0).toUInt();
167
168 QMutexLocker lock(&getCdioMutex());
169
170 m_cdio = openCdio(m_deviceName);
171 if (!m_cdio)
172 return false;
173
174 m_start = cdio_get_track_lsn(m_cdio, m_trackNum);
175 m_end = cdio_get_track_last_lsn(m_cdio, m_trackNum);
176 if (CDIO_INVALID_LSN == m_start ||
177 CDIO_INVALID_LSN == m_end)
178 {
179 LOG(VB_MEDIA, LOG_INFO, "CdDecoder: No tracks on " + m_deviceName);
180 cdio_destroy(m_cdio), m_cdio = nullptr;
181 return false;
182 }
183
184 LOG(VB_MEDIA, LOG_DEBUG, QString("CdDecoder track=%1 lsn start=%2 end=%3")
185 .arg(m_trackNum).arg(m_start).arg(m_end));
186 m_curPos = m_start;
187
188 m_device = cdio_cddap_identify_cdio(m_cdio, 0, nullptr);
189 if (nullptr == m_device)
190 {
191 LOG(VB_GENERAL, LOG_ERR,
192 QString("Error: CdDecoder: cdio_cddap_identify(%1) failed")
193 .arg(m_deviceName));
194 cdio_destroy(m_cdio), m_cdio = nullptr;
195 return false;
196 }
197
198 cdio_cddap_verbose_set(m_device,
199 VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_ANY) ? CDDA_MESSAGE_PRINTIT :
200 CDDA_MESSAGE_FORGETIT,
201 VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_DEBUG) ? CDDA_MESSAGE_PRINTIT :
202 CDDA_MESSAGE_FORGETIT);
203
204 if (DRIVER_OP_SUCCESS == cdio_cddap_open(m_device))
205 {
206 // cdio_get_track_last_lsn is unreliable on discs with data at end
207 lsn_t end2 = cdio_cddap_track_lastsector(m_device, m_trackNum);
208 if (end2 < m_end)
209 {
210 LOG(VB_MEDIA, LOG_INFO, QString("CdDecoder: trim last lsn from %1 to %2")
211 .arg(m_end).arg(end2));
212 m_end = end2;
213 }
214
215 // FIXME can't use cdio_paranoia until we find a way to cleanly
216 // detect when the user has ejected a CD otherwise we enter a
217 // recursive loop in cdio_paranoia_read_limited()
218 //m_paranoia = cdio_paranoia_init(m_device);
219 if (nullptr != m_paranoia)
220 {
221 cdio_paranoia_modeset(m_paranoia, PARANOIA_MODE_DISABLE);
222 (void)cdio_paranoia_seek(m_paranoia, m_start, SEEK_SET);
223 }
224 else
225 {
226 LOG(VB_GENERAL, LOG_WARNING, "CD reading with paranoia is disabled");
227 }
228 }
229 else
230 {
231 LOG(VB_GENERAL, LOG_ERR,
232 QString("Warn: drive '%1' is not cdda capable").
233 arg(m_deviceName));
234 }
235
236 int chnls = cdio_get_track_channels(m_cdio, m_trackNum);
237 m_chan = chnls > 0 ? chnls : 2;
239
240 if (output())
241 {
242 const AudioSettings settings(FORMAT_S16, m_chan,
243 AV_CODEC_ID_PCM_S16LE, m_freq, false /* AC3/DTS passthru */);
244 output()->Reconfigure(settings);
246 }
247
248 // 20ms worth
249 m_bks = (m_freq * m_chan * 2) / 50;
250 m_bksFrames = m_freq / 50;
251 // decode 8 bks worth of samples each time we need more
252 m_decodeBytes = m_bks << 3;
253
254 m_outputBuf = reinterpret_cast< char* >(
255 ::av_malloc(m_decodeBytes + CDIO_CD_FRAMESIZE_RAW * 2));
256 m_outputAt = 0;
257
258 setCDSpeed(2);
259
260 m_inited = true;
261
262 return m_inited;
263}
264
265// pure virtual
266void CdDecoder::seek(double pos)
267{
268 m_seekTime = pos;
269 if (output())
271}
272
273// private
275{
276 setCDSpeed(-1);
277
278 QMutexLocker lock(&getCdioMutex());
279
280 if (m_paranoia)
281 cdio_paranoia_free(m_paranoia), m_paranoia = nullptr;
282 if (m_device)
283 cdio_cddap_close(m_device), m_device = nullptr, m_cdio = nullptr;
284 if (m_cdio)
285 cdio_destroy(m_cdio), m_cdio = nullptr;
286
287 if (m_outputBuf)
288 ::av_free(m_outputBuf), m_outputBuf = nullptr;
289
290 m_inited = false;
291 m_userStop = false;
292 m_finish = false;
293 m_freq = m_bitrate = 0L;
295 m_chan = 0;
296 setOutput(nullptr);
297}
298
299// private virtual
301{
302 RunProlog();
303
304 if (!m_inited)
305 {
306 RunEpilog();
307 return;
308 }
309
311 // NB block scope required to prevent re-entrancy
312 {
314 dispatch(e);
315 }
316
317 // account for possible frame expansion in aobase (upmix, float conv)
318 const std::size_t thresh = m_bks * 6;
319
320 while (!m_finish && !m_userStop)
321 {
322 if (m_seekTime >= +0.)
323 {
324 m_curPos = m_start + static_cast< lsn_t >(
325 (m_seekTime * kSamplesPerSec) / static_cast<int>(CD_FRAMESAMPLES));
326 if (m_paranoia)
327 {
328 QMutexLocker lock(&getCdioMutex());
329 cdio_paranoia_seek(m_paranoia, m_curPos, SEEK_SET);
330 }
331
332 m_outputAt = 0;
333 m_seekTime = -1.;
334 }
335
336 if (m_outputAt < m_bks)
337 {
338 while (m_outputAt < m_decodeBytes &&
339 !m_finish && !m_userStop && m_seekTime <= +0.)
340 {
341 if (m_curPos < m_end)
342 {
343 QMutexLocker lock(&getCdioMutex());
344 if (m_paranoia)
345 {
346 int16_t *cdbuffer = cdio_paranoia_read_limited(
347 m_paranoia, nullptr, 10);
348 if (cdbuffer)
349 memcpy(&m_outputBuf[m_outputAt],
350 cdbuffer, CDIO_CD_FRAMESIZE_RAW);
351 }
352 else
353 {
354 driver_return_code_t c = cdio_read_audio_sector(
355 m_cdio, &m_outputBuf[m_outputAt], m_curPos);
356 if (DRIVER_OP_SUCCESS != c)
357 {
358 LOG(VB_MEDIA, LOG_DEBUG,
359 QString("cdio_read_audio_sector(%1) error %2").
360 arg(m_curPos).arg(c));
361 memset( &m_outputBuf[m_outputAt],
362 0, CDIO_CD_FRAMESIZE_RAW);
363
364 // stop if we got an error
365 m_userStop = true;
366 }
367 }
368
369 m_outputAt += CDIO_CD_FRAMESIZE_RAW;
370 ++(m_curPos);
371 }
372 else
373 {
374 m_finish = true;
375 }
376 }
377 }
378
379 if (!output())
380 continue;
381
382 // Wait until we need to decode or supply more samples
383 uint fill = 0;
384 uint total = 0;
385 while (!m_finish && !m_userStop && m_seekTime <= +0.)
386 {
387 output()->GetBufferStatus(fill, total);
388 // Make sure we have decoded samples ready and that the
389 // audiobuffer is reasonably populated
390 if (fill < (thresh << 6))
391 break;
392 // Wait for half of the buffer to drain
393 std::this_thread::sleep_for(output()->GetAudioBufferedTime() / 2);
394 }
395
396 // write a block if there's sufficient space for it
397 if (!m_userStop &&
398 m_outputAt >= m_bks &&
399 fill <= total - thresh)
400 {
401 writeBlock();
402 }
403 }
404
405 if (m_userStop)
406 {
407 m_inited = false;
408 }
409 else if (output())
410 {
411 // Drain our buffer
412 while (m_outputAt >= m_bks)
413 writeBlock();
414
415 // Drain ao buffer
416 output()->Drain();
417 }
418
419 if (m_finish)
421 else if (m_userStop)
423 else
425
426 // NB block scope required to step onto next track
427 {
429 dispatch(e);
430 }
431
432 deinit();
433
434 RunEpilog();
435}
436
437//public
439{
440 QMutexLocker lock(&getCdioMutex());
441
443 if (cdio)
444 {
445 driver_return_code_t c = cdio_set_speed(cdio, speed >= 0 ? speed : 1);
446 if (DRIVER_OP_SUCCESS != c)
447 {
448 LOG(VB_MEDIA, LOG_INFO,
449 QString("Error: cdio_set_speed('%1',%2) failed").
450 arg(m_deviceName).arg(speed));
451 }
452 }
453}
454
455//public
457{
458 QMutexLocker lock(&getCdioMutex());
459
461 if (!cdio)
462 return 0;
463
464 track_t tracks = cdio_get_num_tracks(cdio);
465 if (CDIO_INVALID_TRACK != tracks)
466 LOG(VB_MEDIA, LOG_DEBUG, QString("getNumTracks = %1").arg(tracks));
467 else
468 tracks = -1;
469
470 return tracks;
471}
472
473//public
475{
476 QMutexLocker lock(&getCdioMutex());
477
479 if (!cdio)
480 return 0;
481
482 int nAudio = 0;
483 const track_t last = cdio_get_last_track_num(cdio);
484 if (CDIO_INVALID_TRACK != last)
485 {
486 for (track_t t = cdio_get_first_track_num(cdio) ; t <= last; ++t)
487 {
488 if (TRACK_FORMAT_AUDIO == cdio_get_track_format(cdio, t))
489 ++nAudio;
490 }
491 LOG(VB_MEDIA, LOG_DEBUG, QString("getNumCDAudioTracks = %1").arg(nAudio));
492 }
493
494 return nAudio;
495}
496
497//public
499{
500 m_setTrackNum = track;
501 return getMetadata();
502}
503
504
505// Create a TOC
506static lsn_t s_lastAudioLsn;
507
508//virtual
510{
511 QString artist;
512 QString album;
513 QString compilation_artist;
514 QString title;
515 QString genre;
516 int year = 0;
517 std::chrono::milliseconds length = 0s;
518 track_t tracknum = 0;
519
520 if (-1 == m_setTrackNum)
521 {
522 tracknum = getURL().toUInt();
523 }
524 else
525 {
526 tracknum = m_setTrackNum;
527 setURL(QString("%1%2").arg(tracknum).arg(CDEXT));
528 }
529
530 QMutexLocker lock(&getCdioMutex());
531
533 if (!cdio)
534 return nullptr;
535
536 const track_t lastTrack = cdio_get_last_track_num(cdio);
537 if (CDIO_INVALID_TRACK == lastTrack)
538 return nullptr;
539
540 if (TRACK_FORMAT_AUDIO != cdio_get_track_format(cdio, tracknum))
541 return nullptr;
542
543 // Assume disc changed if max LSN different
544 bool isDiscChanged = false;
545 static lsn_t s_totalSectors;
546 lsn_t totalSectors = cdio_get_track_lsn(cdio, CDIO_CDROM_LEADOUT_TRACK);
547 if (s_totalSectors != totalSectors)
548 {
549 s_totalSectors = totalSectors;
550 isDiscChanged = true;
551 }
552
553 // NB cdio_get_track_last_lsn is unreliable for the last audio track
554 // of discs with data tracks beyond
555 lsn_t end = cdio_get_track_last_lsn(cdio, tracknum);
556 if (isDiscChanged)
557 {
558 const track_t audioTracks = getNumCDAudioTracks();
559 s_lastAudioLsn = cdio_get_track_last_lsn(cdio, audioTracks);
560
561 if (audioTracks < lastTrack)
562 {
563 cdrom_drive_t *dev = cdio_cddap_identify_cdio(cdio, 0, nullptr);
564 if (nullptr != dev)
565 {
566 if (DRIVER_OP_SUCCESS == cdio_cddap_open(dev))
567 {
568 // NB this can be S L O W but is reliable
569 lsn_t end2 = cdio_cddap_track_lastsector(dev,
571 if (CDIO_INVALID_LSN != end2)
572 s_lastAudioLsn = end2;
573 }
574 cdio_cddap_close_no_free_cdio(dev);
575 }
576 }
577 }
578
579 if (s_lastAudioLsn && s_lastAudioLsn < end)
580 end = s_lastAudioLsn;
581
582 const lsn_t start = cdio_get_track_lsn(cdio, tracknum);
583 if (CDIO_INVALID_LSN != start && CDIO_INVALID_LSN != end)
584 {
585 length = std::chrono::milliseconds((((end - start + 1) * 1000) + CDIO_CD_FRAMES_PER_SEC/2) /
586 CDIO_CD_FRAMES_PER_SEC);
587 }
588
589 bool isCompilation = false;
590
591// Disabled - cd-text access on discs without it is S L O W
592#define CDTEXT 0 // NOLINT(cppcoreguidelines-macro-usage)
593#if CDTEXT
594 static int s_iCdtext;
595 if (isDiscChanged)
596 s_iCdtext = -1;
597
598 if (s_iCdtext)
599 {
600 // cdio_get_cdtext can't take >5 seconds on some CD's without cdtext
601 if (s_iCdtext < 0)
602 LOG(VB_MEDIA, LOG_INFO,
603 QString("Getting cdtext for track %1...").arg(tracknum));
604 cdtext_t * cdtext = cdio_get_cdtext(m_cdio, tracknum);
605 if (nullptr != cdtext)
606 {
607 genre = cdtext_get_const(CDTEXT_GENRE, cdtext);
608 artist = cdtext_get_const(CDTEXT_PERFORMER, cdtext);
609 title = cdtext_get_const(CDTEXT_TITLE, cdtext);
610 const char* isrc = cdtext_get_const(CDTEXT_ISRC, cdtext);
611 /* ISRC codes are 12 characters long, in the form CCXXXYYNNNNN
612 * CC = country code
613 * XXX = registrant e.g. BMG
614 * CC = year withou century
615 * NNNNN = unique ID
616 */
617 if (isrc && strlen(isrc) >= 7)
618 {
619 year = (isrc[5] - '0') * 10 + (isrc[6] - '0');
620 year += (year <= 30) ? 2000 : 1900;
621 }
622
623 cdtext_destroy(cdtext);
624
625 if (!title.isNull())
626 {
627 if (s_iCdtext < 0)
628 LOG(VB_MEDIA, LOG_INFO, "Found cdtext track title");
629 s_iCdtext = 1;
630
631 // Get disc info
632 cdtext = cdio_get_cdtext(cdio, 0);
633 if (nullptr != cdtext)
634 {
635 compilation_artist = cdtext_get_const(
636 CDTEXT_PERFORMER, cdtext);
637 if (!compilation_artist.isEmpty() &&
638 artist != compilation_artist)
639 isCompilation = true;
640
641 album = cdtext_get_const(CDTEXT_TITLE, cdtext);
642
643 if (genre.isNull())
644 genre = cdtext_get_const(CDTEXT_GENRE, cdtext);
645
646 cdtext_destroy(cdtext);
647 }
648 }
649 else
650 {
651 if (s_iCdtext < 0)
652 LOG(VB_MEDIA, LOG_INFO, "No cdtext title for track");
653 s_iCdtext = 0;
654 }
655 }
656 else
657 {
658 if (s_iCdtext < 0)
659 LOG(VB_MEDIA, LOG_INFO, "No cdtext");
660 s_iCdtext = 0;
661 }
662 }
663
664 if (title.isEmpty() || artist.isEmpty() || album.isEmpty())
665#endif // CDTEXT
666 {
667#ifdef HAVE_MUSICBRAINZ
668 if (isDiscChanged)
669 {
670 // lazy load whole CD metadata
671 getMusicBrainz().queryForDevice(m_deviceName);
672 }
673 if (getMusicBrainz().hasMetadata(m_setTrackNum))
674 {
675 auto *metadata = getMusicBrainz().getMetadata(m_setTrackNum);
676 if (metadata)
677 {
678 metadata->setFilename(getURL());
679 return metadata;
680 }
681 }
682#endif // HAVE_MUSICBRAINZ
683 }
684
685 if (compilation_artist.toLower().left(7) == "various")
686 compilation_artist = tr("Various Artists");
687
688 if (artist.isEmpty())
689 {
690 artist = compilation_artist;
691 compilation_artist.clear();
692 }
693
694 if (title.isEmpty())
695 title = tr("Track %1").arg(tracknum);
696
697 auto *m = new MusicMetadata(getURL(), artist, compilation_artist, album,
698 title, genre, year, tracknum, length);
699 if (m)
700 m->setCompilation(isCompilation);
701
702 return m;
703}
704
705#ifdef HAVE_MUSICBRAINZ
706
707MusicBrainz & CdDecoder::getMusicBrainz()
708{
709 static MusicBrainz s_musicBrainz;
710 return s_musicBrainz;
711}
712
713#endif // HAVE_MUSICBRAINZ
714
715
716// pure virtual
717bool CdDecoderFactory::supports(const QString &source) const
718{
719 return (source.right(extension().length()).toLower() == extension());
720}
721
722// pure virtual
723const QString &CdDecoderFactory::extension() const
724{
725 static QString s_ext(CDEXT);
726 return s_ext;
727}
728
729// pure virtual
730const QString &CdDecoderFactory::description() const
731{
732 static QString s_desc(tr("Audio CD parser"));
733 return s_desc;
734}
735
736// pure virtual
737Decoder *CdDecoderFactory::create(const QString &file, AudioOutput *output, bool deletable)
738{
739 if (deletable)
740 return new CdDecoder(file, this, output);
741
742 static CdDecoder *s_decoder;
743 if (! s_decoder)
744 {
745 s_decoder = new CdDecoder(file, this, output);
746 }
747 else
748 {
749 s_decoder->setURL(file);
750 s_decoder->setOutput(output);
751 }
752
753 return s_decoder;
754}
755
@ FORMAT_S16
static lsn_t s_lastAudioLsn
Definition: cddecoder.cpp:506
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:730
bool supports(const QString &source) const override
Definition: cddecoder.cpp:717
Decoder * create(const QString &file, AudioOutput *output, bool deletable) override
Definition: cddecoder.cpp:737
const QString & extension() const override
Definition: cddecoder.cpp:723
char * m_outputBuf
Definition: cddecoder.h:65
int m_chan
Definition: cddecoder.h:74
int m_setTrackNum
Definition: cddecoder.h:77
volatile bool m_userStop
Definition: cddecoder.h:58
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:438
bool initialize() override
Definition: cddecoder.cpp:150
long m_freq
Definition: cddecoder.h:72
void stop() override
Definition: cddecoder.cpp:119
void seek(double pos) override
Definition: cddecoder.cpp:266
long m_bitrate
Definition: cddecoder.h:73
std::size_t m_bksFrames
Definition: cddecoder.h:69
std::size_t m_bks
Definition: cddecoder.h:68
MusicMetadata * getMetadata(void)
Definition: cddecoder.cpp:509
QString m_deviceName
Definition: cddecoder.h:60
~CdDecoder() override
Definition: cddecoder.cpp:102
bool m_finish
Definition: cddecoder.h:71
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:300
std::size_t m_decodeBytes
Definition: cddecoder.h:70
void deinit()
Definition: cddecoder.cpp:274
static QRecursiveMutex & getCdioMutex()
Definition: cddecoder.cpp:143
std::size_t m_outputAt
Definition: cddecoder.h:66
int getNumTracks()
Definition: cddecoder.cpp:456
volatile bool m_inited
Definition: cddecoder.h:57
int getNumCDAudioTracks()
Definition: cddecoder.cpp:474
void writeBlock()
Definition: cddecoder.cpp:125
double m_seekTime
Definition: cddecoder.h:75
DecoderEvent::Type m_stat
Definition: cddecoder.h:64
int m_trackNum
Definition: cddecoder.h:78
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:43
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:180
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:193
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:60
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