Implemented remuxing of recordings (closes #1892)

This commit is contained in:
Frank Schmirler 2014-09-07 02:48:07 +02:00
commit e83c9d92aa
12 changed files with 201 additions and 86 deletions

View File

@ -1,6 +1,7 @@
VDR Plugin 'streamdev' Revision History VDR Plugin 'streamdev' Revision History
--------------------------------------- ---------------------------------------
- Implemented remuxing of recordings
- Make ChannelChange retune only if CA IDs changed (thanks to Oliver Wagner) - Make ChannelChange retune only if CA IDs changed (thanks to Oliver Wagner)
- Implemented VDR 2.1.4 cStatus::ChannelChange(...) - Implemented VDR 2.1.4 cStatus::ChannelChange(...)
- Call detach only if receiver is attached - Call detach only if receiver is attached

View File

@ -3,6 +3,7 @@
#include "server/connection.h" #include "server/connection.h"
#include "server/streamer.h" #include "server/streamer.h"
#include <vdr/channels.h> #include <vdr/channels.h>
#include <vdr/remux.h>
#include <vdr/tools.h> #include <vdr/tools.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -25,7 +26,7 @@ protected:
virtual void Action(void); virtual void Action(void);
public: public:
cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids); cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids);
virtual ~cTSExt(); virtual ~cTSExt();
void Put(const uchar *Data, int Count); void Put(const uchar *Data, int Count);
@ -34,7 +35,7 @@ public:
} // namespace Streamdev } // namespace Streamdev
using namespace Streamdev; using namespace Streamdev;
cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids): cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connection, const cChannel *Channel, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids):
m_ResultBuffer(ResultBuffer), m_ResultBuffer(ResultBuffer),
m_Active(false), m_Active(false),
m_Process(-1), m_Process(-1),
@ -73,15 +74,24 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
#define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i-- #define ADDENV(x...) if (asprintf(&env[i++], x) < 0) i--
// add channel ID, name and pids to environment // add channel ID, name and pids to environment
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString()); if (Channel) {
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name()); ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
ADDENV("REMUX_VTYPE=%d", Channel->Vtype()); ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
if (Channel->Vpid()) ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
ADDENV("REMUX_VPID=%d", Channel->Vpid()); if (Channel->Vpid())
if (Channel->Ppid() != Channel->Vpid()) ADDENV("REMUX_VPID=%d", Channel->Vpid());
ADDENV("REMUX_PPID=%d", Channel->Ppid()); if (Channel->Ppid() != Channel->Vpid())
if (Channel->Tpid()) ADDENV("REMUX_PPID=%d", Channel->Ppid());
ADDENV("REMUX_TPID=%d", Channel->Tpid()); if (Channel->Tpid())
ADDENV("REMUX_TPID=%d", Channel->Tpid());
}
else if (PatPmt) {
ADDENV("REMUX_VTYPE=%d", PatPmt->Vtype());
if (PatPmt->Vpid())
ADDENV("REMUX_VPID=%d", PatPmt->Vpid());
if (PatPmt->Ppid() != PatPmt->Vpid())
ADDENV("REMUX_PPID=%d", PatPmt->Ppid());
}
std::string buffer; std::string buffer;
if (Apids && *Apids) { if (Apids && *Apids) {
@ -92,9 +102,16 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
buffer.clear(); buffer.clear();
for (const int *pid = Apids; *pid; pid++) { for (const int *pid = Apids; *pid; pid++) {
int j; int j;
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++) if (Channel) {
; for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : ""); ;
(buffer += Channel->Alang(j)) += (*(pid + 1) ? " " : "");
}
else if (PatPmt) {
for (j = 0; PatPmt->Apid(j) && PatPmt->Apid(j) != *pid; j++)
;
(buffer += PatPmt->Alang(j)) += (*(pid + 1) ? " " : "");
}
} }
ADDENV("REMUX_ALANG=%s", buffer.c_str()); ADDENV("REMUX_ALANG=%s", buffer.c_str());
} }
@ -108,14 +125,21 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
buffer.clear(); buffer.clear();
for (const int *pid = Dpids; *pid; pid++) { for (const int *pid = Dpids; *pid; pid++) {
int j; int j;
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++) if (Channel) {
; for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : ""); ;
(buffer += Channel->Dlang(j)) += (*(pid + 1) ? " " : "");
}
else if (PatPmt) {
for (j = 0; PatPmt->Dpid(j) && PatPmt->Dpid(j) != *pid; j++)
;
(buffer += PatPmt->Dlang(j)) += (*(pid + 1) ? " " : "");
}
} }
ADDENV("REMUX_DLANG=%s", buffer.c_str()); ADDENV("REMUX_DLANG=%s", buffer.c_str());
} }
if (Channel->Spid(0)) { if (Channel && Channel->Spid(0)) {
buffer.clear(); buffer.clear();
for (const int *pid = Channel->Spids(); *pid; pid++) for (const int *pid = Channel->Spids(); *pid; pid++)
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : ""); (buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
@ -126,6 +150,17 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
(buffer += Channel->Slang(j)) += (Channel->Spid(j + 1) ? " " : ""); (buffer += Channel->Slang(j)) += (Channel->Spid(j + 1) ? " " : "");
ADDENV("REMUX_SLANG=%s", buffer.c_str()); ADDENV("REMUX_SLANG=%s", buffer.c_str());
} }
else if (PatPmt && PatPmt->Spid(0)) {
buffer.clear();
for (const int *pid = PatPmt->Spids(); *pid; pid++)
(buffer += (const char *) itoa(*pid)) += (*(pid + 1) ? " " : "");
ADDENV("REMUX_SPID=%s", buffer.c_str());
buffer.clear();
for (int j = 0; PatPmt->Spid(j); j++)
(buffer += PatPmt->Slang(j)) += (PatPmt->Spid(j + 1) ? " " : "");
ADDENV("REMUX_SLANG=%s", buffer.c_str());
}
if (Connection) { if (Connection) {
// add vars for a CGI like interface // add vars for a CGI like interface
@ -296,7 +331,13 @@ void cTSExt::Put(const uchar *Data, int Count)
cExternRemux::cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids): cExternRemux::cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *Apids, const int *Dpids):
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)), m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)),
m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, Apids, Dpids)) m_Remux(new cTSExt(m_ResultBuffer, Connection, Channel, NULL, Apids, Dpids))
{
m_ResultBuffer->SetTimeouts(500, 100);
}
cExternRemux::cExternRemux(const cServerConnection *Connection, const cPatPmtParser *PatPmt, const int *Apids, const int *Dpids):
m_ResultBuffer(new cRingBufferLinear(WRITERBUFSIZE)),
m_Remux(new cTSExt(m_ResultBuffer, Connection, NULL, PatPmt, Apids, Dpids))
{ {
m_ResultBuffer->SetTimeouts(500, 100); m_ResultBuffer->SetTimeouts(500, 100);
} }

