1#define DO_NOT_WANT_PARANOIA_COMPATIBILITY
11#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
12#include <QtSystemDetection>
21#include <cdio/logging.h>
29#include <libavcodec/avcodec.h>
34static constexpr const char*
CDEXT {
".cda" };
38static void logger(cdio_log_level_t level,
const char *message)
45 LOG(VB_MEDIA, LOG_DEBUG, QString(
"INFO cdio: %1").arg(message));
48 LOG(VB_MEDIA, LOG_DEBUG, QString(
"WARN cdio: %1").arg(message));
52 LOG(VB_GENERAL, LOG_ERR, QString(
"ERROR cdio: %1").arg(message));
65 cdio_log_set_handler(&
logger);
68 CdIo_t *cdio = cdio_open(name.toLatin1().constData(), DRIVER_DEVICE);
71 LOG(VB_MEDIA, LOG_INFO, QString(
"CdDecoder: cdio_open(%1) failed").
82 void*
operator new(std::size_t);
91 operator CdIo_t*()
const {
return m_cdio; }
138 std::this_thread::sleep_for(
output()->GetAudioBufferedTime() / 2);
145 static QRecursiveMutex
s_mtx;
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)
180 cdio_destroy(m_cdio), m_cdio =
nullptr;
184 LOG(VB_MEDIA, LOG_DEBUG, QString(
"CdDecoder track=%1 lsn start=%2 end=%3")
188 m_device = cdio_cddap_identify_cdio(m_cdio, 0,
nullptr);
189 if (
nullptr == m_device)
191 LOG(VB_GENERAL, LOG_ERR,
192 QString(
"Error: CdDecoder: cdio_cddap_identify(%1) failed")
194 cdio_destroy(m_cdio), m_cdio =
nullptr;
198 cdio_cddap_verbose_set(m_device,
200 CDDA_MESSAGE_FORGETIT,
202 CDDA_MESSAGE_FORGETIT);
204 if (DRIVER_OP_SUCCESS == cdio_cddap_open(m_device))
207 lsn_t end2 = cdio_cddap_track_lastsector(m_device,
m_trackNum);
210 LOG(VB_MEDIA, LOG_INFO, QString(
"CdDecoder: trim last lsn from %1 to %2")
211 .arg(m_end).arg(end2));
219 if (
nullptr != m_paranoia)
221 cdio_paranoia_modeset(m_paranoia, PARANOIA_MODE_DISABLE);
222 (void)cdio_paranoia_seek(m_paranoia, m_start, SEEK_SET);
226 LOG(VB_GENERAL, LOG_WARNING,
"CD reading with paranoia is disabled");
231 LOG(VB_GENERAL, LOG_ERR,
232 QString(
"Warn: drive '%1' is not cdda capable").
236 int chnls = cdio_get_track_channels(m_cdio,
m_trackNum);
237 m_chan = chnls > 0 ? chnls : 2;
243 AV_CODEC_ID_PCM_S16LE,
m_freq,
false );
281 cdio_paranoia_free(m_paranoia), m_paranoia =
nullptr;
283 cdio_cddap_close(m_device), m_device =
nullptr, m_cdio =
nullptr;
285 cdio_destroy(m_cdio), m_cdio =
nullptr;
318 const std::size_t thresh =
m_bks * 6;
324 m_curPos = m_start +
static_cast< lsn_t
>(
329 cdio_paranoia_seek(m_paranoia, m_curPos, SEEK_SET);
341 if (m_curPos < m_end)
346 int16_t *cdbuffer = cdio_paranoia_read_limited(
347 m_paranoia,
nullptr, 10);
350 cdbuffer, CDIO_CD_FRAMESIZE_RAW);
354 driver_return_code_t c = cdio_read_audio_sector(
356 if (DRIVER_OP_SUCCESS != c)
358 LOG(VB_MEDIA, LOG_DEBUG,
359 QString(
"cdio_read_audio_sector(%1) error %2").
360 arg(m_curPos).arg(c));
362 0, CDIO_CD_FRAMESIZE_RAW);
390 if (fill < (thresh << 6))
393 std::this_thread::sleep_for(
output()->GetAudioBufferedTime() / 2);
399 fill <= total - thresh)
445 driver_return_code_t c = cdio_set_speed(cdio, speed >= 0 ? speed : 1);
446 if (DRIVER_OP_SUCCESS != c)
448 LOG(VB_MEDIA, LOG_INFO,
449 QString(
"Error: cdio_set_speed('%1',%2) failed").
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));
483 const track_t last = cdio_get_last_track_num(cdio);
484 if (CDIO_INVALID_TRACK != last)
486 for (track_t
t = cdio_get_first_track_num(cdio) ;
t <= last; ++
t)
488 if (TRACK_FORMAT_AUDIO == cdio_get_track_format(cdio,
t))
491 LOG(VB_MEDIA, LOG_DEBUG, QString(
"getNumCDAudioTracks = %1").arg(nAudio));
513 QString compilation_artist;
517 std::chrono::milliseconds length = 0s;
518 track_t tracknum = 0;
522 tracknum =
getURL().toUInt();
536 const track_t lastTrack = cdio_get_last_track_num(cdio);
537 if (CDIO_INVALID_TRACK == lastTrack)
540 if (TRACK_FORMAT_AUDIO != cdio_get_track_format(cdio, tracknum))
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)
549 s_totalSectors = totalSectors;
550 isDiscChanged =
true;
555 lsn_t end = cdio_get_track_last_lsn(cdio, tracknum);
561 if (audioTracks < lastTrack)
563 cdrom_drive_t *dev = cdio_cddap_identify_cdio(cdio, 0,
nullptr);
566 if (DRIVER_OP_SUCCESS == cdio_cddap_open(dev))
569 lsn_t end2 = cdio_cddap_track_lastsector(dev,
571 if (CDIO_INVALID_LSN != end2)
574 cdio_cddap_close_no_free_cdio(dev);
582 const lsn_t
start = cdio_get_track_lsn(cdio, tracknum);
583 if (CDIO_INVALID_LSN !=
start && CDIO_INVALID_LSN != end)
585 length = std::chrono::milliseconds((((end -
start + 1) * 1000) + CDIO_CD_FRAMES_PER_SEC/2) /
586 CDIO_CD_FRAMES_PER_SEC);
589 bool isCompilation =
false;
594 static int s_iCdtext;
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)
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);
617 if (isrc && strlen(isrc) >= 7)
619 year = (isrc[5] -
'0') * 10 + (isrc[6] -
'0');
620 year += (year <= 30) ? 2000 : 1900;
623 cdtext_destroy(cdtext);
628 LOG(VB_MEDIA, LOG_INFO,
"Found cdtext track title");
632 cdtext = cdio_get_cdtext(cdio, 0);
633 if (
nullptr != cdtext)
635 compilation_artist = cdtext_get_const(
636 CDTEXT_PERFORMER, cdtext);
637 if (!compilation_artist.isEmpty() &&
638 artist != compilation_artist)
639 isCompilation =
true;
641 album = cdtext_get_const(CDTEXT_TITLE, cdtext);
644 genre = cdtext_get_const(CDTEXT_GENRE, cdtext);
646 cdtext_destroy(cdtext);
652 LOG(VB_MEDIA, LOG_INFO,
"No cdtext title for track");
659 LOG(VB_MEDIA, LOG_INFO,
"No cdtext");
664 if (title.isEmpty() || artist.isEmpty() || album.isEmpty())
667#ifdef HAVE_MUSICBRAINZ
675 auto *metadata = getMusicBrainz().getMetadata(
m_setTrackNum);
678 metadata->setFilename(
getURL());
685 if (compilation_artist.toLower().left(7) ==
"various")
686 compilation_artist = tr(
"Various Artists");
688 if (artist.isEmpty())
690 artist = compilation_artist;
691 compilation_artist.clear();
695 title = tr(
"Track %1").arg(tracknum);
698 title, genre, year, tracknum, length);
700 m->setCompilation(isCompilation);
705#ifdef HAVE_MUSICBRAINZ
710 return s_musicBrainz;
725 static QString s_ext(
CDEXT);
732 static QString s_desc(tr(
"Audio CD parser"));
static lsn_t s_lastAudioLsn
static CdIo_t * openCdio(const QString &name)
static constexpr long kSamplesPerSec
static constexpr const char * CDEXT
static void logger(cdio_log_level_t level, const char *message)
virtual void Drain(void)=0
virtual void PauseUntilBuffered(void)=0
virtual void Reconfigure(const AudioSettings &settings)=0
virtual void GetBufferStatus(uint &fill, uint &total)
virtual void SetSourceBitrate(int)
const QString & description() const override
bool supports(const QString &source) const override
Decoder * create(const QString &file, AudioOutput *output, bool deletable) override
const QString & extension() const override
void setDevice(const QString &dev)
CdDecoder(const QString &file, DecoderFactory *d, AudioOutput *o)
void setCDSpeed(int speed)
bool initialize() override
void seek(double pos) override
MusicMetadata * getMetadata(void)
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
std::size_t m_decodeBytes
static QRecursiveMutex & getCdioMutex()
int getNumCDAudioTracks()
DecoderEvent::Type m_stat
static const Type kStopped
static const Type kDecoding
static const Type kFinished
QString getURL(void) const
void setURL(const QString &url)
void setOutput(AudioOutput *o)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
StCdioDevice & operator=(const StCdioDevice &)
StCdioDevice(const StCdioDevice &)
StCdioDevice(const QString &dev)
static const iso6937table * d
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)