48#define LOC QString("HLS(%1): ").arg(m_sourceFile)
49#define LOC_ERR QString("HLS(%1) Error: ").arg(m_sourceFile)
50#define SLOC QString("HLS(): ")
51#define SLOC_ERR QString("HLS() Error: ")
74 void run(
void)
override
79 QString(
"mythtranscode --hls --hlsstreamid %1")
86 LOG(VB_GENERAL, LOG_WARNING,
SLOC +
87 QString(
"Command '%1' returned %2")
88 .arg(command).arg(result));
98 uint32_t bitrate, uint32_t abitrate,
100 uint32_t aobitrate, int32_t srate)
101 : m_sourceFile(
std::move(srcFile)),
102 m_segmentSize(segmentSize), m_maxSegments(maxSegments),
103 m_height(height), m_width(width),
105 m_audioBitrate(abitrate), m_audioOnlyBitrate(aobitrate),
141 if (!outDir.exists() && !outDir.mkdir(
m_outDir))
143 LOG(VB_RECORD, LOG_ERR,
"Unable to create HTTP Live Stream output "
144 "directory, Live Stream will not be created");
152 : m_streamid(streamid)
182 bool audioOnly,
bool encoded)
const
197 return filename.arg(segmentNumber, 6, 10, QChar(
'0'));
199 return filename.arg(1, 6, 10, QChar(
'0'));
211 QString tmpBase = QString(
"");
212 QString tmpFullURL = QString(
"");
213 QString tmpRelURL = QString(
"");
227 "SELECT id FROM livestream "
229 "(width = :WIDTH OR height = :HEIGHT) AND bitrate = :BITRATE AND "
230 "audioonlybitrate = :AUDIOONLYBITRATE AND samplerate = :SAMPLERATE AND "
231 "audiobitrate = :AUDIOBITRATE AND segmentsize = :SEGMENTSIZE AND "
232 "sourcefile = :SOURCEFILE AND status <= :STATUS ");
245 LOG(VB_GENERAL, LOG_ERR,
LOC +
"LiveStream existing stream check failed.");
252 "INSERT INTO livestream "
253 " ( width, height, bitrate, audiobitrate, segmentsize, "
254 " maxsegments, startsegment, currentsegment, segmentcount, "
255 " percentcomplete, created, lastmodified, relativeurl, "
256 " fullurl, status, statusmessage, sourcefile, sourcehost, "
257 " sourcewidth, sourceheight, outdir, outbase, "
258 " audioonlybitrate, samplerate ) "
260 " ( :WIDTH, :HEIGHT, :BITRATE, :AUDIOBITRATE, :SEGMENTSIZE, "
261 " :MAXSEGMENTS, 0, 0, 0, "
262 " 0, :CREATED, :LASTMODIFIED, :RELATIVEURL, "
263 " :FULLURL, :STATUS, :STATUSMESSAGE, :SOURCEFILE, :SOURCEHOST, "
264 " :SOURCEWIDTH, :SOURCEHEIGHT, :OUTDIR, :OUTBASE, "
265 " :AUDIOONLYBITRATE, :SAMPLERATE ) ");
274 query.
bindValue(
":RELATIVEURL", tmpRelURL);
278 QString(
"Waiting for mythtranscode startup."));
290 LOG(VB_GENERAL, LOG_ERR,
LOC +
"LiveStream insert failed.");
294 if (!query.
exec(
"SELECT LAST_INSERT_ID()") || !query.
next())
296 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to query LiveStream streamid.");
324 if (!QFile::remove(thisFile))
325 LOG(VB_GENERAL, LOG_ERR,
LOC +
326 QString(
"Unable to delete %1.").arg(thisFile));
358 if (!
file.open(QIODevice::WriteOnly))
360 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").arg(outFile));
367 " <title>%1</title>\n"
369 " <body style='background-color:#FFFFFF;'>\n"
371 " <video controls>\n"
372 " <source src='%2.m3u8' />\n"
402 if (!
file.open(QIODevice::WriteOnly))
404 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").arg(outFile));
411 "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"AV\",NAME=\"Main\",DEFAULT=YES,URI=\"%2.m3u8\"\n"
412 "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=%1\n"
420 "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"AO\",NAME=\"Main\",DEFAULT=NO,URI=\"%2.m3u8\"\n"
421 "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=%1\n"
441 QString outFile =
m_outDir +
"/" + base +
".m3u8";
451 QString tmpFile = outFile +
".tmp";
455 if (!
file.open(QIODevice::WriteOnly))
457 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").arg(tmpFile));
463 "#EXT-X-ALLOW-CACHE:YES\n"
464 "#EXT-X-TARGETDURATION:%1\n"
465 "#EXT-X-MEDIA-SEQUENCE:%2\n"
469 file.write(
"#EXT-X-ENDLIST\n");
479 while (i < tmpSegCount)
485 .arg(
GetFilename(segmentid + i,
true, audioOnly,
true)).toLatin1());
492 if(rename(tmpFile.toLatin1().constData(),
493 outFile.toLatin1().constData()) == -1)
495 LOG(VB_RECORD, LOG_ERR,
LOC +
496 QString(
"Error renaming %1 to %2").arg(tmpFile, outFile) +
ENO);
511 "SET startsegment = :START, currentsegment = :CURRENT, "
512 " segmentcount = :COUNT "
513 "WHERE id = :STREAMID; ");
522 LOG(VB_GENERAL, LOG_ERR,
LOC +
523 QString(
"Unable to update segment info for streamid %1")
535 QString newOutBase = finfo.fileName() +
536 QString(
".%1x%2_%3kV_%4kA").arg(width).arg(height)
538 QString newFullURL =
m_httpPrefix + newOutBase +
".m3u8";
544 "SET width = :WIDTH, height = :HEIGHT, "
545 " sourcewidth = :SRCWIDTH, sourceheight = :SRCHEIGHT, "
546 " fullurl = :FULLURL, relativeurl = :RELATIVEURL, "
547 " outbase = :OUTBASE "
548 "WHERE id = :STREAMID; ");
552 query.
bindValue(
":SRCHEIGHT", srcheight);
554 query.
bindValue(
":RELATIVEURL", newRelativeURL);
560 LOG(VB_GENERAL, LOG_ERR,
LOC +
561 QString(
"Unable to update segment info for streamid %1")
587 LOG(VB_RECORD, LOG_DEBUG,
LOC +
588 QString(
"Attempted to switch streamid %1 from "
589 "Stopping to Running State").arg(
m_streamid));
595 LOG(VB_RECORD, LOG_DEBUG,
LOC +
596 QString(
"Switch streamid %1 from %2 to %3")
597 .arg(QString::number(
m_streamid), mStatusStr, statusStr));
604 "SET status = :STATUS "
605 "WHERE id = :STREAMID; ");
612 LOG(VB_GENERAL, LOG_ERR,
LOC +
613 QString(
"Unable to update status for streamid %1").arg(
m_streamid));
625 "SET statusmessage = :MESSAGE "
626 "WHERE id = :STREAMID; ");
636 LOG(VB_GENERAL, LOG_ERR,
LOC +
637 QString(
"Unable to update status message for streamid %1")
650 "SET percentcomplete = :PERCENT "
651 "WHERE id = :STREAMID; ");
661 LOG(VB_GENERAL, LOG_ERR,
LOC +
662 QString(
"Unable to update percent complete for streamid %1")
680 return {
"Unknown status value"};
690 "SELECT width, height, bitrate, audiobitrate, segmentsize, "
691 " maxsegments, startsegment, currentsegment, segmentcount, "
692 " percentcomplete, created, lastmodified, relativeurl, "
693 " fullurl, status, statusmessage, sourcefile, sourcehost, "
694 " sourcewidth, sourceheight, outdir, outbase, audioonlybitrate, "
697 "WHERE id = :STREAMID; ");
702 LOG(VB_GENERAL, LOG_ERR,
LOC +
703 QString(
"Unable to query DB info for stream %1")
754 "http://%1:%2/StorageGroup/Streaming/")
767 else if (
m_httpPrefix.contains(
"/StorageGroup/Streaming/"))
784 "SELECT status FROM livestream "
785 "WHERE id = :STREAMID; ");
790 LOG(VB_GENERAL, LOG_ERR,
LOC +
791 QString(
"Unable to check stop status for stream %1")
806 "SELECT status FROM livestream "
807 "WHERE id = :STREAMID; ");
812 LOG(VB_GENERAL, LOG_ERR,
LOC +
813 QString(
"Unable to check stop status for stream %1")
QRunnable class for running mythtranscode for HTTP Live Streams.
void run(void) override
Runs mythtranscode for the given HTTP Live Stream ID.
HTTPLiveStreamThread(int streamid)
Constructor for creating a SystemEventThread.
uint16_t m_percentComplete
QString GetHTMLPageName(void) const
QString GetCurrentFilename(bool audioOnly=false, bool encoded=false) const
QString GetPlaylistName(bool audioOnly=false) const
HTTPLiveStreamStatus GetDBStatus(void) const
QString m_audioOutFileEncoded
HTTPLiveStream(QString srcFile, uint16_t width=640, uint16_t height=480, uint32_t bitrate=800000, uint32_t abitrate=64000, uint16_t maxSegments=0, uint16_t segmentSize=10, uint32_t aobitrate=32000, int32_t srate=-1)
bool WritePlaylist(bool audioOnly=false, bool writeEndTag=false)
static QString StatusToString(HTTPLiveStreamStatus status)
bool SaveSegmentInfo(void)
bool UpdatePercentComplete(int percent)
bool WriteMetaPlaylist(void)
bool UpdateStatus(HTTPLiveStreamStatus status)
QString GetFilename(uint16_t segmentNumber=0, bool fileOnly=false, bool audioOnly=false, bool encoded=false) const
bool UpdateSizeInfo(uint16_t width, uint16_t height, uint16_t srcwidth, uint16_t srcheight)
bool UpdateStatusMessage(const QString &message)
HTTPLiveStreamStatus m_status
uint32_t m_audioOnlyBitrate
QString GetMetaPlaylistName(void) const
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
int GetMasterServerStatusPort(void)
Returns the Master Backend status port If no master server status port has been defined in the databa...
QString GetFirstDir(bool appendSlash=false) const
@ GENERIC_EXIT_OK
Exited with no error.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetAppBinDir(void)
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.