View File

@ -6,6 +6,7 @@
#include <string> #include <string>
class cChannel; class cChannel;
class cPatPmtParser;
class cServerConnection; class cServerConnection;
namespace Streamdev { namespace Streamdev {
@ -19,6 +20,7 @@ private:
public: public:
cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids); cExternRemux(const cServerConnection *Connection, const cChannel *Channel, const int *APids, const int *Dpids);
cExternRemux(const cServerConnection *Connection, const cPatPmtParser *PatPmt, const int *APids, const int *Dpids);
virtual ~cExternRemux(); virtual ~cExternRemux();
int Put(const uchar *Data, int Count); int Put(const uchar *Data, int Count);

View File

@ -35,6 +35,7 @@ cConnectionHTTP::cConnectionHTTP(void):
cConnectionHTTP::~cConnectionHTTP() cConnectionHTTP::~cConnectionHTTP()
{ {
SetStreamer(NULL);
delete m_RecPlayer; delete m_RecPlayer;
delete m_MenuList; delete m_MenuList;
} }
@ -164,6 +165,14 @@ bool cConnectionHTTP::ProcessRequest(void)
} }
} }
tStrStrMap::const_iterator it;
it = m_Params.find("apid");
if (it != m_Params.end())
m_Apid[0] = atoi(it->second.c_str());
it = m_Params.find("dpid");
if (it != m_Params.end())
m_Dpid[0] = atoi(it->second.c_str());
tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD); tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD);
tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO); tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO);
if (it_method == Headers().end() || it_pathinfo == Headers().end()) { if (it_method == Headers().end() || it_pathinfo == Headers().end()) {
@ -196,30 +205,46 @@ bool cConnectionHTTP::ProcessRequest(void)
} }
else if (m_RecPlayer != NULL) { else if (m_RecPlayer != NULL) {
Dprintf("GET recording\n"); Dprintf("GET recording\n");
bool isPes = m_RecPlayer->getCurrentRecording()->IsPesRecording();
// no remuxing for old PES recordings
if (isPes && m_StreamType != stPES)
return HttpResponse(503, true);
int64_t from, to; int64_t from, to;
bool hasRange = ParseRange(from, to); bool hasRange = ParseRange(from, to);
cStreamdevRecStreamer* recStreamer; cStreamdevRecStreamer* recStreamer;
if (from == 0 && hasRange && m_ReplayFakeRange) { if (from == 0 && hasRange && m_ReplayFakeRange) {
recStreamer = new cStreamdevRecStreamer(m_RecPlayer, this); recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, (int64_t) 0L, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
from += m_ReplayPos; from += m_ReplayPos;
if (to >= 0) if (to >= 0)
to += m_ReplayPos; to += m_ReplayPos;
} }
else else
recStreamer = new cStreamdevRecStreamer(m_RecPlayer, this, m_ReplayPos); recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
SetStreamer(recStreamer); SetStreamer(recStreamer);
if (m_StreamType == stEXT)
return Respond("HTTP/1.0 200 OK");
else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))
return HttpResponse(200, false, "audio/mpeg");
const char* contentType = (isPes || m_RecPlayer->getPatPmtData()->Vpid()) ? "video/mpeg" : "audio/mpeg";
// range not supported when remuxing
if (m_StreamType != stTS && !isPes)
return HttpResponse(200, false, contentType);
uint64_t total = recStreamer->GetLength(); uint64_t total = recStreamer->GetLength();
if (hasRange) { if (hasRange) {
int64_t length = recStreamer->SetRange(from, to); int64_t length = recStreamer->SetRange(from, to);
Dprintf("range response: %lld-%lld/%lld, len %lld\n", (long long)from, (long long)to, (long long)total, (long long)length); Dprintf("range response: %lld-%lld/%lld, len %lld\n", (long long)from, (long long)to, (long long)total, (long long)length);
if (length < 0L) if (length < 0L)
return HttpResponse(416, true, "video/mpeg", "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total); return HttpResponse(416, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
else else
return HttpResponse(206, false, "video/mpeg", "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length); return HttpResponse(206, false, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length);
} }
else else
return HttpResponse(200, false, "video/mpeg", "Accept-Ranges: bytes"); return HttpResponse(200, false, contentType, "Accept-Ranges: bytes");
} }
else { else {
return HttpResponse(404, true); return HttpResponse(404, true);
@ -247,29 +272,45 @@ bool cConnectionHTTP::ProcessRequest(void)
} }
else if (m_RecPlayer != NULL) { else if (m_RecPlayer != NULL) {
Dprintf("HEAD recording\n"); Dprintf("HEAD recording\n");
bool isPes = m_RecPlayer->getCurrentRecording()->IsPesRecording();
// no remuxing for old PES recordings
if (isPes && m_StreamType != stPES)
return HttpResponse(503, true);
int64_t from, to; int64_t from, to;
bool hasRange = ParseRange(from, to); bool hasRange = ParseRange(from, to);
cStreamdevRecStreamer* recStreamer; cStreamdevRecStreamer* recStreamer;
if (from == 0 && hasRange && m_ReplayFakeRange) { if (from == 0 && hasRange && m_ReplayFakeRange) {
recStreamer = new cStreamdevRecStreamer(m_RecPlayer, this, m_ReplayPos); recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
from += m_ReplayPos; from += m_ReplayPos;
if (to >= 0) if (to >= 0)
to += m_ReplayPos; to += m_ReplayPos;
} }
else else
recStreamer = new cStreamdevRecStreamer(m_RecPlayer, this, m_ReplayPos); recStreamer = new cStreamdevRecStreamer(this, m_RecPlayer, m_StreamType, m_ReplayPos, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
SetStreamer(recStreamer); SetStreamer(recStreamer);
if (m_StreamType == stEXT)
return Respond("HTTP/1.0 200 OK");
else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0]))
return HttpResponse(200, true, "audio/mpeg");
const char* contentType = (isPes || m_RecPlayer->getPatPmtData()->Vpid()) ? "video/mpeg" : "audio/mpeg";
// range not supported when remuxing
if (m_StreamType != stTS && !isPes)
return HttpResponse(200, false, contentType);
uint64_t total = recStreamer->GetLength(); uint64_t total = recStreamer->GetLength();
if (hasRange) { if (hasRange) {
int64_t length = recStreamer->SetRange(from, to); int64_t length = recStreamer->SetRange(from, to);
if (length < 0L) if (length < 0L)
return HttpResponse(416, true, "video/mpeg", "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total); return HttpResponse(416, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
else else
return HttpResponse(206, true, "video/mpeg", "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length); return HttpResponse(206, true, contentType, "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length);
} }
else else
return HttpResponse(200, true, "video/mpeg", "Accept-Ranges: bytes"); return HttpResponse(200, true, contentType, "Accept-Ranges: bytes");
} }
else { else {
return HttpResponse(404, true); return HttpResponse(404, true);

View File

@ -9,7 +9,6 @@
#include "remux/extern.h" #include "remux/extern.h"
#include <vdr/transfer.h> #include <vdr/transfer.h>
#include <vdr/ringbuffer.h>
#include "server/livestreamer.h" #include "server/livestreamer.h"
#include "server/setup.h" #include "server/setup.h"
@ -339,23 +338,21 @@ cStreamdevLiveStreamer::cStreamdevLiveStreamer(const cServerConnection *Connecti
cStreamdevStreamer("streamdev-livestreaming", Connection), cStreamdevStreamer("streamdev-livestreaming", Connection),
m_Priority(Priority), m_Priority(Priority),
m_NumPids(0), m_NumPids(0),
m_StreamType(StreamType),
m_Channel(Channel), m_Channel(Channel),
m_Device(NULL), m_Device(NULL),
m_Receiver(NULL), m_Receiver(NULL),
m_PatFilter(NULL), m_PatFilter(NULL),
m_Remux(NULL),
m_SwitchLive(false) m_SwitchLive(false)
{ {
m_ReceiveBuffer = new cStreamdevBuffer(LIVEBUFSIZE, TS_SIZE *2, true, "streamdev-livestreamer"), m_ReceiveBuffer = new cStreamdevBuffer(LIVEBUFSIZE, TS_SIZE *2, true, "streamdev-livestreamer"),
m_ReceiveBuffer->SetTimeouts(0, 100); m_ReceiveBuffer->SetTimeouts(0, 100);
if (Priority == IDLEPRIORITY) { if (Priority == IDLEPRIORITY) {
SetChannel(Apid, Dpid); SetChannel(StreamType, Apid, Dpid);
} }
else { else {
m_Device = SwitchDevice(Channel, Priority); m_Device = SwitchDevice(Channel, Priority);
if (m_Device) if (m_Device)
SetChannel(Apid, Dpid); SetChannel(StreamType, Apid, Dpid);
memcpy(m_Caids,Channel->Caids(),sizeof(m_Caids)); memcpy(m_Caids,Channel->Caids(),sizeof(m_Caids));
} }
} }
@ -366,7 +363,6 @@ cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
Stop(); Stop();
DELETENULL(m_PatFilter); DELETENULL(m_PatFilter);
DELETENULL(m_Receiver); DELETENULL(m_Receiver);
delete m_Remux;
delete m_ReceiveBuffer; delete m_ReceiveBuffer;
} }
@ -488,7 +484,7 @@ void cStreamdevLiveStreamer::StartReceiver(bool Force)
DELETENULL(m_Receiver); DELETENULL(m_Receiver);
} }
bool cStreamdevLiveStreamer::SetChannel(const int* Apid, const int *Dpid) bool cStreamdevLiveStreamer::SetChannel(eStreamType StreamType, const int* Apid, const int *Dpid)
{ {
Dprintf("Initializing Remuxer for full channel transfer\n"); Dprintf("Initializing Remuxer for full channel transfer\n");
//printf("ca pid: %d\n", Channel->Ca()); //printf("ca pid: %d\n", Channel->Ca());
@ -496,7 +492,7 @@ bool cStreamdevLiveStreamer::SetChannel(const int* Apid, const int *Dpid)
const int *Apids = Apid ? Apid : m_Channel->Apids(); const int *Apids = Apid ? Apid : m_Channel->Apids();
const int *Dpids = Dpid ? Dpid : m_Channel->Dpids(); const int *Dpids = Dpid ? Dpid : m_Channel->Dpids();
switch (m_StreamType) { switch (StreamType) {
case stES: case stES:
{ {
int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid(); int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid();
@ -504,22 +500,22 @@ bool cStreamdevLiveStreamer::SetChannel(const int* Apid, const int *Dpid)
pid = Apid[0]; pid = Apid[0];
else if (Dpid && Dpid[0]) else if (Dpid && Dpid[0])
pid = Dpid[0]; pid = Dpid[0];
m_Remux = new cTS2ESRemux(pid); SetRemux(new cTS2ESRemux(pid));
return SetPids(pid); return SetPids(pid);
} }
case stPES: case stPES:
m_Remux = new cTS2PESRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); SetRemux(new cTS2PESRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()));
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
#ifdef STREAMDEV_PS #ifdef STREAMDEV_PS
case stPS: case stPS:
m_Remux = new cTS2PSRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); SetRemux(new cTS2PSRemux(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()));
return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids());
#endif #endif
case stEXT: case stEXT:
m_Remux = new cExternRemux(Connection(), m_Channel, Apids, Dpids); SetRemux(new cExternRemux(Connection(), m_Channel, Apids, Dpids));
// fall through // fall through
case stTS: case stTS:
// This should never happen, but ... // This should never happen, but ...
@ -574,36 +570,14 @@ int cStreamdevLiveStreamer::Put(const uchar *Data, int Count)
int siCount; int siCount;
uchar *siData = m_PatFilter->Get(siCount); uchar *siData = m_PatFilter->Get(siCount);
if (siData) { if (siData) {
if (m_Remux) siCount = cStreamdevStreamer::Put(siData, siCount);
siCount = m_Remux->Put(siData, siCount);
else
siCount = cStreamdevStreamer::Put(siData, siCount);
if (siCount) if (siCount)
m_PatFilter->Del(siCount); m_PatFilter->Del(siCount);
} }
} }
if (m_Remux) return cStreamdevStreamer::Put(Data, Count);
return m_Remux->Put(Data, Count);
else
return cStreamdevStreamer::Put(Data, Count);
} }
uchar *cStreamdevLiveStreamer::Get(int &Count)
{
if (m_Remux)
return m_Remux->Get(Count);
else
return cStreamdevStreamer::Get(Count);
}
void cStreamdevLiveStreamer::Del(int Count)
{
if (m_Remux)
m_Remux->Del(Count);
else
cStreamdevStreamer::Del(Count);
}
void cStreamdevLiveStreamer::Attach(void) void cStreamdevLiveStreamer::Attach(void)
{ {
Dprintf("cStreamdevLiveStreamer::Attach()\n"); Dprintf("cStreamdevLiveStreamer::Attach()\n");

View File

@ -11,9 +11,6 @@
#define LIVEBUFSIZE (20000 * TS_SIZE) #define LIVEBUFSIZE (20000 * TS_SIZE)
namespace Streamdev {
class cTSRemux;
}
class cStreamdevPatFilter; class cStreamdevPatFilter;
class cStreamdevLiveReceiver; class cStreamdevLiveReceiver;
@ -29,13 +26,11 @@ private:
int m_Pids[MAXRECEIVEPIDS + 1]; int m_Pids[MAXRECEIVEPIDS + 1];
int m_NumPids; int m_NumPids;
int m_Caids[MAXCAIDS + 1]; int m_Caids[MAXCAIDS + 1];
eStreamType m_StreamType;
const cChannel *m_Channel; const cChannel *m_Channel;
cDevice *m_Device; cDevice *m_Device;
cStreamdevLiveReceiver *m_Receiver; cStreamdevLiveReceiver *m_Receiver;
cStreamdevBuffer *m_ReceiveBuffer; cStreamdevBuffer *m_ReceiveBuffer;
cStreamdevPatFilter *m_PatFilter; cStreamdevPatFilter *m_PatFilter;
Streamdev::cTSRemux *m_Remux;
bool m_SwitchLive; bool m_SwitchLive;
void StartReceiver(bool Force = false); void StartReceiver(bool Force = false);
@ -48,7 +43,7 @@ private:
/* Find a suitable device and tune it to the requested channel. */ /* Find a suitable device and tune it to the requested channel. */
cDevice *SwitchDevice(const cChannel *Channel, int Priority); cDevice *SwitchDevice(const cChannel *Channel, int Priority);
bool SetChannel(const int* Apid = NULL, const int* Dpid = NULL); bool SetChannel(eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
protected: protected:
virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); } virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); }
@ -72,9 +67,6 @@ public:
void Receive(uchar *Data, int Length); void Receive(uchar *Data, int Length);
virtual bool IsReceiving(void) const; virtual bool IsReceiving(void) const;
virtual uchar *Get(int &Count);
virtual void Del(int Count);
virtual void Attach(void); virtual void Attach(void);
virtual void Detach(void); virtual void Detach(void);

