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/"))
780 "SELECT status FROM livestream "
781 "WHERE id = :STREAMID; ");
786 LOG(VB_GENERAL, LOG_ERR,
LOC +
787 QString(
"Unable to check stop status for stream %1")
802 "SELECT status FROM livestream "
803 "WHERE id = :STREAMID; ");
808 LOG(VB_GENERAL, LOG_ERR,
LOC +
809 QString(
"Unable to check stop status for stream %1")
833 delay = (int)(delay * 1.5);
846 "SELECT startSegment, segmentCount "
848 "WHERE id = :STREAMID; ");
853 LOG(VB_RECORD, LOG_ERR,
"Error selecting stream info in RemoveStream");
864 int startSegment = query.
value(0).toInt();
865 int segmentCount = query.
value(1).toInt();
867 for (
int x = 0; x < segmentCount; ++x)
869 thisFile = hls->GetFilename(startSegment + x);
871 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
872 LOG(VB_GENERAL, LOG_ERR,
SLOC +
873 QString(
"Unable to delete %1.").arg(thisFile));
875 thisFile = hls->GetFilename(startSegment + x,
false,
true);
877 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
878 LOG(VB_GENERAL, LOG_ERR,
SLOC +
879 QString(
"Unable to delete %1.").arg(thisFile));
882 thisFile = hls->GetMetaPlaylistName();
883 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
884 LOG(VB_GENERAL, LOG_ERR,
SLOC +
885 QString(
"Unable to delete %1.").arg(thisFile));
887 thisFile = hls->GetPlaylistName();
888 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
889 LOG(VB_GENERAL, LOG_ERR,
SLOC +
890 QString(
"Unable to delete %1.").arg(thisFile));
892 thisFile = hls->GetPlaylistName(
true);
893 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
894 LOG(VB_GENERAL, LOG_ERR,
SLOC +
895 QString(
"Unable to delete %1.").arg(thisFile));
897 thisFile = hls->GetHTMLPageName();
898 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
899 LOG(VB_GENERAL, LOG_ERR,
SLOC +
900 QString(
"Unable to delete %1.").arg(thisFile));
903 "DELETE FROM livestream "
904 "WHERE id = :STREAMID; ");
908 LOG(VB_RECORD, LOG_ERR,
"Error deleting stream info in RemoveStream");
919 "SET status = :STATUS "
920 "WHERE id = :STREAMID; ");
926 LOG(VB_GENERAL, LOG_ERR,
SLOC +
927 QString(
"Unable to remove mark stream stopped for stream %1.")
946 delay = (int)(delay * 1.5);
949 status = hls->GetDBStatus();
956 return pLiveStreamInfo;
1003 QString sql =
"SELECT id FROM livestream ";
1005 if (!FileName.isEmpty())
1006 sql +=
"WHERE sourcefile LIKE :FILENAME ";
1008 sql +=
"ORDER BY lastmodified DESC;";
1012 if (!FileName.isEmpty())
1013 query.
bindValue(
":FILENAME", QString(
"%%1%").arg(FileName));
1017 LOG(VB_GENERAL, LOG_ERR,
SLOC +
"Unable to get list of Live Streams");
1023 while (query.
next())
1026 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)
@ GENERIC_EXIT_OK
Exited with no error.
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.
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.