Skip to content

Commit

Permalink
Add HTTP Live Streaming recorder.
Browse files Browse the repository at this point in the history
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
jyavenard committed May 25, 2012
1 parent d836725 commit 17c164a
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 1 deletion.
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/HLS/httplivestreambuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
124 changes: 124 additions & 0 deletions mythtv/libs/libmythtv/iptv/iptvfeederhls.cpp
Original file line number Diff line number Diff line change
@@ -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");
}
44 changes: 44 additions & 0 deletions mythtv/libs/libmythtv/iptv/iptvfeederhls.h
Original file line number Diff line number Diff line change
@@ -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
46 changes: 46 additions & 0 deletions mythtv/libs/libmythtv/iptv/iptvfeederlive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

3 changes: 3 additions & 0 deletions mythtv/libs/libmythtv/iptv/iptvfeederlive.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions mythtv/libs/libmythtv/iptv/iptvfeederwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 +
Expand Down
2 changes: 2 additions & 0 deletions mythtv/libs/libmythtv/libmythtv.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/signalmonitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 // // // // // // // // // // // // // // // // // // // // //
Expand Down
4 changes: 3 additions & 1 deletion mythtv/libs/libmythtv/tv_rec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 17c164a

Please sign in to comment.