View File

@ -42,6 +42,12 @@ RecPlayer::RecPlayer(const char* FileName)
if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!"); if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!");
scan(); scan();
parser = new cPatPmtParser();
unsigned char buffer[2 * TS_SIZE];
unsigned long l = getBlock(buffer, 0UL, sizeof(buffer));
if (!l || !parser->ParsePatPmt(buffer, (int) l))
esyslog("ERROR: Streamdev: Failed to parse PAT/PMT");
} }
void RecPlayer::scan() void RecPlayer::scan()
@ -87,6 +93,7 @@ RecPlayer::~RecPlayer()
if (file) fclose(file); if (file) fclose(file);
delete indexFile; delete indexFile;
delete recording; delete recording;
delete parser;
} }
int RecPlayer::openFile(int index) int RecPlayer::openFile(int index)

View File

@ -23,6 +23,7 @@
#include <stdio.h> #include <stdio.h>
#include <vdr/recording.h> #include <vdr/recording.h>
#include <vdr/remux.h>
#include "server/streamer.h" #include "server/streamer.h"
@ -44,6 +45,7 @@ class RecPlayer
int openFile(int index); int openFile(int index);
uint64_t getLastPosition(); uint64_t getLastPosition();
cRecording* getCurrentRecording(); cRecording* getCurrentRecording();
const cPatPmtParser* getPatPmtData() { return parser; }
void scan(); void scan();
uint64_t positionFromResume(int ResumeID); uint64_t positionFromResume(int ResumeID);
uint64_t positionFromMark(int MarkIndex); uint64_t positionFromMark(int MarkIndex);
@ -56,6 +58,7 @@ class RecPlayer
private: private:
cRecording* recording; cRecording* recording;
cIndexFile* indexFile; cIndexFile* indexFile;
cPatPmtParser* parser;
FILE* file; FILE* file;
int fileOpen; int fileOpen;
Segment* segments[1000]; Segment* segments[1000];

