mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
Implemented remuxing of recordings (closes #1892)
This commit is contained in:
commit
e83c9d92aa
1
HISTORY
1
HISTORY
@ -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
|
||||||
|
@ -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,6 +74,7 @@ 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
|
||||||
|
if (Channel) {
|
||||||
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
|
ADDENV("REMUX_CHANNEL_ID=%s", *Channel->GetChannelID().ToString());
|
||||||
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
|
ADDENV("REMUX_CHANNEL_NAME=%s", Channel->Name());
|
||||||
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
|
ADDENV("REMUX_VTYPE=%d", Channel->Vtype());
|
||||||
@ -82,6 +84,14 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
|||||||
ADDENV("REMUX_PPID=%d", Channel->Ppid());
|
ADDENV("REMUX_PPID=%d", Channel->Ppid());
|
||||||
if (Channel->Tpid())
|
if (Channel->Tpid())
|
||||||
ADDENV("REMUX_TPID=%d", 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,10 +102,17 @@ 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;
|
||||||
|
if (Channel) {
|
||||||
for (j = 0; Channel->Apid(j) && Channel->Apid(j) != *pid; j++)
|
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;
|
||||||
|
if (Channel) {
|
||||||
for (j = 0; Channel->Dpid(j) && Channel->Dpid(j) != *pid; j++)
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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 = m_Remux->Put(siData, siCount);
|
|
||||||
else
|
|
||||||
siCount = cStreamdevStreamer::Put(siData, siCount);
|
siCount = cStreamdevStreamer::Put(siData, siCount);
|
||||||
if (siCount)
|
if (siCount)
|
||||||
m_PatFilter->Del(siCount);
|
m_PatFilter->Del(siCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_Remux)
|
|
||||||
return m_Remux->Put(Data, Count);
|
|
||||||
else
|
|
||||||
return cStreamdevStreamer::Put(Data, Count);
|
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");
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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];
|
||||||
|
@ -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()
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Streamdev::cTSRemux *m_Remux;
|
||||||
cStreamdevWriter *m_Writer;
|
cStreamdevWriter *m_Writer;
|
||||||
cStreamdevBuffer *m_SendBuffer;
|
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) {}
|
||||||
|
Loading…
Reference in New Issue
Block a user