Go to the documentation of this file.
31 #include <mythconfig.h>
52 #include "libavformat/avio.h"
53 #include "libavutil/opt.h"
56 using namespace std::chrono_literals;
68 void setMetaFormat(
const QString &metaformat);
73 int m_metaArtistPos {-1};
74 int m_metaTitlePos {-1};
75 int m_metaAlbumPos {-1};
87 m_metaFormat = metaformat;
96 pos = m_metaFormat.indexOf(
"%", pos);
103 if (pos < m_metaFormat.length())
104 ch = m_metaFormat.at(pos);
106 if (!ch.isNull() && ch ==
'%')
110 else if (!ch.isNull() && (ch ==
'r' || ch ==
'a' || ch ==
'b' || ch ==
't'))
113 m_metaArtistPos = assign_index;
116 m_metaAlbumPos = assign_index;
119 m_metaTitlePos = assign_index;
125 LOG(VB_GENERAL, LOG_ERR,
126 QString(
"ShoutCastMetaParser: malformed metaformat '%1'")
130 pos = m_metaFormat.indexOf(
"%", pos);
133 m_metaFormat.replace(
"%a",
"(.*)");
134 m_metaFormat.replace(
"%t",
"(.*)");
135 m_metaFormat.replace(
"%b",
"(.*)");
136 m_metaFormat.replace(
"%r",
"(.*)");
137 m_metaFormat.replace(
"%%",
"%");
143 int title_begin_pos = mdata.indexOf(
"StreamTitle='");
145 if (title_begin_pos >= 0)
147 title_begin_pos += 13;
148 int title_end_pos = mdata.indexOf(
"';", title_begin_pos);
149 QString
title = mdata.mid(title_begin_pos, title_end_pos - title_begin_pos);
151 rx.setPattern(m_metaFormat);
152 if (rx.indexIn(
title) != -1)
154 LOG(VB_PLAYBACK, LOG_INFO, QString(
"ShoutCast: Meta : '%1'")
156 LOG(VB_PLAYBACK, LOG_INFO,
157 QString(
"ShoutCast: Parsed as: '%1' by '%2'")
158 .
arg(rx.cap(m_metaTitlePos))
159 .arg(rx.cap(m_metaArtistPos)));
161 if (m_metaTitlePos > 0)
162 result[
"title"] = rx.cap(m_metaTitlePos);
164 if (m_metaArtistPos > 0)
165 result[
"artist"] = rx.cap(m_metaArtistPos);
167 if (m_metaAlbumPos > 0)
168 result[
"album"] = rx.cap(m_metaAlbumPos);
175 static 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::asprintf(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();
241 av_log_set_level((
debug) ? AV_LOG_DEBUG : AV_LOG_ERROR);
273 error(
"avfDecoder: initialise called with a NULL audiooutput");
279 error(
"avfDecoder: couldn't allocate memory");
296 if (
getURL().startsWith(
"http://") ||
getURL().startsWith(
"mmsh://"))
305 if (
getURL().startsWith(
"mmsh://"))
307 AVDictionaryEntry *tag =
nullptr;
328 error(
"Could not determine the stream format.");
335 AVCodec *codec =
nullptr;
341 error(QString(
"Could not find audio stream."));
353 if (avcodec_open2(
m_audioDec, codec,
nullptr) < 0)
355 error(QString(
"Could not open audio codec: %1")
366 error(QString(
"AVCodecContext tells us %1 channels are "
367 "available, this is bad, bailing.")
378 error(QString(
"Error: Unsupported sample format: %1")
436 memset(&pkt, 0,
sizeof(AVPacket));
437 av_init_packet(&pkt);
452 LOG(VB_GENERAL, LOG_INFO, QString(
"avfdecoder.o: seek time %1")
457 LOG(VB_GENERAL, LOG_ERR,
"Error seeking");
468 if (res != AVERROR_EOF)
470 LOG(VB_GENERAL, LOG_ERR, QString(
"Read frame failed: %1").
arg(res));
471 LOG(VB_FILE, LOG_ERR, (
"... for file '" +
m_url) +
"'");
478 av_init_packet(&tmp_pkt);
479 tmp_pkt.data = pkt.data;
480 tmp_pkt.size = pkt.size;
482 while (tmp_pkt.size > 0 && !
m_finish &&
505 av_packet_unref(&pkt);
515 const struct timespec ns {0, (buffered - 1000) * 1000000};
516 nanosleep(&ns,
nullptr);
547 uint8_t *pdata =
nullptr;
551 QString s = QString::fromUtf8((
const char*) pdata);
557 LOG(VB_PLAYBACK, LOG_INFO, QString(
"avfDecoder: shoutcast metadata changed - %1").
arg(
m_lastMetadata));
588 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
589 QStringList list =
extension().split(
"|", QString::SkipEmptyParts);
591 QStringList list =
extension().split(
"|", Qt::SkipEmptyParts);
593 return std::any_of(list.cbegin(), list.cend(),
594 [source](
const auto& str)
595 { return str == source.right(str.length()).toLower(); } );
605 static QString s_desc(tr(
"Internal Decoder"));
static const int kMaxSizeBuffer
kMaxSizeBuffer is the maximum size of a buffer to be used with DecodeAudio
virtual int64_t GetAudioBufferedTime(void)
report amount of audio buffered in milliseconds.
static AudioFormat AVSampleFormatToFormat(AVSampleFormat format, int bits=0)
Return AVSampleFormat closest equivalent to AudioFormat.
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
AVCodecContext * GetCodecContext(const AVStream *Stream, const AVCodec *Codec=nullptr, bool NullCodec=false)
QMap< QString, QString > ShoutCastMetaMap
AVFormatContext * getContext(void)
void setObjectName(const QString &name)
arg(title).arg(filename).arg(doDelete))
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
AVInputFormat * m_inputFormat
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
bool initialize() override
RemoteAVFormatContext * m_inputContext
virtual void Drain(void)=0
Events sent by the DecoderHandler and it's helper classes.
bool supports(const QString &source) const override
virtual void SetSourceBitrate(int)
const QString & description() const override
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_GENERAL
void FreeCodecContext(const AVStream *Stream)
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
avfDecoder(const QString &file, DecoderFactory *d, AudioOutput *o)
virtual int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, const AVPacket *pkt)
Utility routine.
const QString & extension() const override
QString getURL(void) const
#define VERBOSE_LEVEL_NONE
virtual bool AddData(void *buffer, int len, int64_t timecode, int frames)=0
Add data to the audiobuffer for playback.
void setURL(const QString &url)
Decoder * create(const QString &file, AudioOutput *output, bool deletable) override
void error(const QString &e)
AVCodecContext * m_audioDec
static void myth_av_log(void *ptr, int level, const char *fmt, va_list vl)
DecoderHandler * getDecoderHandler(void)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
virtual void Reconfigure(const AudioSettings &settings)=0
void setOutput(AudioOutput *o)
MusicMetadata & getMetadata()
void seek(double pos) override
static const iso6937table * d
~avfDecoder(void) override
virtual void PauseUntilBuffered(void)=0
void checkMetatdata(void)