View File

@ -12,7 +12,7 @@ using namespace Streamdev;
// --- cStreamdevRecStreamer ------------------------------------------------- // --- cStreamdevRecStreamer -------------------------------------------------
cStreamdevRecStreamer::cStreamdevRecStreamer(RecPlayer *RecPlayer, const cServerConnection *Connection, int64_t StartOffset): cStreamdevRecStreamer::cStreamdevRecStreamer(const cServerConnection *Connection, RecPlayer *RecPlayer, eStreamType StreamType, int64_t StartOffset, const int *Apid, const int *Dpid):
cStreamdevStreamer("streamdev-recstreaming", Connection), cStreamdevStreamer("streamdev-recstreaming", Connection),
m_RecPlayer(RecPlayer), m_RecPlayer(RecPlayer),
m_StartOffset(StartOffset), m_StartOffset(StartOffset),
@ -20,6 +20,36 @@ cStreamdevRecStreamer::cStreamdevRecStreamer(RecPlayer *RecPlayer, const cServer
{ {
Dprintf("New rec streamer\n"); Dprintf("New rec streamer\n");
m_To = (int64_t) m_RecPlayer->getLengthBytes() - StartOffset - 1; m_To = (int64_t) m_RecPlayer->getLengthBytes() - StartOffset - 1;
const cPatPmtParser *parser = RecPlayer->getPatPmtData();
const int *Apids = Apid ? Apid : parser->Apids();
const int *Dpids = Dpid ? Dpid : parser->Dpids();
switch (StreamType) {
case stES:
{
int pid = parser->Vpid();
if (Apid && Apid[0])
pid = Apid[0];
else if (Dpid && Dpid[0])
pid = Dpid[0];
SetRemux(new cTS2ESRemux(pid));
}
break;
case stPES:
if (!m_RecPlayer->getCurrentRecording()->IsPesRecording())
SetRemux(new cTS2PESRemux(parser->Vpid(), Apids, Dpids, parser->Spids()));
break;
#ifdef STREAMDEV_PS
case stPS:
SetRemux(new cTS2PSRemux(parser->Vpid(), Apids, Dpids, parser->Spids()));
break;
#endif
case stEXT:
SetRemux(new cExternRemux(Connection, parser, Apids, Dpids));
break;
default:
break;
}
} }
cStreamdevRecStreamer::~cStreamdevRecStreamer() cStreamdevRecStreamer::~cStreamdevRecStreamer()

View File

@ -1,6 +1,7 @@
#ifndef VDR_STREAMDEV_RECSTREAMER_H #ifndef VDR_STREAMDEV_RECSTREAMER_H
#define VDR_STREAMDEV_RECSTREAMER_H #define VDR_STREAMDEV_RECSTREAMER_H
#include "common.h"
#include "server/streamer.h" #include "server/streamer.h"
#include "server/recplayer.h" #include "server/recplayer.h"
@ -26,7 +27,7 @@ public:
uint64_t GetLength() { return m_RecPlayer->getLengthBytes() - m_StartOffset; } uint64_t GetLength() { return m_RecPlayer->getLengthBytes() - m_StartOffset; }
int64_t SetRange(int64_t &From, int64_t &To); int64_t SetRange(int64_t &From, int64_t &To);
virtual cString ToText() const; virtual cString ToText() const;
cStreamdevRecStreamer(RecPlayer *RecPlayer, const cServerConnection *Connection, int64_t StartOffset = 0L); cStreamdevRecStreamer(const cServerConnection *Connection, RecPlayer *RecPlayer, eStreamType StreamType, int64_t StartOffset = 0L, const int *Apids = NULL, const int *Dpids = NULL);
virtual ~cStreamdevRecStreamer(); virtual ~cStreamdevRecStreamer();
}; };

