30#include <QRegularExpression>
53 #include <libavformat/avio.h>
54 #include <libavutil/opt.h>
105 if (!ch.isNull() && ch ==
'%')
109 else if (!ch.isNull() && (ch ==
'r' || ch ==
'a' || ch ==
'b' || ch ==
't'))
124 LOG(VB_GENERAL, LOG_ERR,
125 QString(
"ShoutCastMetaParser: malformed metaformat '%1'")
142 int title_begin_pos = mdata.indexOf(
"StreamTitle='");
144 if (title_begin_pos >= 0)
146 title_begin_pos += 13;
147 int title_end_pos = mdata.indexOf(
"';", title_begin_pos);
148 QString title = mdata.mid(title_begin_pos, title_end_pos - title_begin_pos);
150 auto match = rx.match(title);
151 if (match.hasMatch())
153 LOG(VB_PLAYBACK, LOG_DEBUG, QString(
"ShoutCast: Meta : '%1'")
155 LOG(VB_PLAYBACK, LOG_DEBUG,
156 QString(
"ShoutCast: Parsed as: '%1' by '%2' on '%3'")
175static void myth_av_log(
void *ptr,
int level,
const char* fmt, va_list vl)
180 static QString s_fullLine(
"");
181 static QMutex s_stringLock;
182 uint64_t verbose_mask = VB_GENERAL;
183 LogLevel_t verbose_level = LOG_DEBUG;
189 verbose_level = LOG_EMERG;
192 verbose_level = LOG_CRIT;
195 verbose_level = LOG_ERR;
196 verbose_mask |= VB_LIBAV;
201 verbose_level = LOG_DEBUG;
202 verbose_mask |= VB_LIBAV;
205 verbose_mask |= VB_LIBAV;
215 if (s_fullLine.isEmpty() && ptr) {
216 AVClass* avc = *(AVClass**)ptr;
217 s_fullLine = QString(
"[%1 @ %2] ")
218 .arg(avc->item_name(ptr))
219 .arg(
reinterpret_cast<size_t>(avc),QT_POINTER_SIZE,8,QChar(
'0'));
222 s_fullLine += QString::vasprintf(fmt, vl);
223 if (s_fullLine.endsWith(
"\n"))
225 LOG(verbose_mask, verbose_level, s_fullLine.trimmed());
226 s_fullLine.truncate(0);
228 s_stringLock.unlock();
233 m_outputBuffer((uint8_t *)av_malloc(
AudioOutput::kMaxSizeBuffer))
239 av_log_set_level(
debug ? AV_LOG_DEBUG : AV_LOG_ERROR);
271 error(
"avfDecoder: initialise called with a NULL audiooutput");
277 error(
"avfDecoder: couldn't allocate memory");
288 error(QString(
"Could not open url (%1)").arg(
m_url));
294 if (
getURL().startsWith(
"http://") ||
getURL().startsWith(
"mmsh://"))
303 if (
getURL().startsWith(
"mmsh://"))
305 AVDictionaryEntry *tag =
nullptr;
326 error(
"Could not determine the stream format.");
333 const AVCodec *codec =
nullptr;
339 error(QString(
"Could not find audio stream."));
351 if (avcodec_open2(
m_audioDec, codec,
nullptr) < 0)
353 error(QString(
"Could not open audio codec: %1")
364 error(QString(
"AVCodecContext tells us %1 channels are "
365 "available, this is bad, bailing.")
376 error(QString(
"Error: Unsupported sample format: %1")
377 .arg(av_get_sample_fmt_name(
m_audioDec->sample_fmt)));
432 AVPacket *pkt = av_packet_alloc();
433 AVPacket *tmp_pkt = av_packet_alloc();
434 if ((pkt ==
nullptr) || (tmp_pkt ==
nullptr))
436 LOG(VB_GENERAL, LOG_ERR,
"packet allocation failed");
453 LOG(VB_GENERAL, LOG_INFO, QString(
"avfdecoder.o: seek time %1")
458 LOG(VB_GENERAL, LOG_ERR,
"Error seeking");
472 if (res != AVERROR_EOF)
474 LOG(VB_GENERAL, LOG_ERR, QString(
"Read frame failed: %1").arg(res));
475 LOG(VB_FILE, LOG_ERR, (
"... for file '" +
m_url) +
"'");
482 av_packet_ref(tmp_pkt, pkt);
484 while (tmp_pkt->size > 0 && !
m_finish &&
498 tmp_pkt->size -= ret;
499 tmp_pkt->data += ret;
507 av_packet_unref(pkt);
516 std::this_thread::sleep_for(buffered - 1s);
541 av_packet_free(&pkt);
542 av_packet_free(&tmp_pkt);
549 uint8_t *pdata =
nullptr;
553 QString shout = QString::fromUtf8((
const char*) pdata);
562 QString parsed = meta_map[
"title"] +
"\\" + meta_map[
"artist"] +
"\\" + meta_map[
"album"];
567 LOG(VB_PLAYBACK, LOG_INFO, QString(
"avfDecoder: shoutcast metadata changed - %1").arg(shout));
568 LOG(VB_PLAYBACK, LOG_INFO, QString(
"avfDecoder: new metadata (%1)").arg(parsed));
595 QStringList list =
extension().split(
"|", Qt::SkipEmptyParts);
596 return std::ranges::any_of(std::as_const(list),
597 [source](
const auto& str)
598 {
return str == source.right(str.length()).toLower(); } );
608 static QString s_desc(tr(
"Internal Decoder"));
static void myth_av_log(void *ptr, int level, const char *fmt, va_list vl)
QMap< QString, QString > ShoutCastMetaMap
static AudioFormat AVSampleFormatToFormat(AVSampleFormat format, int bits=0)
Return AVSampleFormat closest equivalent to AudioFormat.
virtual bool AddData(void *buffer, int len, std::chrono::milliseconds timecode, int frames)=0
Add data to the audiobuffer for playback.
virtual void Drain(void)=0
virtual std::chrono::milliseconds GetAudioBufferedTime(void)
report amount of audio buffered in milliseconds.
virtual void PauseUntilBuffered(void)=0
int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, const AVPacket *pkt)
Utility routine.
virtual void Reconfigure(const AudioSettings &settings)=0
virtual void SetSourceBitrate(int)
static const Type kStopped
static const Type kDecoding
static const Type kFinished
Events sent by the DecoderHandler and it's helper classes.
static const Type kBufferStatus
MusicMetadata & getMetadata()
void error(const QString &e)
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 RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
void setObjectName(const QString &name)
DecoderHandler * getDecoderHandler(void)
AVCodecContext * GetCodecContext(const AVStream *Stream, const AVCodec *Codec=nullptr, bool NullCodec=false)
void FreeCodecContext(const AVStream *Stream)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
AVFormatContext * getContext(void)
bool supports(const QString &source) const override
Decoder * create(const QString &file, AudioOutput *output, bool deletable) override
const QString & description() const override
const QString & extension() const override
void checkMetatdata(void)
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
bool initialize() override
class RemoteAVFormatContext * m_inputContext
void seek(double pos) override
~avfDecoder(void) override
AVCodecContext * m_audioDec
QString m_lastMetadataParsed
const AVInputFormat * m_inputFormat
avfDecoder(const QString &file, DecoderFactory *d, AudioOutput *o)
static const iso6937table * d
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
static bool VERBOSE_LEVEL_NONE()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_GENERAL