42 #define LOC QString("HLS(%1): ").arg(m_sourceFile)
43 #define LOC_ERR QString("HLS(%1) Error: ").arg(m_sourceFile)
44 #define SLOC QString("HLS(): ")
45 #define SLOC_ERR QString("HLS() Error: ")
75 QString(
"/bin/mythtranscode --hls --hlsstreamid %1")
80 if (result != GENERIC_EXIT_OK)
81 LOG(VB_GENERAL, LOG_WARNING, SLOC +
82 QString(
"Command '%1' returned %2")
83 .arg(command).arg(result));
92 uint32_t bitrate, uint32_t abitrate,
94 uint32_t aobitrate, int32_t srate)
96 m_streamid(-1), m_sourceFile(srcFile),
97 m_sourceWidth(0), m_sourceHeight(0),
98 m_segmentSize(segmentSize), m_maxSegments(maxSegments),
99 m_segmentCount(0), m_startSegment(0),
101 m_height(height), m_width(width),
103 m_audioBitrate(abitrate), m_audioOnlyBitrate(aobitrate),
105 m_created(MythDate::
current()),
106 m_lastModified(MythDate::
current()),
107 m_percentComplete(0),
139 QDir outDir(m_outDir);
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");
183 bool audioOnly,
bool encoded)
const
192 filename +=
".%1.ts";
195 filename =
m_outDir +
"/" + filename;
198 return filename.arg(segmentNumber, 6, 10, QChar(
'0'));
200 return filename.arg(1, 6, 10, QChar(
'0'));
212 QString tmpBase = QString(
"");
213 QString tmpFullURL = QString(
"");
214 QString tmpRelURL = QString(
"");
225 "INSERT INTO livestream "
226 " ( width, height, bitrate, audiobitrate, segmentsize, "
227 " maxsegments, startsegment, currentsegment, segmentcount, "
228 " percentcomplete, created, lastmodified, relativeurl, "
229 " fullurl, status, statusmessage, sourcefile, sourcehost, "
230 " sourcewidth, sourceheight, outdir, outbase, "
231 " audioonlybitrate, samplerate ) "
233 " ( :WIDTH, :HEIGHT, :BITRATE, :AUDIOBITRATE, :SEGMENTSIZE, "
234 " :MAXSEGMENTS, 0, 0, 0, "
235 " 0, :CREATED, :LASTMODIFIED, :RELATIVEURL, "
236 " :FULLURL, :STATUS, :STATUSMESSAGE, :SOURCEFILE, :SOURCEHOST, "
237 " :SOURCEWIDTH, :SOURCEHEIGHT, :OUTDIR, :OUTBASE, "
238 " :AUDIOONLYBITRATE, :SAMPLERATE ) ");
247 query.
bindValue(
":RELATIVEURL", tmpRelURL);
251 QString(
"Waiting for mythtranscode startup."));
263 LOG(VB_GENERAL, LOG_ERR,
LOC +
"LiveStream insert failed.");
267 if (!query.
exec(
"SELECT LAST_INSERT_ID()") || !query.
next())
269 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to query LiveStream streamid.");
296 if (!QFile::remove(thisFile))
297 LOG(VB_GENERAL, LOG_ERR,
LOC +
298 QString(
"Unable to delete %1.").arg(thisFile));
330 if (!file.open(QIODevice::WriteOnly))
332 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").arg(outFile));
339 " <title>%1</title>\n"
341 " <body style='background-color:#FFFFFF;'>\n"
343 " <video controls>\n"
344 " <source src='%2.m3u8' />\n"
374 if (!file.open(QIODevice::WriteOnly))
376 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").arg(outFile));
382 "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=%1\n"
390 "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=%1\n"
410 QString outFile =
m_outDir +
"/" + base +
".m3u8";
420 QString tmpFile = outFile +
".tmp";
424 if (!file.open(QIODevice::WriteOnly))
426 LOG(VB_RECORD, LOG_ERR, QString(
"Error opening %1").arg(tmpFile));
432 "#EXT-X-TARGETDURATION:%1\n"
433 "#EXT-X-MEDIA-SEQUENCE:%2\n"
437 file.write(
"#EXT-X-ENDLIST\n");
447 while (i < tmpSegCount)
453 .arg(
GetFilename(segmentid + i,
true, audioOnly,
true)).toLatin1());
460 rename(tmpFile.toLatin1().constData(), outFile.toLatin1().constData());
473 "SET startsegment = :START, currentsegment = :CURRENT, "
474 " segmentcount = :COUNT "
475 "WHERE id = :STREAMID; ");
484 LOG(VB_GENERAL, LOG_ERR,
LOC +
485 QString(
"Unable to update segment info for streamid %1")
497 QString newOutBase = finfo.fileName() +
498 QString(
".%1x%2_%3kV_%4kA").arg(width).arg(height)
500 QString newFullURL =
m_httpPrefix + newOutBase +
".m3u8";
506 "SET width = :WIDTH, height = :HEIGHT, "
507 " sourcewidth = :SRCWIDTH, sourceheight = :SRCHEIGHT, "
508 " fullurl = :FULLURL, relativeurl = :RELATIVEURL, "
509 " outbase = :OUTBASE "
510 "WHERE id = :STREAMID; ");
514 query.
bindValue(
":SRCHEIGHT", srcheight);
516 query.
bindValue(
":RELATIVEURL", newRelativeURL);
522 LOG(VB_GENERAL, LOG_ERR,
LOC +
523 QString(
"Unable to update segment info for streamid %1")
549 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"Attempted to switch from "
550 "Stopping to Running State");
561 "SET status = :STATUS "
562 "WHERE id = :STREAMID; ");
569 LOG(VB_GENERAL, LOG_ERR,
LOC +
570 QString(
"Unable to update status for streamid %1").arg(
m_streamid));
582 "SET statusmessage = :MESSAGE "
583 "WHERE id = :STREAMID; ");
593 LOG(VB_GENERAL, LOG_ERR,
LOC +
594 QString(
"Unable to update status message for streamid %1")
607 "SET percentcomplete = :PERCENT "
608 "WHERE id = :STREAMID; ");
618 LOG(VB_GENERAL, LOG_ERR,
LOC +
619 QString(
"Unable to update percent complete for streamid %1")
637 return QString(
"Unknown status value");
647 "SELECT width, height, bitrate, audiobitrate, segmentsize, "
648 " maxsegments, startsegment, currentsegment, segmentcount, "
649 " percentcomplete, created, lastmodified, relativeurl, "
650 " fullurl, status, statusmessage, sourcefile, sourcehost, "
651 " sourcewidth, sourceheight, outdir, outbase, audioonlybitrate, "
654 "WHERE id = :STREAMID; ");
659 LOG(VB_GENERAL, LOG_ERR,
LOC +
660 QString(
"Unable to query DB info for stream %1")
711 "http://%1:%2/StorageGroup/Streaming/")
724 else if (
m_httpPrefix.contains(
"/StorageGroup/Streaming/"))
737 "SELECT status FROM livestream "
738 "WHERE id = :STREAMID; ");
743 LOG(VB_GENERAL, LOG_ERR,
LOC +
744 QString(
"Unable to check stop status for stream %1")
759 "SELECT status FROM livestream "
760 "WHERE id = :STREAMID; ");
765 LOG(VB_GENERAL, LOG_ERR,
LOC +
766 QString(
"Unable to check stop status for stream %1")
789 ((statusTimer.
elapsed() / 1000) < 30))
791 delay = (
int)(delay * 1.5);
804 "SELECT startSegment, segmentCount "
806 "WHERE id = :STREAMID; ");
811 LOG(VB_RECORD, LOG_ERR,
"Error selecting stream info in RemoveStream");
822 int startSegment = query.
value(0).toInt();
823 int segmentCount = query.
value(1).toInt();
825 for (
int x = 0;
x < segmentCount; ++
x)
829 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
830 LOG(VB_GENERAL, LOG_ERR, SLOC +
831 QString(
"Unable to delete %1.").arg(thisFile));
833 thisFile = hls->
GetFilename(startSegment +
x,
false,
true);
835 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
836 LOG(VB_GENERAL, LOG_ERR, SLOC +
837 QString(
"Unable to delete %1.").arg(thisFile));
841 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
842 LOG(VB_GENERAL, LOG_ERR, SLOC +
843 QString(
"Unable to delete %1.").arg(thisFile));
846 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
847 LOG(VB_GENERAL, LOG_ERR, SLOC +
848 QString(
"Unable to delete %1.").arg(thisFile));
851 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
852 LOG(VB_GENERAL, LOG_ERR, SLOC +
853 QString(
"Unable to delete %1.").arg(thisFile));
856 if (!thisFile.isEmpty() && !QFile::remove(thisFile))
857 LOG(VB_GENERAL, LOG_ERR, SLOC +
858 QString(
"Unable to delete %1.").arg(thisFile));
861 "DELETE FROM livestream "
862 "WHERE id = :STREAMID; ");
866 LOG(VB_RECORD, LOG_ERR,
"Error deleting stream info in RemoveStream");
877 "SET status = :STATUS "
878 "WHERE id = :STREAMID; ");
884 LOG(VB_GENERAL, LOG_ERR, SLOC +
885 QString(
"Unable to remove mark stream stopped for stream %1.")
902 ((statusTimer.
elapsed() / 1000) < 30))
904 delay = (
int)(delay * 1.5);
914 return pLiveStreamInfo;
958 QString sql =
"SELECT id FROM livestream ";
960 if (!FileName.isEmpty())
961 sql +=
"WHERE sourcefile LIKE :FILENAME ";
963 sql +=
"ORDER BY lastmodified DESC;";
967 if (!FileName.isEmpty())
968 query.
bindValue(
":FILENAME", QString(
"%%1%").arg(FileName));
972 LOG(VB_GENERAL, LOG_ERR, SLOC +
"Unable to get list of Live Streams");