View File

@ -8,7 +8,6 @@
#include <unistd.h> #include <unistd.h>
#include "server/streamer.h" #include "server/streamer.h"
#include "server/suspend.h"
#include "tools/socket.h" #include "tools/socket.h"
#include "tools/select.h" #include "tools/select.h"
#include "common.h" #include "common.h"
@ -108,21 +107,37 @@ void cStreamdevWriter::Action(void)
Dprintf("Max. Transmit Blocksize was: %d\n", max); Dprintf("Max. Transmit Blocksize was: %d\n", max);
} }
// --- cRemuxDummy ------------------------------------------------------------
class cRemuxDummy: public Streamdev::cTSRemux {
private:
cStreamdevBuffer m_Buffer;
public:
cRemuxDummy();
virtual int Put(const uchar *Data, int Count) { return m_Buffer.Put(Data, Count); }
virtual uchar *Get(int& Count) { return m_Buffer.Get(Count); }
virtual void Del(int Count) { return m_Buffer.Del(Count); }
};
cRemuxDummy::cRemuxDummy(): m_Buffer(WRITERBUFSIZE, TS_SIZE * 2)
{
m_Buffer.SetTimeouts(100, 100);
}
// --- cStreamdevStreamer ----------------------------------------------------- // --- cStreamdevStreamer -----------------------------------------------------
cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection): cStreamdevStreamer::cStreamdevStreamer(const char *Name, const cServerConnection *Connection):
cThread(Name), cThread(Name),
m_Connection(Connection), m_Connection(Connection),
m_Writer(NULL), m_Remux(new cRemuxDummy()),
m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2)) m_Writer(NULL)
{ {
m_SendBuffer->SetTimeouts(100, 100);
} }
cStreamdevStreamer::~cStreamdevStreamer() cStreamdevStreamer::~cStreamdevStreamer()
{ {
Dprintf("Desctructing streamer\n"); Dprintf("Desctructing streamer\n");
delete m_SendBuffer; delete m_Remux;
} }
void cStreamdevStreamer::Start(cTBSocket *Socket) void cStreamdevStreamer::Start(cTBSocket *Socket)
@ -163,3 +178,7 @@ void cStreamdevStreamer::Action(void)
} }
} }
int cStreamdevStreamer::Put(const uchar *Data, int Count) {
return m_Remux->Put(Data, Count);
}

