Go to the documentation of this file.
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")
837 delay = (int)(delay * 1.5);
850 "SELECT startSegment, segmentCount "
852 "WHERE id = :STREAMID; ");
857 LOG(VB_RECORD, LOG_ERR,
"Error selecting stream info in RemoveStream");
868 int startSegment = query.
value(0).toInt();
869 int segmentCount = query.
value(1).toInt();
871 for (
int x = 0; x < segmentCount; ++x)
873 thisFile = hls->GetFilename(startSegment + x);
875 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
876 LOG(VB_GENERAL, LOG_ERR,
SLOC +
877 QString(
"Unable to delete %1.").arg(thisFile));
879 thisFile = hls->GetFilename(startSegment + x,
false,
true);
881 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
882 LOG(VB_GENERAL, LOG_ERR,
SLOC +
883 QString(
"Unable to delete %1.").arg(thisFile));
886 thisFile = hls->GetMetaPlaylistName();
887 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
888 LOG(VB_GENERAL, LOG_ERR,
SLOC +
889 QString(
"Unable to delete %1.").arg(thisFile));
891 thisFile = hls->GetPlaylistName();
892 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
893 LOG(VB_GENERAL, LOG_ERR,
SLOC +
894 QString(
"Unable to delete %1.").arg(thisFile));
896 thisFile = hls->GetPlaylistName(
true);
897 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
898 LOG(VB_GENERAL, LOG_ERR,
SLOC +
899 QString(
"Unable to delete %1.").arg(thisFile));
901 thisFile = hls->GetHTMLPageName();
902 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
903 LOG(VB_GENERAL, LOG_ERR,
SLOC +
904 QString(
"Unable to delete %1.").arg(thisFile));
907 "DELETE FROM livestream "
908 "WHERE id = :STREAMID; ");
912 LOG(VB_RECORD, LOG_ERR,
"Error deleting stream info in RemoveStream");
923 "SET status = :STATUS "
924 "WHERE id = :STREAMID; ");
930 LOG(VB_GENERAL, LOG_ERR,
SLOC +
931 QString(
"Unable to remove mark stream stopped for stream %1.")
950 delay = (int)(delay * 1.5);
953 status = hls->GetDBStatus();
960 return pLiveStreamInfo;
1007 QString sql =
"SELECT id FROM livestream ";
1009 if (!FileName.isEmpty())
1010 sql +=
"WHERE sourcefile LIKE :FILENAME ";
1012 sql +=
"ORDER BY lastmodified DESC;";
1016 if (!FileName.isEmpty())
1017 query.
bindValue(
":FILENAME", QString(
"%%1%").arg(FileName));
1021 LOG(VB_GENERAL, LOG_ERR,
SLOC +
"Unable to get list of Live Streams");
1027 while (query.
next())
1030 info = infoList->AddNewLiveStreamInfo();
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
#define ENO
This can be appended to the LOG args with "+".
QString GetCurrentFilename(bool audioOnly=false, bool encoded=false) const
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
bool WritePlaylist(bool audioOnly=false, bool writeEndTag=false)
bool UpdateSizeInfo(uint16_t width, uint16_t height, uint16_t srcwidth, uint16_t srcheight)
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
A QElapsedTimer based timer to replace use of QTime as a timer.
QString GetHTMLPageName(void) const
QVariant value(int i) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void start(void)
starts measuring elapsed time.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
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 UpdateStatus(HTTPLiveStreamStatus status)
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
void startReserved(QRunnable *runnable, const QString &debugName, std::chrono::milliseconds waitForAvailMS=0ms)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
int GetMasterServerStatusPort(void)
Returns the Master Backend status port If no master server status port has been defined in the databa...
HTTPLiveStreamStatus GetDBStatus(void) const
static QString StatusToString(HTTPLiveStreamStatus status)
QString GetFilename(uint16_t segmentNumber=0, bool fileOnly=false, bool audioOnly=false, bool encoded=false) const
QRunnable class for running mythtranscode for HTTP Live Streams.
bool SaveSegmentInfo(void)
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
HTTPLiveStreamThread(int streamid)
Constructor for creating a SystemEventThread.
@ GENERIC_EXIT_OK
Exited with no error.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
DTC::LiveStreamInfo * GetLiveStreamInfo(DTC::LiveStreamInfo *info=nullptr)
bool WriteMetaPlaylist(void)
bool UpdateStatusMessage(const QString &message)
DTC::LiveStreamInfo * StartStream(void)
HTTPLiveStreamStatus m_status
static DTC::LiveStreamInfoList * GetLiveStreamInfoList(const QString &FileName="")
QString GetFirstDir(bool appendSlash=false) const
QString GetMetaPlaylistName(void) const
QString m_audioOutFileEncoded
QString GetPlaylistName(bool audioOnly=false) const
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static DTC::LiveStreamInfo * StopStream(int id)
bool UpdatePercentComplete(int percent)
static bool RemoveStream(int id)
uint32_t m_audioOnlyBitrate
uint16_t m_percentComplete
int GetStreamID(void) const
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
QString GetAppBinDir(void)
QString GetHostName(void)
static MThreadPool * globalInstance(void)
void run(void) override
Runs mythtranscode for the given HTTP Live Stream ID.
QString GetSetting(const QString &key, const QString &defaultval="")
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.