Skip to content

Commit

Permalink
Extend IPTV/HLS/M3U tag parsing
Browse files Browse the repository at this point in the history
Add parsing of tag EXT-X-DISCONTINUITY-SEQUENCE.
Previously, this tag was erroneously parsed and
executed as EXT-X-DISCONTINUITY. This then caused
continuous downloading at maximum bandwidth.

Add parsing of tags EXT-X-INDEPENDENT-SEGMENTS.
There is no implementation added but this does
prevent parsing failure.

Allow EXT-X-VERSION tag values from 1 to 7.
There is no implementation added but this does
prevent parsing failure.

Refs #936
  • Loading branch information
kmdewaal committed Nov 23, 2024
1 parent 0cab851 commit a8f86b2
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 8 deletions.
54 changes: 51 additions & 3 deletions mythtv/libs/libmythtv/HLS/m3u.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ namespace M3U
return false;
}

if (version <= 0 || version > 3)
if (version < 1 || version > 7)
{
LOG(VB_RECORD, LOG_ERR, loc +
QString("#EXT-X-VERSION is %1, but we only understand 0 through 3")
QString("#EXT-X-VERSION is %1, but we only understand 1 to 7")
.arg(version));
return false;
}
Expand Down Expand Up @@ -341,6 +341,11 @@ namespace M3U
path = DecodedURI(uri.remove(QChar(QLatin1Char('"'))));
iv = ParseAttributes(line, "IV");
}
else if (attr.startsWith(QLatin1String("SAMPLE-AES")))
{
LOG(VB_RECORD, LOG_ERR, loc + "encryption SAMPLE-AES not supported.");
return false;
}
#endif
else
{
Expand Down Expand Up @@ -405,6 +410,30 @@ namespace M3U
return true;
}

bool ParseDiscontinuitySequence(const QString& line, const QString& loc, int &discontinuity_sequence)
{
/*
* The EXT-X-DISCONTINUITY-SEQUENCE tag allows synchronization between
* different Renditions of the same Variant Stream or different Variant
* Streams that have EXT-X-DISCONTINUITY tags in their Media Playlists.
*
* Its format is:
*
* #EXT-X-DISCONTINUITY-SEQUENCE:<number>
*
* where number is a decimal-integer
*/
if (!ParseDecimalValue(line, discontinuity_sequence))
{
LOG(VB_RECORD, LOG_ERR, loc + "expected #EXT-X-DISCONTINUITY-SEQUENCE:<s>");
return false;
}

LOG(VB_RECORD, LOG_DEBUG, loc + QString("#EXT-X-DISCONTINUITY-SEQUENCE %1")
.arg(line));
return true;
}

bool ParseDiscontinuity(const QString& line, const QString& loc)
{
/* Not handled, never seen so far */
Expand All @@ -425,4 +454,23 @@ namespace M3U
return true;
}

} // Namespace M3U
bool ParseIndependentSegments(const QString& line, const QString& loc)
{
/* #EXT-X-INDEPENDENT-SEGMENTS
*
* The EXT-X-INDEPENDENT-SEGMENTS tag indicates that all media samples
* in a Media Segment can be decoded without information from other
* segments. It applies to every Media Segment in the Playlist.
*
* Its format is:
*
* #EXT-X-INDEPENDENT-SEGMENTS
*/

// Not handled yet
LOG(VB_RECORD, LOG_DEBUG, loc + QString("#EXT-X-INDEPENDENT-SEGMENTS %1")
.arg(line));
return true;
}

} // namespace M3U
4 changes: 4 additions & 0 deletions mythtv/libs/libmythtv/HLS/m3u.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <cinttypes>

#include <QDateTime>
#include <QString>

namespace M3U
{
Expand All @@ -30,8 +31,11 @@ namespace M3U
QDateTime &date);
bool ParseAllowCache(const QString& line, const QString& loc,
bool& do_cache);
bool ParseDiscontinuitySequence(const QString& line, const QString& loc,
int &discontinuity_sequence);
bool ParseDiscontinuity(const QString& line, const QString& loc);
bool ParseEndList(const QString& loc, bool& is_vod);
bool ParseIndependentSegments(const QString& line, const QString& loc);
}