View File

@ -9,6 +9,8 @@
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
#include <vdr/tools.h> #include <vdr/tools.h>
#include "remux/tsremux.h"
class cTBSocket; class cTBSocket;
class cStreamdevStreamer; class cStreamdevStreamer;
class cServerConnection; class cServerConnection;
@ -65,16 +67,18 @@ public:
class cStreamdevStreamer: public cThread { class cStreamdevStreamer: public cThread {
private: private:
const cServerConnection *m_Connection; const cServerConnection *m_Connection;
cStreamdevWriter *m_Writer; Streamdev::cTSRemux *m_Remux;
cStreamdevBuffer *m_SendBuffer; cStreamdevWriter *m_Writer;
cStreamdevBuffer *m_SendBuffer;
protected: protected:
virtual uchar* GetFromReceiver(int &Count) = 0; virtual uchar* GetFromReceiver(int &Count) = 0;
virtual void DelFromReceiver(int Count) = 0; virtual void DelFromReceiver(int Count) = 0;
virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->PutTS(Data, Count); } virtual int Put(const uchar *Data, int Count);
virtual void Action(void); virtual void Action(void);
bool IsRunning(void) const { return m_Writer; } bool IsRunning(void) const { return m_Writer; }
void SetRemux(Streamdev::cTSRemux *Remux) { delete m_Remux; m_Remux = Remux; }
public: public:
cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL); cStreamdevStreamer(const char *Name, const cServerConnection *Connection = NULL);
@ -87,8 +91,8 @@ public:
virtual bool IsReceiving(void) const = 0; virtual bool IsReceiving(void) const = 0;
bool Abort(void); bool Abort(void);
virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); } uchar *Get(int &Count) { return m_Remux->Get(Count); }
virtual void Del(int Count) { m_SendBuffer->Del(Count); } void Del(int Count) { m_Remux->Del(Count); }
virtual void Detach(void) {} virtual void Detach(void) {}
virtual void Attach(void) {} virtual void Attach(void) {}