mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
Basic support for HTTP streaming of recordings
This commit is contained in:
parent
0cf406ed3a
commit
9135cde712
@ -208,3 +208,6 @@ Methodus
|
|||||||
|
|
||||||
Uwe
|
Uwe
|
||||||
for reporting a compiler error in client/device.c with VDR < 1.7.22
|
for reporting a compiler error in client/device.c with VDR < 1.7.22
|
||||||
|
|
||||||
|
Chris Tallon
|
||||||
|
for his kind permission to use VOMP's recplayer for replaying recordings
|
||||||
|
1
HISTORY
1
HISTORY
@ -1,6 +1,7 @@
|
|||||||
VDR Plugin 'streamdev' Revision History
|
VDR Plugin 'streamdev' Revision History
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
- Basic support for HTTP streaming of recordings
|
||||||
- Close writer when streamer is finished
|
- Close writer when streamer is finished
|
||||||
- Don't abort VTP connection if filter stream is broken
|
- Don't abort VTP connection if filter stream is broken
|
||||||
- Restructured cStreamdevStreamer: Moved inbound buffer into actual subclass.
|
- Restructured cStreamdevStreamer: Moved inbound buffer into actual subclass.
|
||||||
|
@ -22,7 +22,7 @@ SERVEROBJS = $(PLUGIN).o \
|
|||||||
componentVTP.o connectionVTP.o \
|
componentVTP.o connectionVTP.o \
|
||||||
componentHTTP.o connectionHTTP.o menuHTTP.o \
|
componentHTTP.o connectionHTTP.o menuHTTP.o \
|
||||||
componentIGMP.o connectionIGMP.o \
|
componentIGMP.o connectionIGMP.o \
|
||||||
streamer.o livestreamer.o livefilter.o recplayer.o \
|
streamer.o livestreamer.o livefilter.o recstreamer.o recplayer.o \
|
||||||
menu.o suspend.o setup.o
|
menu.o suspend.o setup.o
|
||||||
|
|
||||||
### The main target:
|
### The main target:
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <vdr/thread.h>
|
||||||
|
#include <vdr/recording.h>
|
||||||
|
|
||||||
#include "server/connectionHTTP.h"
|
#include "server/connectionHTTP.h"
|
||||||
#include "server/menuHTTP.h"
|
#include "server/menuHTTP.h"
|
||||||
@ -14,9 +16,10 @@
|
|||||||
cConnectionHTTP::cConnectionHTTP(void):
|
cConnectionHTTP::cConnectionHTTP(void):
|
||||||
cServerConnection("HTTP"),
|
cServerConnection("HTTP"),
|
||||||
m_Status(hsRequest),
|
m_Status(hsRequest),
|
||||||
m_LiveStreamer(NULL),
|
m_Streamer(NULL),
|
||||||
m_Channel(NULL),
|
|
||||||
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
|
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
|
||||||
|
m_Channel(NULL),
|
||||||
|
m_Recording(NULL),
|
||||||
m_ChannelList(NULL)
|
m_ChannelList(NULL)
|
||||||
{
|
{
|
||||||
Dprintf("constructor hsRequest\n");
|
Dprintf("constructor hsRequest\n");
|
||||||
@ -26,7 +29,8 @@ cConnectionHTTP::cConnectionHTTP(void):
|
|||||||
|
|
||||||
cConnectionHTTP::~cConnectionHTTP()
|
cConnectionHTTP::~cConnectionHTTP()
|
||||||
{
|
{
|
||||||
delete m_LiveStreamer;
|
delete m_Streamer;
|
||||||
|
delete m_Recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cConnectionHTTP::CanAuthenticate(void)
|
bool cConnectionHTTP::CanAuthenticate(void)
|
||||||
@ -168,9 +172,10 @@ bool cConnectionHTTP::ProcessRequest(void)
|
|||||||
device = GetDevice(m_Channel, StreamdevServerSetup.HTTPPriority);
|
device = GetDevice(m_Channel, StreamdevServerSetup.HTTPPriority);
|
||||||
if (device != NULL) {
|
if (device != NULL) {
|
||||||
device->SwitchChannel(m_Channel, false);
|
device->SwitchChannel(m_Channel, false);
|
||||||
m_LiveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this);
|
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this);
|
||||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
|
m_Streamer = liveStreamer;
|
||||||
m_LiveStreamer->SetDevice(device);
|
if (liveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
|
||||||
|
liveStreamer->SetDevice(device);
|
||||||
if (!SetDSCP())
|
if (!SetDSCP())
|
||||||
LOG_ERROR_STR("unable to set DSCP sockopt");
|
LOG_ERROR_STR("unable to set DSCP sockopt");
|
||||||
if (m_StreamType == stEXT) {
|
if (m_StreamType == stEXT) {
|
||||||
@ -183,10 +188,26 @@ bool cConnectionHTTP::ProcessRequest(void)
|
|||||||
return HttpResponse(200, false, "video/mpeg");
|
return HttpResponse(200, false, "video/mpeg");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DELETENULL(m_LiveStreamer);
|
DELETENULL(m_Streamer);
|
||||||
}
|
}
|
||||||
return HttpResponse(503, true);
|
return HttpResponse(503, true);
|
||||||
}
|
}
|
||||||
|
else if (m_Recording != NULL) {
|
||||||
|
Dprintf("GET recording\n");
|
||||||
|
cStreamdevRecStreamer* recStreamer = new cStreamdevRecStreamer(m_Recording, this);
|
||||||
|
m_Streamer = recStreamer;
|
||||||
|
int64_t from, to;
|
||||||
|
uint64_t total = recStreamer->GetLength();
|
||||||
|
if (ParseRange(from, to)) {
|
||||||
|
int64_t length = recStreamer->SetRange(from, to);
|
||||||
|
if (length < 0L)
|
||||||
|
return HttpResponse(416, true, "video/mpeg", "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return HttpResponse(200, false, "video/mpeg", "Accept-Ranges: bytes");
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return HttpResponse(404, true);
|
return HttpResponse(404, true);
|
||||||
}
|
}
|
||||||
@ -198,8 +219,9 @@ bool cConnectionHTTP::ProcessRequest(void)
|
|||||||
else if (m_Channel != NULL) {
|
else if (m_Channel != NULL) {
|
||||||
if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
|
if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
|
||||||
if (m_StreamType == stEXT) {
|
if (m_StreamType == stEXT) {
|
||||||
m_LiveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this);
|
cStreamdevLiveStreamer *liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this);
|
||||||
m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
liveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
|
||||||
|
m_Streamer = liveStreamer;
|
||||||
return Respond("HTTP/1.0 200 OK");
|
return Respond("HTTP/1.0 200 OK");
|
||||||
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {
|
||||||
return HttpResponse(200, true, "audio/mpeg", "icy-name: %s", m_Channel->Name());
|
return HttpResponse(200, true, "audio/mpeg", "icy-name: %s", m_Channel->Name());
|
||||||
@ -211,6 +233,22 @@ bool cConnectionHTTP::ProcessRequest(void)
|
|||||||
}
|
}
|
||||||
return HttpResponse(503, true);
|
return HttpResponse(503, true);
|
||||||
}
|
}
|
||||||
|
else if (m_Recording != NULL) {
|
||||||
|
Dprintf("HEAD recording\n");
|
||||||
|
cStreamdevRecStreamer *recStreamer = new cStreamdevRecStreamer(m_Recording, this);
|
||||||
|
m_Streamer = recStreamer;
|
||||||
|
int64_t from, to;
|
||||||
|
uint64_t total = recStreamer->GetLength();
|
||||||
|
if (ParseRange(from, to)) {
|
||||||
|
int64_t length = recStreamer->SetRange(from, to);
|
||||||
|
if (length < 0L)
|
||||||
|
return HttpResponse(416, true, "video/mpeg", "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return HttpResponse(200, true, "video/mpeg", "Accept-Ranges: bytes");
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return HttpResponse(404, true);
|
return HttpResponse(404, true);
|
||||||
}
|
}
|
||||||
@ -244,9 +282,11 @@ bool cConnectionHTTP::HttpResponse(int Code, bool Last, const char* ContentType,
|
|||||||
switch (Code)
|
switch (Code)
|
||||||
{
|
{
|
||||||
case 200: rc = Respond("HTTP/1.1 200 OK"); break;
|
case 200: rc = Respond("HTTP/1.1 200 OK"); break;
|
||||||
|
case 206: rc = Respond("HTTP/1.1 206 Partial Content"); break;
|
||||||
case 400: rc = Respond("HTTP/1.1 400 Bad Request"); break;
|
case 400: rc = Respond("HTTP/1.1 400 Bad Request"); break;
|
||||||
case 401: rc = Respond("HTTP/1.1 401 Authorization Required"); break;
|
case 401: rc = Respond("HTTP/1.1 401 Authorization Required"); break;
|
||||||
case 404: rc = Respond("HTTP/1.1 404 Not Found"); break;
|
case 404: rc = Respond("HTTP/1.1 404 Not Found"); break;
|
||||||
|
case 416: rc = Respond("HTTP/1.1 416 Requested range not satisfiable"); break;
|
||||||
case 503: rc = Respond("HTTP/1.1 503 Service Unavailable"); break;
|
case 503: rc = Respond("HTTP/1.1 503 Service Unavailable"); break;
|
||||||
default: rc = Respond("HTTP/1.1 500 Internal Server Error");
|
default: rc = Respond("HTTP/1.1 500 Internal Server Error");
|
||||||
}
|
}
|
||||||
@ -279,6 +319,40 @@ bool cConnectionHTTP::HttpResponse(int Code, bool Last, const char* ContentType,
|
|||||||
return rc && Respond("");
|
return rc && Respond("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cConnectionHTTP::ParseRange(int64_t &From, int64_t &To) const
|
||||||
|
{
|
||||||
|
const static std::string RANGE("HTTP_RANGE");
|
||||||
|
From = To = 0L;
|
||||||
|
tStrStrMap::const_iterator it = Headers().find(RANGE);
|
||||||
|
if (it != Headers().end()) {
|
||||||
|
size_t b = it->second.find("bytes=");
|
||||||
|
if (b != std::string::npos) {
|
||||||
|
char* e = NULL;
|
||||||
|
const char* r = it->second.c_str() + b + sizeof("bytes=") - 1;
|
||||||
|
if (strchr(r, ',') != NULL)
|
||||||
|
esyslog("streamdev-server cConnectionHTTP::GetRange: Multi-ranges not supported");
|
||||||
|
From = strtol(r, &e, 10);
|
||||||
|
if (r != e) {
|
||||||
|
if (From < 0L) {
|
||||||
|
To = -1L;
|
||||||
|
return *e == 0 || *e == ',';
|
||||||
|
}
|
||||||
|
else if (*e == '-') {
|
||||||
|
r = e + 1;
|
||||||
|
if (*r == 0 || *e == ',') {
|
||||||
|
To = -1L;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
To = strtol(r, &e, 10);
|
||||||
|
return r != e && To >= From &&
|
||||||
|
(*e == 0 || *e == ',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void cConnectionHTTP::Flushed(void)
|
void cConnectionHTTP::Flushed(void)
|
||||||
{
|
{
|
||||||
if (m_Status != hsBody)
|
if (m_Status != hsBody)
|
||||||
@ -296,9 +370,9 @@ void cConnectionHTTP::Flushed(void)
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (m_Channel != NULL) {
|
else if (m_Streamer != NULL) {
|
||||||
Dprintf("streamer start\n");
|
Dprintf("streamer start\n");
|
||||||
m_LiveStreamer->Start(this);
|
m_Streamer->Start(this);
|
||||||
m_Status = hsFinished;
|
m_Status = hsFinished;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -401,6 +475,13 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
|||||||
if ((m_ChannelList = ChannelListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
|
if ((m_ChannelList = ChannelListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
|
||||||
Dprintf("Channel list requested\n");
|
Dprintf("Channel list requested\n");
|
||||||
return true;
|
return true;
|
||||||
|
} else if (strcmp(fileext.c_str(), ".rec") == 0) {
|
||||||
|
cThreadLock RecordingsLock(&Recordings);
|
||||||
|
cRecording* rec = Recordings.Get(atoi(filespec.c_str()) - 1);
|
||||||
|
Dprintf("Recording %s%s found\n", rec ? rec->Name() : filespec.c_str(), rec ? "" : " not");
|
||||||
|
if (rec)
|
||||||
|
m_Recording = new cRecording(rec->FileName());
|
||||||
|
return m_Recording != NULL;
|
||||||
} else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
|
} else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
|
||||||
Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
|
Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
|
||||||
return true;
|
return true;
|
||||||
@ -411,5 +492,5 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
|
|||||||
cString cConnectionHTTP::ToText() const
|
cString cConnectionHTTP::ToText() const
|
||||||
{
|
{
|
||||||
cString str = cServerConnection::ToText();
|
cString str = cServerConnection::ToText();
|
||||||
return m_LiveStreamer ? cString::sprintf("%s\t%s", *str, *m_LiveStreamer->ToText()) : str;
|
return m_Streamer ? cString::sprintf("%s\t%s", *str, *m_Streamer->ToText()) : str;
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,12 @@
|
|||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "server/livestreamer.h"
|
#include "server/livestreamer.h"
|
||||||
|
#include "server/recstreamer.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <tools/select.h>
|
#include <tools/select.h>
|
||||||
|
|
||||||
class cChannel;
|
class cChannel;
|
||||||
class cStreamdevLiveStreamer;
|
|
||||||
class cChannelList;
|
class cChannelList;
|
||||||
|
|
||||||
class cConnectionHTTP: public cServerConnection {
|
class cConnectionHTTP: public cServerConnection {
|
||||||
@ -27,12 +27,14 @@ private:
|
|||||||
std::string m_Authorization;
|
std::string m_Authorization;
|
||||||
eHTTPStatus m_Status;
|
eHTTPStatus m_Status;
|
||||||
tStrStrMap m_Params;
|
tStrStrMap m_Params;
|
||||||
|
cStreamdevStreamer *m_Streamer;
|
||||||
|
eStreamType m_StreamType;
|
||||||
// job: transfer
|
// job: transfer
|
||||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
|
||||||
const cChannel *m_Channel;
|
const cChannel *m_Channel;
|
||||||
int m_Apid[2];
|
int m_Apid[2];
|
||||||
int m_Dpid[2];
|
int m_Dpid[2];
|
||||||
eStreamType m_StreamType;
|
// job: replay
|
||||||
|
cRecording *m_Recording;
|
||||||
// job: listing
|
// job: listing
|
||||||
cChannelList *m_ChannelList;
|
cChannelList *m_ChannelList;
|
||||||
|
|
||||||
@ -40,6 +42,13 @@ private:
|
|||||||
bool ProcessURI(const std::string &PathInfo);
|
bool ProcessURI(const std::string &PathInfo);
|
||||||
bool HttpResponse(int Code, bool Last, const char* ContentType = NULL, const char* Headers = "", ...);
|
bool HttpResponse(int Code, bool Last, const char* ContentType = NULL, const char* Headers = "", ...);
|
||||||
//__attribute__ ((format (printf, 5, 6)));
|
//__attribute__ ((format (printf, 5, 6)));
|
||||||
|
/**
|
||||||
|
* Extract byte range from HTTP Range header. Returns false if no valid
|
||||||
|
* range is found. The contents of From and To are undefined in this
|
||||||
|
* case. From may be negative in which case To is undefined.
|
||||||
|
* TODO: support for multiple ranges.
|
||||||
|
*/
|
||||||
|
bool ParseRange(int64_t &From, int64_t &To) const;
|
||||||
protected:
|
protected:
|
||||||
bool ProcessRequest(void);
|
bool ProcessRequest(void);
|
||||||
|
|
||||||
@ -47,8 +56,8 @@ public:
|
|||||||
cConnectionHTTP(void);
|
cConnectionHTTP(void);
|
||||||
virtual ~cConnectionHTTP();
|
virtual ~cConnectionHTTP();
|
||||||
|
|
||||||
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
|
virtual void Attach(void) { if (m_Streamer != NULL) m_Streamer->Attach(); }
|
||||||
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
|
virtual void Detach(void) { if (m_Streamer != NULL) m_Streamer->Detach(); }
|
||||||
|
|
||||||
virtual cString ToText() const;
|
virtual cString ToText() const;
|
||||||
|
|
||||||
@ -62,7 +71,7 @@ public:
|
|||||||
|
|
||||||
inline bool cConnectionHTTP::Abort(void) const
|
inline bool cConnectionHTTP::Abort(void) const
|
||||||
{
|
{
|
||||||
return !IsOpen() || (m_LiveStreamer && m_LiveStreamer->Abort());
|
return !IsOpen() || (m_Streamer && m_Streamer->Abort());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||||
|
@ -49,7 +49,7 @@ public:
|
|||||||
bool SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
|
bool SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
|
||||||
void SetPriority(int Priority);
|
void SetPriority(int Priority);
|
||||||
void GetSignal(int *DevNum, int *Strength, int *Quality) const;
|
void GetSignal(int *DevNum, int *Strength, int *Quality) const;
|
||||||
cString ToText() const;
|
virtual cString ToText() const;
|
||||||
|
|
||||||
void Receive(uchar *Data, int Length);
|
void Receive(uchar *Data, int Length);
|
||||||
virtual bool IsReceiving(void) const;
|
virtual bool IsReceiving(void) const;
|
||||||
|
67
server/recstreamer.c
Normal file
67
server/recstreamer.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include "remux/ts2ps.h"
|
||||||
|
#include "remux/ts2pes.h"
|
||||||
|
#include "remux/ts2es.h"
|
||||||
|
#include "remux/extern.h"
|
||||||
|
|
||||||
|
#include <vdr/ringbuffer.h>
|
||||||
|
#include "server/recstreamer.h"
|
||||||
|
#include "server/connection.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
using namespace Streamdev;
|
||||||
|
|
||||||
|
// --- cStreamdevRecStreamer -------------------------------------------------
|
||||||
|
|
||||||
|
cStreamdevRecStreamer::cStreamdevRecStreamer(cRecording *Rec, const cServerConnection *Connection):
|
||||||
|
cStreamdevStreamer("streamdev-recstreaming", Connection),
|
||||||
|
m_RecPlayer(Rec),
|
||||||
|
m_From(0L)
|
||||||
|
{
|
||||||
|
Dprintf("New rec streamer\n");
|
||||||
|
m_To = (int64_t) m_RecPlayer.getLengthBytes() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cStreamdevRecStreamer::~cStreamdevRecStreamer()
|
||||||
|
{
|
||||||
|
Dprintf("Desctructing rec streamer\n");
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t cStreamdevRecStreamer::SetRange(int64_t &From, int64_t &To)
|
||||||
|
{
|
||||||
|
int64_t l = (int64_t) m_RecPlayer.getLengthBytes();
|
||||||
|
if (From < 0L) {
|
||||||
|
From += l;
|
||||||
|
if (From < 0L)
|
||||||
|
From = 0L;
|
||||||
|
To = l - 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (To < 0L)
|
||||||
|
To += l;
|
||||||
|
else if (To >= l)
|
||||||
|
To = l - 1;
|
||||||
|
if (From > To) {
|
||||||
|
// invalid range - return whole content
|
||||||
|
From = 0L;
|
||||||
|
To = l - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_From = From;
|
||||||
|
m_To = To;
|
||||||
|
return m_To - m_From + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar* cStreamdevRecStreamer::GetFromReceiver(int &Count)
|
||||||
|
{
|
||||||
|
if (m_From <= m_To) {
|
||||||
|
Count = (int) m_RecPlayer.getBlock(m_Buffer, m_From, sizeof(m_Buffer));
|
||||||
|
return m_Buffer;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cStreamdevRecStreamer::ToText() const
|
||||||
|
{
|
||||||
|
return "REPLAY";
|
||||||
|
}
|
32
server/recstreamer.h
Normal file
32
server/recstreamer.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef VDR_STREAMDEV_RECSTREAMER_H
|
||||||
|
#define VDR_STREAMDEV_RECSTREAMER_H
|
||||||
|
|
||||||
|
#include "server/streamer.h"
|
||||||
|
#include "server/recplayer.h"
|
||||||
|
|
||||||
|
#define RECBUFSIZE (174 * TS_SIZE)
|
||||||
|
|
||||||
|
// --- cStreamdevRecStreamer -------------------------------------------------
|
||||||
|
|
||||||
|
class cStreamdevRecStreamer: public cStreamdevStreamer {
|
||||||
|
private:
|
||||||
|
//Streamdev::cTSRemux *m_Remux;
|
||||||
|
RecPlayer m_RecPlayer;
|
||||||
|
int64_t m_From;
|
||||||
|
int64_t m_To;
|
||||||
|
uchar m_Buffer[RECBUFSIZE];
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual uchar* GetFromReceiver(int &Count);
|
||||||
|
virtual void DelFromReceiver(int Count) { m_From += Count; };
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual bool IsReceiving(void) const { return m_From <= m_To; };
|
||||||
|
inline uint64_t GetLength() { return m_RecPlayer.getLengthBytes(); }
|
||||||
|
int64_t SetRange(int64_t &From, int64_t &To);
|
||||||
|
virtual cString ToText() const;
|
||||||
|
cStreamdevRecStreamer(cRecording *Recording, const cServerConnection *Connection);
|
||||||
|
virtual ~cStreamdevRecStreamer();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VDR_STREAMDEV_RECSTREAMER_H
|
@ -92,6 +92,8 @@ public:
|
|||||||
|
|
||||||
virtual void Detach(void) {}
|
virtual void Detach(void) {}
|
||||||
virtual void Attach(void) {}
|
virtual void Attach(void) {}
|
||||||
|
|
||||||
|
virtual cString ToText() const { return ""; };
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool cStreamdevStreamer::Abort(void)
|
inline bool cStreamdevStreamer::Abort(void)
|
||||||
|
Loading…
Reference in New Issue
Block a user