Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add HTTP Live Streaming recorder.

This is done as an IPTV/Network recorder. To use it, start mythtv-setup and add a new capture card. Select "Network Recorder". Enter a URL to a m3u playlist like this one:
http://www.avenard.org/files/media/playlist.m3u (has NASA TV, Al Jazeera and France 24, I intend to expand it).

The playlist has the same format as your usual m3u playlist, except that it contains link to a HLS m3u8 URL.

Known issue: Changing channel in LiveTV doesn't work for some reasons, I believe the issue comes from tuning taking too much time. This will be investigated later.
  • Loading branch information...
commit 17c164a19881d5401fd9ddf5a1f88f2a4941c9ad 1 parent d836725
@jyavenard jyavenard authored
View
1  mythtv/libs/libmythtv/HLS/httplivestreambuffer.h
@@ -59,6 +59,7 @@ class HLSRingBuffer : public RingBuffer
static bool TestForHTTPLiveStreaming(QString &filename);
bool SaveToDisk(QString filename, int segstart = 0, int segend = -1);
int NumStreams(void) const;
+ int Read(void *data, uint i_read) { return safe_read(data, i_read); }
protected:
virtual int safe_read(void *data, uint i_read);
View
124 mythtv/libs/libmythtv/iptv/iptvfeederhls.cpp
@@ -0,0 +1,124 @@
+//
+// iptvfeederhls.cpp
+// MythTV
+//
+// Created by Jean-Yves Avenard on 24/05/12.
+// Copyright (c) 2012 Bubblestuff Pty Ltd. All rights reserved.
+//
+
+#include "iptvfeederhls.h"
+
+// MythTV headers
+#include "mythlogging.h"
+#include "streamlisteners.h"
+
+#include "HLS/httplivestreambuffer.h"
+#define LOC QString("IPTVHLS: ")
+
+#define TS_SIZE 188
+
+// BUFFER_SIZE must be a multiple of TS_SIZE, otherwise the IPTVRecorder will
+// choke on it
+#define BUFFER_SIZE (128 * TS_SIZE)
+
+IPTVFeederHLS::IPTVFeederHLS() :
+ m_buffer(new uint8_t[BUFFER_SIZE]), m_hls(NULL),
+ m_interrupted(false), m_running(false)
+{
+}
+
+IPTVFeederHLS::~IPTVFeederHLS()
+{
+ Close();
+ delete[] m_buffer;
+}
+
+bool IPTVFeederHLS::IsHLS(const QString &url)
+{
+ QString turl = url;
+ return HLSRingBuffer::TestForHTTPLiveStreaming(turl);
+}
+
+bool IPTVFeederHLS::IsOpen(void) const
+{
+ if (m_hls == NULL)
+ return false;
+ return m_hls->IsOpen();
+}
+
+bool IPTVFeederHLS::Open(const QString &url)
+{
+ LOG(VB_RECORD, LOG_INFO, LOC + QString("Open(%1) -- begin").arg(url));
+
+ QMutexLocker locker(&_lock);
+
+ if (m_hls)
+ {
+ LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- end 1");
+ return true;
+ }
+
+ m_hls = new HLSRingBuffer(url);
+
+ LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- end");
+
+ return true;
+}
+
+void IPTVFeederHLS::Run(void)
+{
+ m_lock.lock();
+ m_running = true;
+ m_interrupted = false;
+ m_lock.unlock();
+
+ while (!m_interrupted)
+ {
+ if (m_hls == NULL)
+ break;
+ m_lock.lock();
+ uint size = m_hls->Read((void *)m_buffer, BUFFER_SIZE);
+ if (m_buffer[0] != 0x47)
+ {
+ LOG(VB_RECORD, LOG_INFO, LOC +
+ QString("Packet not starting with SYNC Byte (got 0x%1)")
+ .arg((char)m_buffer[0], 2, QLatin1Char('0')));
+ }
+
+ _lock.lock();
+ vector<TSDataListener*>::iterator it = _listeners.begin();
+ for (; it != _listeners.end(); ++it)
+ (*it)->AddData(m_buffer, size);
+ _lock.unlock();
+ m_lock.unlock();
+ }
+
+ m_running = false;
+ m_lock.lock();
+ m_waitcond.wakeAll();
+ m_lock.unlock();
+}
+
+void IPTVFeederHLS::Stop(void)
+{
+ LOG(VB_RECORD, LOG_INFO, LOC + "Stop() -- begin");
+ QMutexLocker locker(&m_lock);
+ m_interrupted = true;
+
+ while (m_running)
+ m_waitcond.wait(&m_lock, 500);
+
+ LOG(VB_RECORD, LOG_INFO, LOC + "Stop() -- end");
+}
+
+void IPTVFeederHLS::Close(void)
+{
+ LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- begin");
+ Stop();
+
+ QMutexLocker lock(&m_lock);
+ delete m_hls;
+ m_hls = NULL;
+
+ LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- end");
+}
View
44 mythtv/libs/libmythtv/iptv/iptvfeederhls.h
@@ -0,0 +1,44 @@
+//
+// iptvfeederhls.h
+// MythTV
+//
+// Created by Jean-Yves Avenard on 24/05/12.
+// Copyright (c) 2012 Bubblestuff Pty Ltd. All rights reserved.
+//
+
+#ifndef MythTV_iptvfeederhls_h
+#define MythTV_iptvfeederhls_h
+
+// MythTV headers
+#include "mythcorecontext.h"
+#include "iptvfeederlive.h"
+
+class TSDataListener;
+class HLSRingBuffer;
+
+class IPTVFeederHLS : public IPTVFeederLive
+{
+public:
+ IPTVFeederHLS();
+ virtual ~IPTVFeederHLS();
+
+ bool CanHandle(const QString &url) const { return IsHLS(url); }
+ bool IsOpen(void) const;
+
+ bool Open(const QString &url);
+ void Stop(void);
+ void Close(void);
+ void Run(void);
+
+ static bool IsHLS(const QString &url);
+
+private:
+ uint8_t *m_buffer;
+ HLSRingBuffer *m_hls;
+ mutable QMutex m_lock;
+ QWaitCondition m_waitcond;
+ bool m_interrupted;
+ bool m_running;
+};
+
+#endif
View
46 mythtv/libs/libmythtv/iptv/iptvfeederlive.cpp
@@ -91,3 +91,49 @@ void IPTVFeederLive::Stop(void)
_cond.wait(&_lock, 500);
LOG(VB_RECORD, LOG_INFO, LOC + "Stop() -- end");
}
+
+void IPTVFeederLive::AddListener(TSDataListener *item)
+{
+ LOG(VB_RECORD, LOG_INFO, LOC + QString("AddListener(0x%1) -- begin")
+ .arg((uint64_t)item,0,16));
+ if (!item)
+ {
+ LOG(VB_RECORD, LOG_INFO, LOC + QString("AddListener(0x%1) -- end")
+ .arg((uint64_t)item,0,16));
+ return;
+ }
+
+ // avoid duplicates
+ RemoveListener(item);
+
+ // add to local list
+ QMutexLocker locker(&_lock);
+ _listeners.push_back(item);
+
+ LOG(VB_RECORD, LOG_INFO, LOC + QString("AddListener(0x%1) -- end")
+ .arg((uint64_t)item,0,16));
+}
+
+void IPTVFeederLive::RemoveListener(TSDataListener *item)
+{
+ LOG(VB_RECORD, LOG_INFO, LOC + QString("RemoveListener(0x%1) -- begin")
+ .arg((uint64_t)item,0,16));
+ QMutexLocker locker(&_lock);
+ vector<TSDataListener*>::iterator it =
+ find(_listeners.begin(), _listeners.end(), item);
+
+ if (it == _listeners.end())
+ {
+ LOG(VB_RECORD, LOG_INFO, LOC + QString("RemoveListener(0x%1) -- end 1")
+ .arg((uint64_t)item,0,16));
+ return;
+ }
+
+ // remove from local list..
+ *it = *_listeners.rbegin();
+ _listeners.resize(_listeners.size() - 1);
+
+ LOG(VB_RECORD, LOG_INFO, LOC + QString("RemoveListener(0x%1) -- end 2")
+ .arg((uint64_t)item,0,16));
+}
+
View
3  mythtv/libs/libmythtv/iptv/iptvfeederlive.h
@@ -32,6 +32,9 @@ class IPTVFeederLive : public IPTVFeeder
void Run(void);
void Stop(void);
+ void AddListener(TSDataListener*);
+ void RemoveListener(TSDataListener*);
+
protected:
bool InitEnv(void);
void FreeEnv(void);
View
5 mythtv/libs/libmythtv/iptv/iptvfeederwrapper.cpp
@@ -16,6 +16,7 @@ using namespace std;
#include "iptvfeederudp.h"
#include "iptvfeederrtp.h"
#include "iptvfeederfile.h"
+#include "iptvfeederhls.h"
#include "mythcontext.h"
#include "mythlogging.h"
@@ -69,6 +70,10 @@ bool IPTVFeederWrapper::InitFeeder(const QString &url)
{
tmp_feeder = new IPTVFeederFile();
}
+ else if (IPTVFeederHLS::IsHLS(url))
+ {
+ tmp_feeder = new IPTVFeederHLS();
+ }
else
{
LOG(VB_RECORD, LOG_ERR, LOC +
View
2  mythtv/libs/libmythtv/libmythtv.pro
@@ -567,6 +567,7 @@ using_backend {
HEADERS += iptv/iptvfeederrtsp.h iptv/iptvfeederudp.h
HEADERS += iptv/iptvfeederfile.h iptv/iptvfeederlive.h
HEADERS += iptv/iptvfeederrtp.h iptv/timeoutedtaskscheduler.h
+ HEADERS += iptv/iptvfeederhls.h
SOURCES += iptvchannel.cpp iptvrecorder.cpp
SOURCES += iptvsignalmonitor.cpp
@@ -576,6 +577,7 @@ using_backend {
SOURCES += iptv/iptvfeederrtsp.cpp iptv/iptvfeederudp.cpp
SOURCES += iptv/iptvfeederfile.cpp iptv/iptvfeederlive.cpp
SOURCES += iptv/iptvfeederrtp.cpp iptv/timeoutedtaskscheduler.cpp
+ SOURCES += iptv/iptvfeederhls.cpp
DEFINES += USING_IPTV
}
View
1  mythtv/libs/libmythtv/signalmonitor.h
@@ -52,6 +52,7 @@ class SignalMonitor : protected MThread
bool HasFlags(uint64_t _flags) const;
bool HasAnyFlag(uint64_t _flags) const;
uint64_t GetFlags(void) const { return flags; }
+ virtual bool HasExtraSlowTuning(void) const { return false; }
// // // // // // // // // // // // // // // // // // // // // // // //
// Gets // // // // // // // // // // // // // // // // // // // // //
View
4 mythtv/libs/libmythtv/tv_rec.cpp
@@ -1967,7 +1967,9 @@ bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
}
signalMonitor->AddListener(this);
- signalMonitor->SetUpdateRate(kSignalMonitoringRate);
+ signalMonitor->SetUpdateRate(signalMonitor->HasExtraSlowTuning() ?
+ kSignalMonitoringRate * 5 :
+ kSignalMonitoringRate);
signalMonitor->SetNotifyFrontend(notify);
// Start the monitoring thread
Please sign in to comment.
Something went wrong with that request. Please try again.