Go to the documentation of this file.
47 #define LOC QString("HLS(%1): ").arg(m_sourceFile)
48 #define LOC_ERR QString("HLS(%1) Error: ").arg(m_sourceFile)
49 #define SLOC QString("HLS(): ")
50 #define SLOC_ERR QString("HLS() Error: ")
73 void run(
void)
override
78 QString(
"mythtranscode --hls --hlsstreamid %1")
85 LOG(VB_GENERAL, LOG_WARNING,
SLOC +
86 QString(
"Command '%1' returned %2")
87 .
arg(command).
arg(result));
97 uint32_t bitrate, uint32_t abitrate,
99 uint32_t aobitrate, int32_t srate)
100 : m_sourceFile(
std::move(srcFile)),
101 m_segmentSize(segmentSize), m_maxSegments(maxSegments),
102 m_height(height), m_width(width),
104 m_audioBitrate(abitrate), m_audioOnlyBitrate(aobitrate),
140 if (!outDir.exists() && !outDir.mkdir(
m_outDir))
142 LOG(VB_RECORD, LOG_ERR,
"Unable to create HTTP Live Stream output "
143 "directory, Live Stream will not be created");
151 : m_streamid(streamid)
181 bool audioOnly,
bool encoded)
const
196 return filename.arg(segmentNumber, 6, 10, QChar(
'0'));
198 return filename.arg(1, 6, 10, QChar(
'0'));
210 QString tmpBase = QString(
"");
211 QString tmpFullURL = QString(
"");
212 QString tmpRelURL = QString(
"");
226 "SELECT id FROM livestream "
228 "(width = :WIDTH OR height = :HEIGHT) AND bitrate = :BITRATE AND "
229 "audioonlybitrate = :AUDIOONLYBITRATE AND samplerate = :SAMPLERATE AND "
230 "audiobitrate = :AUDIOBITRATE AND segmentsize = :SEGMENTSIZE AND "
231 "sourcefile = :SOURCEFILE AND status <= :STATUS ");
244 LOG(VB_GENERAL, LOG_ERR,
LOC +
"LiveStream existing stream check failed.");
251 "INSERT INTO livestream "
252 " ( width, height, bitrate, audiobitrate, segmentsize, "
253 " maxsegments, startsegment, currentsegment, segmentcount, "
254 " percentcomplete, created, lastmodified, relativeurl, "
255 " fullurl, status, statusmessage, sourcefile, sourcehost, "
256 " sourcewidth, sourceheight, outdir, outbase, "
257 " audioonlybitrate, samplerate ) "
259 " ( :WIDTH, :HEIGHT, :BITRATE, :AUDIOBITRATE, :SEGMENTSIZE, "
260 " :MAXSEGMENTS, 0, 0, 0, "
261 " 0, :CREATED, :LASTMODIFIED, :RELATIVEURL, "
262 " :FULLURL, :STATUS, :STATUSMESSAGE, :SOURCEFILE, :SOURCEHOST, "
263 " :SOURCEWIDTH, :SOURCEHEIGHT, :OUTDIR, :OUTBASE, "
264 " :AUDIOONLYBITRATE, :SAMPLERATE ) ");
277 QString(
"Waiting for mythtranscode startup."));
289 LOG(VB_GENERAL, LOG_ERR,
LOC +
"LiveStream insert failed.");
295 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to query LiveStream streamid.");
323 if (!QFile::remove(thisFile))
324 LOG(VB_GENERAL, LOG_ERR,
LOC +
325 QString(
"Unable to delete %1.").
arg(thisFile));
357 if (!
file.open(QIODevice::WriteOnly))
359 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").
arg(outFile));
366 " <title>%1</title>\n"
368 " <body style='background-color:#FFFFFF;'>\n"
370 " <video controls>\n"
371 " <source src='%2.m3u8' />\n"
401 if (!
file.open(QIODevice::WriteOnly))
403 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").
arg(outFile));
410 "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"AV\",NAME=\"Main\",DEFAULT=YES,URI=\"%2.m3u8\"\n"
411 "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=%1\n"
419 "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"AO\",NAME=\"Main\",DEFAULT=NO,URI=\"%2.m3u8\"\n"
420 "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=%1\n"
440 QString outFile =
m_outDir +
"/" + base +
".m3u8";
450 QString tmpFile = outFile +
".tmp";
454 if (!
file.open(QIODevice::WriteOnly))
456 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").
arg(tmpFile));
462 "#EXT-X-ALLOW-CACHE:YES\n"
463 "#EXT-X-TARGETDURATION:%1\n"
464 "#EXT-X-MEDIA-SEQUENCE:%2\n"
468 file.write(
"#EXT-X-ENDLIST\n");
478 while (i < tmpSegCount)
484 .
arg(
GetFilename(segmentid + i,
true, audioOnly,
true)).toLatin1());
491 if(rename(tmpFile.toLatin1().constData(),
492 outFile.toLatin1().constData()) == -1)
494 LOG(VB_RECORD, LOG_ERR,
LOC +
495 QString(
"Error renaming %1 to %2").
arg(tmpFile).
arg(outFile) +
ENO);
510 "SET startsegment = :START, currentsegment = :CURRENT, "
511 " segmentcount = :COUNT "
512 "WHERE id = :STREAMID; ");
521 LOG(VB_GENERAL, LOG_ERR,
LOC +
522 QString(
"Unable to update segment info for streamid %1")
534 QString newOutBase = finfo.fileName() +
535 QString(
".%1x%2_%3kV_%4kA").arg(width).arg(height)
537 QString newFullURL =
m_httpPrefix + newOutBase +
".m3u8";
543 "SET width = :WIDTH, height = :HEIGHT, "
544 " sourcewidth = :SRCWIDTH, sourceheight = :SRCHEIGHT, "
545 " fullurl = :FULLURL, relativeurl = :RELATIVEURL, "
546 " outbase = :OUTBASE "
547 "WHERE id = :STREAMID; ");
559 LOG(VB_GENERAL, LOG_ERR,
LOC +
560 QString(
"Unable to update segment info for streamid %1")
586 LOG(VB_RECORD, LOG_DEBUG,
LOC +
587 QString(
"Attempted to switch streamid %1 from "
594 LOG(VB_RECORD, LOG_DEBUG,
LOC +
595 QString(
"Switch streamid %1 from %2 to %3")
603 "SET status = :STATUS "
604 "WHERE id = :STREAMID; ");
611 LOG(VB_GENERAL, LOG_ERR,
LOC +
612 QString(
"Unable to update status for streamid %1").
arg(
m_streamid));
624 "SET statusmessage = :MESSAGE "
625 "WHERE id = :STREAMID; ");
635 LOG(VB_GENERAL, LOG_ERR,
LOC +
636 QString(
"Unable to update status message for streamid %1")
649 "SET percentcomplete = :PERCENT "
650 "WHERE id = :STREAMID; ");
660 LOG(VB_GENERAL, LOG_ERR,
LOC +
661 QString(
"Unable to update percent complete for streamid %1")
679 return QString(
"Unknown status value");
689 "SELECT width, height, bitrate, audiobitrate, segmentsize, "
690 " maxsegments, startsegment, currentsegment, segmentcount, "
691 " percentcomplete, created, lastmodified, relativeurl, "
692 " fullurl, status, statusmessage, sourcefile, sourcehost, "
693 " sourcewidth, sourceheight, outdir, outbase, audioonlybitrate, "
696 "WHERE id = :STREAMID; ");
701 LOG(VB_GENERAL, LOG_ERR,
LOC +
702 QString(
"Unable to query DB info for stream %1")
753 "http://%1:%2/StorageGroup/Streaming/")
766 else if (
m_httpPrefix.contains(
"/StorageGroup/Streaming/"))
779 "SELECT status FROM livestream "
780 "WHERE id = :STREAMID; ");
785 LOG(VB_GENERAL, LOG_ERR,
LOC +
786 QString(
"Unable to check stop status for stream %1")
801 "SELECT status FROM livestream "
802 "WHERE id = :STREAMID; ");
807 LOG(VB_GENERAL, LOG_ERR,
LOC +
808 QString(
"Unable to check stop status for stream %1")
832 delay = (int)(delay * 1.5);
845 "SELECT startSegment, segmentCount "
847 "WHERE id = :STREAMID; ");
852 LOG(VB_RECORD, LOG_ERR,
"Error selecting stream info in RemoveStream");
866 for (
int x = 0; x < segmentCount; ++x)
868 thisFile = hls->GetFilename(startSegment + x);
870 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
871 LOG(VB_GENERAL, LOG_ERR,
SLOC +
872 QString(
"Unable to delete %1.").
arg(thisFile));
874 thisFile = hls->GetFilename(startSegment + x,
false,
true);
876 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
877 LOG(VB_GENERAL, LOG_ERR,
SLOC +
878 QString(
"Unable to delete %1.").
arg(thisFile));
881 thisFile = hls->GetMetaPlaylistName();
882 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
883 LOG(VB_GENERAL, LOG_ERR,
SLOC +
884 QString(
"Unable to delete %1.").
arg(thisFile));
886 thisFile = hls->GetPlaylistName();
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(
true);
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->GetHTMLPageName();
897 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
898 LOG(VB_GENERAL, LOG_ERR,
SLOC +
899 QString(
"Unable to delete %1.").
arg(thisFile));
902 "DELETE FROM livestream "
903 "WHERE id = :STREAMID; ");
907 LOG(VB_RECORD, LOG_ERR,
"Error deleting stream info in RemoveStream");
918 "SET status = :STATUS "
919 "WHERE id = :STREAMID; ");
925 LOG(VB_GENERAL, LOG_ERR,
SLOC +
926 QString(
"Unable to remove mark stream stopped for stream %1.")
945 delay = (int)(delay * 1.5);
948 status = hls->GetDBStatus();
955 return pLiveStreamInfo;
1002 QString sql =
"SELECT id FROM livestream ";
1004 if (!FileName.isEmpty())
1005 sql +=
"WHERE sourcefile LIKE :FILENAME ";
1007 sql +=
"ORDER BY lastmodified DESC;";
1011 if (!FileName.isEmpty())
1016 LOG(VB_GENERAL, LOG_ERR,
SLOC +
"Unable to get list of Live Streams");
1025 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 "+".
#define GENERIC_EXIT_OK
Exited with no error.
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
arg(title).arg(filename).arg(doDelete))
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.
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)
MSqlQuery query(MSqlQuery::InitCon())
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.