#endif // MYTHTV_M3U_H
22 changes: 20 additions & 2 deletions mythtv/libs/libmythtv/recorders/HLS/HLSReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
#include "HLSReader.h"
#include "HLS/m3u.h"

#define LOC QString("%1: ").arg(m_curstream ? m_curstream->M3U8Url() : "HLSReader")
#define LOC QString("HLSReader[%1]%2: ")\
.arg(m_inputId).arg(m_curstream ? "(" + m_curstream->M3U8Url() + ")" : "")

/**
* Handles relative URLs without breaking URI encoded parameters by avoiding
Expand Down Expand Up @@ -264,7 +265,11 @@ bool HLSReader::IsValidPlaylist(QTextStream & text)
{
/* Parse stream and search for
* EXT-X-TARGETDURATION or EXT-X-STREAM-INF tag, see
* http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8 */
* http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8
*
* Updated with latest available version from 2017, see
* https://datatracker.ietf.org/doc/html/rfc8216
*/
QString line = text.readLine();
if (!line.startsWith((const char*)"#EXTM3U"))
return false;
Expand Down Expand Up @@ -509,12 +514,25 @@ bool HLSReader::ParseM3U8(const QByteArray& buffer, HLSRecStream* stream)
return false;
hls->SetCache(do_cache);
}
else if (line.startsWith(QLatin1String("#EXT-X-DISCONTINUITY-SEQUENCE")))
{
int sequence = 0;
if (!M3U::ParseDiscontinuitySequence(line, StreamURL(), sequence))
return false;
SetDiscontinuitySequence(sequence);
}
else if (line.startsWith(QLatin1String("#EXT-X-DISCONTINUITY")))
{
if (!M3U::ParseDiscontinuity(line, StreamURL()))
return false;
ResetSequence();
}
else if (line.startsWith(QLatin1String("#EXT-X-INDEPENDENT-SEGMENTS")))
{
if (!M3U::ParseIndependentSegments(line, StreamURL()))
return false;
// Not handled yet
}
else if (line.startsWith(QLatin1String("#EXT-X-VERSION")))
{
int version2 = 0;
Expand Down
10 changes: 8 additions & 2 deletions mythtv/libs/libmythtv/recorders/HLS/HLSReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class MTV_PUBLIC HLSReader
using StreamContainer = QMap<QString, HLSRecStream* >;
using SegmentContainer = QVector<HLSRecSegment>;

HLSReader(void) = default;
HLSReader(int inputId) { m_inputId = inputId; };
~HLSReader(void);

bool Open(const QString & m3u, int bitrate_index = 0);
Expand All @@ -56,6 +56,7 @@ class MTV_PUBLIC HLSReader
void ResetStream(void)
{ QMutexLocker lock(&m_streamLock); m_curstream = nullptr; }
void ResetSequence(void) { m_curSeq = -1; }
void SetDiscontinuitySequence(int s) { m_sequence = s; }

QString StreamURL(void) const
{ return QString("%1").arg(m_curstream ? m_curstream->M3U8Url() : ""); }
Expand Down Expand Up @@ -106,7 +107,7 @@ class MTV_PUBLIC HLSReader
bool m_fatal {false};
bool m_cancel {false};
bool m_throttle {true};
// only print one time that the media is encrypted
// Only print one time that the media is encrypted
bool m_aesMsg {false};

HLSPlaylistWorker *m_playlistWorker {nullptr};
Expand All @@ -127,6 +128,11 @@ class MTV_PUBLIC HLSReader
int m_slowCnt {0};
QByteArray m_buffer;
QMutex m_bufLock;

int m_sequence {0}; // Discontinuity sequence number

// Log message
int m_inputId {0};
};

#endif // HLS_READER_H
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/recorders/hlsstreamhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ HLSStreamHandler::HLSStreamHandler(const IPTVTuningData& tuning, int inputid)
: IPTVStreamHandler(tuning, inputid)
{
LOG(VB_GENERAL, LOG_INFO, LOC + "ctor");
m_hls = new HLSReader();
m_hls = new HLSReader(m_inputId);
m_readbuffer = new uint8_t[BUFFER_SIZE];
}

Expand Down

0 comments on commit a8f86b2

Please sign in to comment.