summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthlo <smarttv640@gmail.com>2013-11-26 22:11:45 +0100
committerthlo <smarttv640@gmail.com>2013-11-26 22:11:45 +0100
commit802f35d8a1a7f06dd76068d30f429da6dad9f227 (patch)
treec6cee6003b66ce7097ffeaa139edb18340e7f5ed
parent3ac54dc3c1f6dd6d8b9adc4024e5003bd4532cae (diff)
downloadvdr-plugin-smarttvweb-802f35d8a1a7f06dd76068d30f429da6dad9f227.tar.gz
vdr-plugin-smarttvweb-802f35d8a1a7f06dd76068d30f429da6dad9f227.tar.bz2
first version of the built-in live solution
-rw-r--r--Makefile2
-rwxr-xr-x[-rw-r--r--]Makefile.new2
-rwxr-xr-xhttpresource.c17
-rwxr-xr-xhttpresource.h5
-rw-r--r--httpresource_base.c46
-rwxr-xr-x[-rw-r--r--]httpresource_base.h19
-rwxr-xr-x[-rw-r--r--]log.c2
-rw-r--r--responselive.c525
-rw-r--r--responselive.h103
-rwxr-xr-xresponsememblk.c8
-rwxr-xr-xsmarttvfactory.c180
-rwxr-xr-xsmarttvfactory.h16
-rw-r--r--smarttvweb.conf13
-rwxr-xr-xstvw_cfg.c29
-rwxr-xr-xstvw_cfg.h9
15 files changed, 879 insertions, 97 deletions
diff --git a/Makefile b/Makefile
index 64148e1..61673f5 100644
--- a/Makefile
+++ b/Makefile
@@ -55,7 +55,7 @@ DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
### The object files (add further files here):
-OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o httpclient.o mngurls.o log.o url.o stvw_cfg.o responsebase.o responsefile.o responsevdrdir.o responsememblk.o
+OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o httpclient.o mngurls.o log.o url.o stvw_cfg.o responsebase.o responsefile.o responsevdrdir.o responsememblk.o responselive.o httpresource_base.o
OBJS2 =
diff --git a/Makefile.new b/Makefile.new
index 344dea4..3c007f9 100644..100755
--- a/Makefile.new
+++ b/Makefile.new
@@ -53,7 +53,7 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
### The object files (add further files here):
-OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o httpclient.o mngurls.o log.o url.o stvw_cfg.o responsebase.o responsefile.o responsevdrdir.o responsememblk.o
+OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o httpclient.o mngurls.o log.o url.o stvw_cfg.o responsebase.o responsefile.o responsevdrdir.o responsememblk.o responselive.o httpresource_base.o
### The main target:
diff --git a/httpresource.c b/httpresource.c
index c6050e3..3ca5ee7 100755
--- a/httpresource.c
+++ b/httpresource.c
@@ -46,6 +46,7 @@
#include "responsefile.h"
#include "responsevdrdir.h"
#include "responsememblk.h"
+#include "responselive.h"
#ifndef STANDALONE
#include <vdr/recording.h>
@@ -488,8 +489,9 @@ int cHttpResource::processRequest() {
if (mPath.find("/live/", 0, 6) == 0) {
*(mLog->log())<< DEBUGPREFIX
- << " Found live request. serving " << mPath << endl;
- //mResponse = new cResponseLive(this, mPath.substr(6));
+ << " Found live request. serving " << mPath
+ << endl;
+ mResponse = new cResponseLive(this, mPath.substr(6));
//((cResponseVdrDir*)mResponse)->sendMediaSegment( &statbuf);
return OKAY;
@@ -599,24 +601,23 @@ int cHttpResource::handleWrite() {
return OKAY;
}
- // if (mResponse->mBlkLen == mResponse->mBlkPos) {
if (mResponse->isBlkWritten()) {
// note the mBlk may be filled with header info first.
if (mResponse->fillDataBlk() != OKAY) {
return ERROR;
}
+ if (mResponse->mBlkLen == 0) {
+ return OKAY;
+ }
}
- // int this_write = write(mFd, &(mResponse->mBlkData[mBlkPos]), mResponse->mBlkLen - mResponse->mBlkPos);
- // int this_write = mResponse->writeData(mFd);
- // if (this_write <=0) {
if (mResponse->writeData(mFd) <=0) {
#ifndef DEBUG
*(mLog->log())<< DEBUGPREFIX
- << " ERROR after write: Stopped (Client terminated Connection)"
+ << " close in write: Stopped (Client terminated Connection)"
<< " mBlkPos= " << mResponse->mBlkPos << " mBlkLen= " << mResponse->mBlkLen
- << DEBUGHDR << endl;
+ << DEBUGHDR << endl;
#endif
mConnState = TOCLOSE;
mConnected = false;
diff --git a/httpresource.h b/httpresource.h
index e170b4f..daa75a6 100755
--- a/httpresource.h
+++ b/httpresource.h
@@ -31,7 +31,10 @@
#include "log.h"
#include "httpresource_base.h"
-#define MAXLEN 32768
+//#define MAXLEN 65536
+//#define MAXLEN 524288
+//#define MAXLEN 1048576
+#define MAXLEN 3760000
using namespace std;
diff --git a/httpresource_base.c b/httpresource_base.c
new file mode 100644
index 0000000..889bb7b
--- /dev/null
+++ b/httpresource_base.c
@@ -0,0 +1,46 @@
+/*
+ * httpresource_base.c: VDR on Smart TV plugin
+ *
+ * Copyright (C) 2013 T. Lohmar
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ */
+
+#include "httpresource_base.h"
+#include "log.h"
+
+int cHttpResourcePipe::mPipeId = 0;
+
+#define MEMBUFLEN 1000
+
+cHttpResourcePipe::cHttpResourcePipe(int f, SmartTvServer* fac) : cHttpResourceBase(f, mPipeId++, 0, fac), mLog(NULL), mBuf(NULL) {
+
+ mLog = Log::getInstance();
+ mBuf = new char[MEMBUFLEN];
+};
+
+cHttpResourcePipe::~cHttpResourcePipe() {
+ delete [] mBuf;
+};
+
+int cHttpResourcePipe::handleRead() {
+
+ int buflen = read(mFd, mBuf, MEMBUFLEN);
+ if (buflen == 0)
+ return -1;
+ return 0;
+}; // OKAY
diff --git a/httpresource_base.h b/httpresource_base.h
index 6494443..8341c8f 100644..100755
--- a/httpresource_base.h
+++ b/httpresource_base.h
@@ -41,4 +41,23 @@ class cHttpResourceBase {
int mServerPort;
};
+
+class Log;
+
+class cHttpResourcePipe : public cHttpResourceBase {
+ public:
+ cHttpResourcePipe(int f, SmartTvServer* fac);
+ virtual ~cHttpResourcePipe();
+
+ int handleRead();
+ int handleWrite() { return 0; };
+ int checkStatus() { return 0; };
+ private:
+ static int mPipeId;
+ Log* mLog;
+
+ char* mBuf;
+};
+
+
#endif
diff --git a/log.c b/log.c
index 9c0b93a..e409f4e 100644..100755
--- a/log.c
+++ b/log.c
@@ -49,7 +49,7 @@ int Log::init(string fileName) {
if (fileName != "") {
mLogFile = new ofstream();
- mLogFile->open(fileName.c_str(), ios::out );
+ mLogFile->open(fileName.c_str(), ios::out); // | ios::app
*mLogFile << "Log Created: " << timebuf << endl;
}
else
diff --git a/responselive.c b/responselive.c
new file mode 100644
index 0000000..567c6bb
--- /dev/null
+++ b/responselive.c
@@ -0,0 +1,525 @@
+/*
+ * responselive.c: VDR on Smart TV plugin
+ *
+ * Copyright (C) 2013 T. Lohmar
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ */
+/*
+ *
+ * TODO: Stop, when the channel is unavailable
+ *
+*/
+#include "responselive.h"
+#include "httpresource.h"
+#include "smarttvfactory.h"
+#include "log.h"
+
+#include <vdr/remux.h>
+
+#include <vector>
+#include <sys/stat.h>
+
+#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd
+#define OKAY 0
+#define ERROR (-1)
+#define DEBUG
+
+
+cLiveRelay::cLiveRelay(cChannel* channel, cDevice* device, string chan_id, int req_id, cHttpResource* req) : cReceiver(channel, 0),
+ mChannelId(chan_id), mRingBuffer(), mReqId(req_id), mRequest(req), mNotifFd(0), mNotifRequired(true)
+{
+
+ mLog = Log::getInstance();
+
+ pthread_mutex_init(&processedRingLock, NULL);
+
+ mNotifFd = mRequest->mFactory->openPipe();
+ if (mNotifFd == 0) {
+ *(mLog->log()) << "mReqId= " << mReqId
+ << " cLiveRelay::cLiveRelay - Constructor ERROR, mNotifId is ZERO" << endl;
+ }
+
+ SetPids(channel);
+
+ // AddPid(channel->Tpid());
+ // AddPid(channel->Vpid());
+ // AddPid(channel->Apid(0));
+
+ device->SwitchChannel(channel, false);
+ device->AttachReceiver(this);
+
+ // mRingBuffer = new cRingBufferLinear(60000*188, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE, true, "responselive");
+ mRingBuffer = new cRingBufferLinear(60000*188);
+ mRingBuffer->SetTimeouts(0, 100);
+
+#ifndef DEBUG
+ *(mLog->log()) << "mReqId= " << mReqId << " cLiveRelay::cLiveRelay created for chan_id= " << chan_id << endl;
+#endif
+};
+
+cLiveRelay::~cLiveRelay() {
+ close(mNotifFd);
+ cReceiver::Detach();
+ delete mRingBuffer;
+};
+
+void cLiveRelay::Activate(bool On) {
+ // this method is called by vdr
+ if (On) {
+ // Start();
+ *(Log::getInstance()->log()) << "mReqId= " << mReqId
+ << " cLiveRelay::Activate called by VDR for chan_id= " << mChannelId <<endl;
+ }
+ else {
+ // Cancel();
+ *(Log::getInstance()->log()) << "mReqId= " << mReqId
+ << " cLiveRelay::Deactivate for chan_id= " << mChannelId <<endl;
+ }
+}
+
+void cLiveRelay::detachLiveRelay() {
+ Detach();
+}
+
+void cLiveRelay::Receive(uchar* data, int length) {
+
+ if (length != 188) {
+ *(Log::getInstance()->log()) << "ERROR: ******* ts packet unequal 188 Byte Length= " << length << endl;
+ }
+ if (data[0] != 0x47) {
+ *(Log::getInstance()->log()) << "ERROR: ******* Out of Sync " << endl;
+ }
+
+ pthread_mutex_lock(&processedRingLock);
+ int p = mRingBuffer->Put(data, length);
+ pthread_mutex_unlock(&processedRingLock);
+ if (p != length ) {
+ *(Log::getInstance()->log()) << "cLiveRelay::Receive: Overflow" << endl;
+ mRingBuffer->ReportOverflow(length - p);
+ }
+
+ if (mNotifRequired ) {
+ // *(Log::getInstance()->log()) << "cLiveRelay::Receive: Sending Notification" << endl;
+ mRequest->mFactory->setWriteFlag(mRequest->mFd);
+ mNotifRequired = false;
+ ssize_t res = write (mNotifFd, " ", 1);
+ if (res <= 0)
+ *(Log::getInstance()->log()) << "cLiveRelay::Receive: Notification NOT sent" << endl;
+ }
+
+}
+
+
+//----------------------------------------------
+//----------------------------------------------
+//----------------------------------------------
+
+cResponseLive::cResponseLive(cHttpResource* req, string chan_id) : cResponseBase(req), mChannelId(chan_id),
+ mPatPmtGen(NULL), mInStartPhase(true), mFrameDetector(NULL), mVpid(0), mVtype(0), mPpid(0), mStartThreshold(75*188) {
+
+ mStartThreshold= mRequest->mFactory->getConfig()->getBuiltInLivePktBuf4Sd() * 188;
+ *(mLog->log()) << DEBUGPREFIX << " cResponseLive::cResponseLive - Constructor" << endl;
+
+ if (InitRelay(chan_id)) {
+ mRelay->setNotifRequired();
+ mRequest->mFactory->clrWriteFlag(mRequest->mFd);
+ }
+
+ if (mRelay != NULL) {
+ sendHeaders(200, "OK", NULL, "video/mpeg", -1, -1);
+ }
+ else {
+ sendError(404, "Not Found.", NULL, "004 Resource Busy");
+ }
+}
+
+cResponseLive::~cResponseLive() {
+ if (mRelay != NULL)
+ delete mRelay;
+ if (mPatPmtGen != NULL)
+ delete mPatPmtGen;
+
+ if (mFrameDetector != NULL)
+ delete mFrameDetector;
+}
+
+bool cResponseLive::InitRelay(string channel_id) {
+
+ cChannel * channel = Channels.GetByChannelID(tChannelID::FromString(channel_id.c_str()));
+
+ int priority = 99;
+ if (!channel ){
+ *(mLog->log()) << DEBUGPREFIX
+ << " " << channel_id << " No channel found: " << endl;
+ return false;
+ }
+
+ char buf [40];
+ snprintf(buf, 40, "%s", *(channel->GetChannelID()).ToString());
+
+ if (channel->Vpid() != 0) {
+ mVpid = channel->Vpid();
+ mVtype = channel->Vtype();
+ mPpid = channel->Ppid();
+
+ if (mVtype == 27) {
+ // mStartThreshold = 150 * 188;
+ mStartThreshold= mRequest->mFactory->getConfig()->getBuiltInLivePktBuf4Hd() * 188;
+
+ *(mLog->log()) << DEBUGPREFIX << " cResponseLive::InitRelay H.264 detected. mStartThreshold is " << mStartThreshold << endl;
+ }
+
+ mFrameDetector = new cFrameDetector(channel->Vpid(), channel->Vtype());
+ mInStartPhase = true;
+ }
+ else {
+ mInStartPhase = false;
+ }
+
+ *(mLog->log()) << DEBUGPREFIX << " cResponseLive::InitRelay channel " << buf << " found" << endl;
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX << " vpid= " << channel->Vpid() << " vtype= " << channel->Vtype() << endl;
+
+ for (int n = 0; channel->Apid(n); n++) {
+ *(mLog->log()) << DEBUGPREFIX << " apid= " << channel->Apid(n) << " atype= " << channel->Atype(n)
+ << " lang= " << channel->Alang(n) << endl;
+ }
+ for (int n = 0; channel->Dpid(n); n++) {
+ *(mLog->log()) << DEBUGPREFIX << " dpid= " << channel->Dpid(n) << " dtype= " << channel->Dtype(n) << endl;
+ }
+ for (int n = 0; channel->Spid(n); n++) {
+ *(mLog->log()) << DEBUGPREFIX << " spid= " << channel->Spid(n) << " stype= " << channel->SubtitlingType(n)
+ << " lang= " << channel->Slang(n) << endl;
+ }
+#endif
+
+ cDevice* device = cDevice::GetDevice(channel, priority, true); // last param is live-view
+
+ if (!device) {
+ *(mLog->log()) << DEBUGPREFIX << " cLiveRelay::create: No device found to receive this channel at this priority. chan_id= "
+ << channel_id << endl;
+
+ return false;
+ }
+
+ mRelay = new cLiveRelay(channel, device, channel_id, mRequest->mReqId, mRequest);
+
+ mPatPmtGen = new cPatPmtGenerator(channel);
+
+ return true;
+}
+
+void cResponseLive::WritePatPmt() {
+ mBlkPos = 0;
+ mBlkLen = 0;
+
+ uchar* ptr = mPatPmtGen->GetPat();
+ memcpy(mBlkData, ptr, 188);
+ mBlkLen = 188;
+
+ int idx = 0;
+ while ((ptr = mPatPmtGen->GetPmt(idx)) != NULL) {
+ memcpy(mBlkData+mBlkLen, ptr, 188);
+ mBlkLen += 188;
+ };
+}
+
+int cResponseLive::fillDataBlk() {
+ switch (mRequest->mFactory->getConfig()->getBuiltInLiveStartMode()){
+ case 0:
+ return fillDataBlk_straight();
+ break;
+ case 1:
+ return fillDataBlk_pcr();
+ break;
+ case 2:
+ return fillDataBlk_iframe();
+ break;
+ };
+}
+
+int cResponseLive::fillDataBlk_pcr() {
+ mBlkPos = 0;
+ mBlkLen = 0;
+
+ if (mError)
+ return ERROR;
+
+ int threshold = (mInStartPhase) ? mStartThreshold : (1*188);
+
+ if (mRelay->mRingBuffer->Available() >= threshold) {
+
+ pthread_mutex_lock(&(mRelay->processedRingLock));
+ int r;
+ char *b = (char*)mRelay->mRingBuffer->Get(r);
+
+ if (b != NULL) {
+ // mInStartPhase = false;
+
+ if ((r % 188) != 0) {
+ *(mLog->log()) << DEBUGPREFIX << " ERROR: r= " << r << " %188 = " << (r%188) << endl;
+ }
+
+ if (mInStartPhase) {
+ int offset = 0;
+ bool already_deleted = false;
+ while ((offset +188) <= r) {
+
+ if (TsPid((const uchar*) (b+offset)) == mPpid){
+ int64_t pcr = TsGetPcr((const uchar*) (b+offset));
+ if (pcr != -1) {
+ // pcr pkt found
+ if ((r - (offset+188)) < threshold) {
+ already_deleted = true;
+ mRelay->mRingBuffer->Del(offset + 188);
+ break;
+ }
+ *(mLog->log()) << DEBUGPREFIX
+ << " cResponseLive::fillDataBlk() - PCR found, starting now pcr= "
+ << pcr << endl;
+
+ // fill the memblock
+ mInStartPhase = false;
+ WritePatPmt();
+ int len = ((r - offset ) > (MAXLEN - mBlkLen)) ? (MAXLEN - mBlkLen): (r - offset );
+
+ if ((r - offset ) > (MAXLEN - mBlkLen)) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " cResponseLive::fillDataBlk() mInStartPhase = true - ((r - offset ) > (MAXLEN - mBlkLen))"
+ << endl;
+ }
+ memcpy(mBlkData + mBlkLen, b+offset, len);
+
+ mBlkLen += len;
+ already_deleted = true;
+ mRelay->mRingBuffer->Del(len + offset);
+ break;
+ } // if (pcr != 0)
+ } // if (TsPid((
+
+ // end of find pcr
+
+ offset += 188;
+ } // while (offset < r) {
+
+ if (!already_deleted) {
+ *(mLog->log()) << DEBUGPREFIX << " cResponseLive::fillDataBlk() - Not sufficient data. Deleting "
+ << offset +188 << " Byte corresponding to " << (offset / 188) +1 << " pkts" << endl;
+ mRelay->mRingBuffer->Del(offset + 188);
+ }
+ } // if (mInStartPhase)
+
+ else {
+
+ WritePatPmt();
+ int len = (r > (MAXLEN - mBlkLen)) ? (MAXLEN - mBlkLen): r;
+
+ if (r > (MAXLEN - mBlkLen)) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " cResponseLive::fillDataBlk() mInStartPhase = false - ((r - offset ) > (MAXLEN - mBlkLen)) len= " << len
+ << " MAXLEN= " << MAXLEN
+ << " mBlkLen= " << mBlkLen
+ << endl;
+ }
+ memcpy(mBlkData + mBlkLen, b, len);
+
+ mBlkLen += len;
+ mRelay->mRingBuffer->Del(len);
+ } // else
+ } // if (b != NULL)
+ else {
+ *(mLog->log()) << DEBUGPREFIX << " WARNING - cResponseLive::fillDataBlk() - b== NULL" << endl;
+ }
+ pthread_mutex_unlock(&(mRelay->processedRingLock));
+ }
+ else {
+ mRequest->mFactory->clrWriteFlag(mRequest->mFd);
+ mRelay->setNotifRequired();
+ }
+
+ return OKAY;
+}
+
+int cResponseLive::fillDataBlk_iframe() {
+ mBlkPos = 0;
+ mBlkLen = 0;
+
+ if (mError)
+ return ERROR;
+
+ int threshold = (mInStartPhase) ? mStartThreshold : (10*188);
+
+ if (mRelay->mRingBuffer->Available() >= threshold) {
+
+ pthread_mutex_lock(&(mRelay->processedRingLock));
+ int r;
+ char *b = (char*)mRelay->mRingBuffer->Get(r);
+
+ if (b != NULL) {
+ // mInStartPhase = false;
+
+ if ((r % 188) != 0) {
+ *(mLog->log()) << DEBUGPREFIX << " ERROR: r= " << r << " %188 = " << (r%188) << endl;
+ }
+
+ if (mInStartPhase) {
+ int offset = 0;
+ bool already_deleted = false;
+ cFrameDetector frame_detector(mVpid, mVtype);
+
+ while ((offset +188) <= r) {
+
+ if (TsPid((const uchar*) (b+offset)) == mVpid){
+ // check payload unit start ind
+ if (!TsPayloadStart((const uchar*) (b+offset))) {
+ offset += 188;
+ continue;
+ }
+
+ //ok. point to frame start now. check whether i frame
+ if (frame_detector.Analyze((const uchar*) (b+offset), (r - offset) ) == 0) {
+ // not sufficient data. delete and return
+ already_deleted = true;
+ mRelay->mRingBuffer->Del(offset + 188);
+ break;
+ }
+ if (frame_detector.IndependentFrame()) {
+ // was an I-Frame
+ if ((r - (offset +188)) < threshold) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " IFrame found, but not sufficient data to start. " << endl;
+ already_deleted = true;
+ mRelay->mRingBuffer->Del(offset);
+ break;
+ }
+
+ *(mLog->log()) << DEBUGPREFIX
+ << " IFrame found, starting now " << endl;
+ mInStartPhase = false;
+ WritePatPmt();
+ int len = ((r - offset ) > (MAXLEN - mBlkLen)) ? (MAXLEN - mBlkLen): (r - offset );
+ if ((r - offset ) > (MAXLEN - mBlkLen)) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " cResponseLive::fillDataBlk_iframe() mInStartPhase = true - ((r - offset ) > (MAXLEN - mBlkLen))"
+ << endl;
+ }
+ memcpy(mBlkData + mBlkLen, b+offset, len);
+
+ mBlkLen += len;
+ already_deleted = true;
+ mRelay->mRingBuffer->Del(len + offset);
+ break;
+ }
+
+ } // if (TsPid((
+
+ offset += 188;
+ } // while (offset < r) {
+
+ if (!already_deleted) {
+ *(mLog->log()) << DEBUGPREFIX << " cResponseLive::fillDataBlk() - Not sufficient data. Deleting "
+ << offset << " Byte" << endl;
+ mRelay->mRingBuffer->Del(offset + 188);
+ }
+ } // if (mInStartPhase)
+
+ else {
+
+ WritePatPmt();
+ int len = (r > (MAXLEN - mBlkLen)) ? (MAXLEN - mBlkLen): r;
+
+ if (r > (MAXLEN - mBlkLen)) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " cResponseLive::fillDataBlk() mInStartPhase = false - ((r - offset ) > (MAXLEN - mBlkLen)) len= " << len
+ << " MAXLEN= " << MAXLEN
+ << " mBlkLen= " << mBlkLen
+ << endl;
+ }
+ memcpy(mBlkData + mBlkLen, b, len);
+
+ mBlkLen += len;
+ mRelay->mRingBuffer->Del(len);
+ } // else
+ } // if (b != NULL)
+ else {
+ *(mLog->log()) << DEBUGPREFIX << " WARNING - cResponseLive::fillDataBlk() - b== NULL" << endl;
+ }
+ pthread_mutex_unlock(&(mRelay->processedRingLock));
+ }
+ else {
+ mRequest->mFactory->clrWriteFlag(mRequest->mFd);
+ mRelay->setNotifRequired();
+ }
+
+ return OKAY;
+}
+
+
+int cResponseLive::fillDataBlk_straight() {
+ mBlkPos = 0;
+ mBlkLen = 0;
+
+ if (mError)
+ return ERROR;
+
+ int threshold = (mInStartPhase) ? mStartThreshold : (10*188);
+
+ if (mRelay->mRingBuffer->Available() >= threshold) {
+
+ pthread_mutex_lock(&(mRelay->processedRingLock));
+ int r;
+ char *b = (char*)mRelay->mRingBuffer->Get(r);
+
+ if (b != NULL) {
+ mInStartPhase = false;
+
+ if ((r % 188) != 0) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " ERROR: r= " << r << " %188 = " << (r%188) << endl;
+
+ }
+
+ WritePatPmt();
+ int len = (r > (MAXLEN - mBlkLen)) ? (MAXLEN - mBlkLen): r;
+
+ if (r > (MAXLEN - mBlkLen)) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " cResponseLive::fillDataBlk() mInStartPhase = false - ((r - offset ) > (MAXLEN - mBlkLen)) len= " << len
+ << " MAXLEN= " << MAXLEN
+ << " mBlkLen= " << mBlkLen
+ << endl;
+ }
+ memcpy(mBlkData + mBlkLen, b, len);
+
+ mBlkLen += len;
+ mRelay->mRingBuffer->Del(len);
+
+
+ } // if (b != NULL)
+ else {
+ *(mLog->log()) << DEBUGPREFIX << " WARNING - cResponseLive::fillDataBlk() - b== NULL" << endl;
+ }
+ pthread_mutex_unlock(&(mRelay->processedRingLock));
+ }
+ else {
+ mRequest->mFactory->clrWriteFlag(mRequest->mFd);
+ mRelay->setNotifRequired();
+ }
+
+ return OKAY;
+}
diff --git a/responselive.h b/responselive.h
new file mode 100644
index 0000000..4df0d75
--- /dev/null
+++ b/responselive.h
@@ -0,0 +1,103 @@
+/*
+ * responselive.h: VDR on Smart TV plugin
+ *
+ * Copyright (C) 2013 T. Lohmar
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ */
+
+#ifndef __RESPONSE_LIVE_H__
+#define __RESPONSE_LIVE_H__
+
+#include "responsebase.h"
+
+#include <cstdio>
+#include <string>
+#include <sys/time.h>
+
+#include <vdr/ringbuffer.h>
+#include <vdr/receiver.h>
+
+//class cHttpResource;
+
+using namespace std;
+
+//class SmartTvServer;
+class cPatPmtGenerator;
+
+class cLiveRelay : public cReceiver {
+ public:
+ cLiveRelay(cChannel* channel, cDevice* device, string chan_id, int req_id, cHttpResource*);
+ ~cLiveRelay();
+ // static cLiveRelay *create(string channel_id, int req_id, cHttpResource*);
+
+ // void addClient();
+ // int removeClient();
+
+ void setNotifRequired() { mNotifRequired = true; };
+ string mChannelId;
+ cRingBufferLinear *mRingBuffer;
+ pthread_mutex_t processedRingLock;
+
+protected:
+ virtual void Activate(bool On);
+ virtual void Receive(uchar *Data, int Length);
+ void detachLiveRelay();
+ // cThread
+ // virtual void Action(void);
+
+ Log* mLog;
+ private:
+ // SmartTvServer *mFactory;
+ int mReqId;
+ cHttpResource* mRequest;
+ // int mNumClients;
+ // timeval mStartTime;
+
+ int mNotifFd;
+ bool mNotifRequired;
+};
+
+class cResponseLive : public cResponseBase {
+ public:
+ cResponseLive(cHttpResource*, string chan_id );
+ virtual ~cResponseLive();
+
+ int fillDataBlk();
+
+ int fillDataBlk_straight();
+ int fillDataBlk_pcr();
+ int fillDataBlk_iframe();
+
+ private:
+ bool InitRelay(string chan_id);
+ void WritePatPmt();
+
+ string mChannelId;
+ cLiveRelay* mRelay;
+
+ cPatPmtGenerator* mPatPmtGen;
+ bool mInStartPhase;
+ cFrameDetector* mFrameDetector;
+ int mVpid;
+ int mVtype;
+ int mPpid;
+
+ int mStartThreshold;
+};
+
+#endif
diff --git a/responsememblk.c b/responsememblk.c
index 0420f67..c590816 100755
--- a/responsememblk.c
+++ b/responsememblk.c
@@ -2027,8 +2027,12 @@ int cResponseMemBlk::sendChannelsXml (struct stat *statbuf) {
break;
}
- // snprintf(f, sizeof(f), "http://%s:3000/%s.ts", mServerAddr.c_str(), *(channel->GetChannelID()).ToString());
- snprintf(f, sizeof(f), "http://%s:3000/%s.ts", own_ip.c_str(), *(channel->GetChannelID()).ToString());
+ if (mRequest->mFactory->getConfig()->useStreamDev4Live() )
+ snprintf(f, sizeof(f), "http://%s:3000/%s.ts", own_ip.c_str(), *(channel->GetChannelID()).ToString());
+ else
+ snprintf(f, sizeof(f), "http://%s:%d/live/%s", own_ip.c_str(), mRequest->mServerPort,
+ *(channel->GetChannelID()).ToString());
+
string link = f;
const cSchedule *schedule = schedules->GetSchedule(channel->GetChannelID());
diff --git a/smarttvfactory.c b/smarttvfactory.c
index a2154eb..485918b 100755
--- a/smarttvfactory.c
+++ b/smarttvfactory.c
@@ -52,6 +52,8 @@
#include "httpclient.h"
#include "url.h"
+#include "responselive.h"
+
#ifndef STANDALONE
#define PORT 8000
#else
@@ -107,11 +109,15 @@ void cCmd::trim(string &t) {
t.erase(m+1);
}
+
+
+
+
SmartTvServer::SmartTvServer(): cStatus(), mRequestCount(0), isInited(false), serverPort(PORT), mServerFd(-1),
mSegmentDuration(10), mHasMinBufferTime(40), mLiveChannels(20),
clientList(), mConTvClients(), mRecCmds(), mCmdCmds(), mRecMsg(), mCmdMsg(), mActiveSessions(0), mHttpClientId(0),
mConfig(NULL), mMaxFd(0),
- mManagedUrls(NULL){
+ mManagedUrls(NULL) {
}
@@ -275,11 +281,6 @@ void SmartTvServer::cleanUp() {
mLog.shutdown();
}
-void SmartTvServer::setNonBlocking(int fd) {
- int oldflags = fcntl(fd, F_GETFL, 0);
- oldflags |= O_NONBLOCK;
- fcntl(fd, F_SETFL, oldflags);
-}
void SmartTvServer::updateTvClient(string ip, string mac, time_t upd) {
@@ -346,7 +347,7 @@ bool SmartTvServer::deleteYtVideoId(string guid) {
void SmartTvServer::pushYtVideoId(string vid_id, bool store) {
- time_t now = time(NULL);
+ // time_t now = time(NULL);
for (uint i = 0; i < mConTvClients.size(); i ++) {
if ((mConTvClients[i]->ip).compare("") != 0) {
// pushYtVideoIdToClient(vid_id, mConTvClients[i]->ip, store);
@@ -417,18 +418,6 @@ void SmartTvServer::addHttpResource(int rfd, cHttpResourceBase* resource) {
}
}
-/* // obsolete
-void SmartTvServer::pushYtVideoIdToClient(string vid_id, string peer, bool store) {
- *(mLog.log()) << " SmartTvServer::pushYtVideoIdToClient vid_id= " << vid_id
- << " client= " << peer << endl;
-
- int cfd= connectToClient(peer);
- if (cfd < 0)
- return;
- addHttpResource(cfd, new cHttpYtPushClient(cfd, mHttpClientId, serverPort, this, peer, vid_id, store));
-
-}
-*/
void SmartTvServer::pushCfgServerAddressToTv( string tv_addr) {
*(mLog.log()) << " SmartTvServer::pushCfgServerAddressToTv TV= " << tv_addr
<< endl;
@@ -457,17 +446,87 @@ void SmartTvServer::threadLoop() {
*(mLog.log()) << " SmartTvServer Thread Stopped " << endl;
}
+//---------------------------------------------------
+void SmartTvServer::setNonBlocking(int fd) {
+ int oldflags = fcntl(fd, F_GETFL, 0);
+ oldflags |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, oldflags);
+}
-void SmartTvServer::loop() {
+void SmartTvServer::clrWriteFlag(int fd) {
+ // *(mLog.log()) << " clrWriteFlag fd= " << fd << endl;
+ FD_CLR(fd, &mWriteState);
+}
+
+void SmartTvServer::setWriteFlag(int fd) {
+ // *(mLog.log()) << " setWriteFlag fd= " << fd << endl;
+ FD_SET(fd, &mWriteState);
+}
+
+int SmartTvServer::openPipe() {
+ int pipefd[2];
+
+ if (pipe2(pipefd, O_NONBLOCK) == -1) {
+ return 0;
+ }
+ *(mLog.log()) << "SmartTvServer::openPipe pipefd[0]= " << pipefd[0]
+ << " pipefd[1]= " << pipefd[1] << endl;
+
+ addHttpResource(pipefd[0], new cHttpResourcePipe(pipefd[0], this));
+ return pipefd[1];
+}
+
+
+void SmartTvServer::closeHttpResource(int rfd) {
+ close(rfd);
+ delete clientList[rfd];
+ clientList[rfd] = NULL;
+ mActiveSessions--;
+ *(mLog.log()) << " - Check Read: mActiveSessions= " << mActiveSessions << endl;
+ FD_CLR(rfd, &mReadState); /* dead client */
+ FD_CLR(rfd, &mWriteState);
+
+}
+
+void SmartTvServer::acceptHttpResource(int &req_id) {
+ int rfd = 0;
+ sockaddr_in sadr;
socklen_t addr_size = 0;
+
+ if((rfd = accept(mServerFd, (sockaddr*)&sadr, &addr_size))!= -1){
+ req_id ++;
+
+#ifndef DEBUG
+ *(mLog.log()) << "fd= " << rfd
+ << " --------------------- Received connection ---------------------" << endl;
+#endif
+
+ FD_SET(rfd, &mReadState);
+ FD_SET(rfd, &mWriteState);
+
+ if (rfd > mMaxFd) {
+ mMaxFd = rfd;
+ }
+
+ if (clientList.size() < (rfd+1)) {
+ clientList.resize(rfd+1, NULL); // Check.
+ }
+ clientList[rfd] = new cHttpResource(rfd, req_id, serverPort, this);
+ mActiveSessions ++;
+ *(mLog.log()) << " + mActiveSessions= " << mActiveSessions << endl;
+ }
+ else{
+ *(mLog.log()) << "Error accepting " << errno << endl;
+ }
+
+}
+
+void SmartTvServer::loop() {
unsigned int rfd;
- sockaddr_in sadr;
int req_id = 0;
int ret = 0;
struct timeval timeout;
- // int maxfd;
-
fd_set read_set;
fd_set write_set;
@@ -523,12 +582,7 @@ void SmartTvServer::loop() {
for (uint idx= 0; idx < clientList.size(); idx++) {
if (clientList[idx] != NULL)
if (clientList[idx]->checkStatus() == ERROR) {
- close(idx);
- delete clientList[idx];
- clientList[idx] = NULL;
- mActiveSessions--;
- FD_CLR(idx, &mReadState); /* dead client */
- FD_CLR(idx, &mWriteState);
+ closeHttpResource(idx);
*(mLog.log()) << "WARNING: Timeout - Dead Client fd=" << idx << endl;
}
}
@@ -543,31 +597,7 @@ void SmartTvServer::loop() {
// new accept
if (FD_ISSET(mServerFd, &read_set)) {
handeled_fds ++;
- if((rfd = accept(mServerFd, (sockaddr*)&sadr, &addr_size))!= -1){
- req_id ++;
-
-#ifndef DEBUG
- *(mLog.log()) << "fd= " << rfd
- << " --------------------- Received connection ---------------------" << endl;
-#endif
-
- FD_SET(rfd, &mReadState);
- FD_SET(rfd, &mWriteState);
-
- if (rfd > mMaxFd) {
- mMaxFd = rfd;
- }
-
- if (clientList.size() < (rfd+1)) {
- clientList.resize(rfd+1, NULL); // Check.
- }
- clientList[rfd] = new cHttpResource(rfd, req_id, serverPort, this);
- mActiveSessions ++;
- *(mLog.log()) << " + mActiveSessions= " << mActiveSessions << endl;
- }
- else{
- *(mLog.log()) << "Error accepting " << errno << endl;
- }
+ acceptHttpResource(req_id);
}
// Check for data on already accepted connections
@@ -584,17 +614,21 @@ void SmartTvServer::loop() {
FD_CLR(rfd, &mWriteState);
continue;
}
+
+ int n = 0;
+ ioctl(rfd, FIONREAD, &n);
+ if ( n == 0) {
+ closeHttpResource(rfd);
+ *(mLog.log()) << "fd= " << rfd << " ------ Check Read: Closing (n=0)-------"
+ << endl;
+ continue;
+ }
if ( clientList[rfd]->handleRead() < 0){
#ifndef DEBUG
- *(mLog.log()) << "fd= " << rfd << " --------------------- Check Read: Closing ---------------------" << endl;
+ *(mLog.log()) << "fd= " << rfd << " --------------------- Check Read: Closing ---------------------"
+ << endl;
#endif
- close(rfd);
- delete clientList[rfd];
- clientList[rfd] = NULL;
- mActiveSessions--;
- *(mLog.log()) << " - Check Read: mActiveSessions= " << mActiveSessions << endl;
- FD_CLR(rfd, &mReadState); /* dead client */
- FD_CLR(rfd, &mWriteState);
+ closeHttpResource(rfd);
}
}
}
@@ -616,13 +650,7 @@ void SmartTvServer::loop() {
#ifndef DEBUG
*(mLog.log()) << "fd= " << rfd << " --------------------- Check Write: Closing ---------------------" << endl;
#endif
- close(rfd);
- delete clientList[rfd];
- clientList[rfd] = NULL;
- mActiveSessions--;
- *(mLog.log()) << " - Check Write: mActiveSessions= " << mActiveSessions << endl;
- FD_CLR(rfd, &mReadState);
- FD_CLR(rfd, &mWriteState);
+ closeHttpResource(rfd);
}
}
}
@@ -670,6 +698,7 @@ int SmartTvServer::isServing() {
return (mActiveSessions != 0 ? true : false) or connected_tv;
}
+
string SmartTvServer::processNestedItemList(string pref, cList<cNestedItem> *cmd, vector<cCmd*> *cmd_list) {
char f[400];
string msg ="";
@@ -682,14 +711,11 @@ string SmartTvServer::processNestedItemList(string pref, cList<cNestedItem> *cmd
}
else {
cCmd *itm = new cCmd(c->Text());
- // *(mLog.log()) << " Parsed RecCmd: t= " << itm->mTitle << " c= " << itm->mCommand
- // << " ?= " << ((itm->mConfirm)?"true":"false") << endl;
cmd_list->push_back(itm);
snprintf(f, sizeof(f), "<item cmd=\"%d\" confirm=\"%s\">%s</item>\n", cmd_list->size()-1, ((itm->mConfirm)?"true":"false"),
(pref + itm->mTitle).c_str());
msg += f;
- // *(mLog.log()) << "Line= " << f << endl;
}
}
@@ -703,7 +729,6 @@ void SmartTvServer::initRecCmds() {
mRecMsg += "</reccmds>\n";
*(mLog.log()) << "reccmds.conf parsed" << endl;
-
}
void SmartTvServer::initCmdCmds() {
@@ -715,6 +740,7 @@ void SmartTvServer::initCmdCmds() {
*(mLog.log()) << "commands.conf parsed" << endl;
}
+
void SmartTvServer::initServer(string dir) {
/* This function initialtes the listening socket for the server
* and sets isInited to true
@@ -731,8 +757,7 @@ void SmartTvServer::initServer(string dir) {
if (mConfig->getLogFile() != "") {
string msg = "SmartTvWeb: Logfile created File= " + mConfig->getLogFile();
- // esyslog("SmartTvWeb: Logfile created");
- esyslog(msg.c_str());
+ esyslog("%s", msg.c_str());
}
*(mLog.log()) << "LogFile= " << mConfig->getLogFile() << endl;
@@ -747,7 +772,8 @@ void SmartTvServer::initServer(string dir) {
#endif
- // mConfig->printConfig();
+ mConfig->printConfig();
+
mSegmentDuration= mConfig->getSegmentDuration();
mHasMinBufferTime= mConfig->getHasMinBufferTime();
@@ -804,5 +830,3 @@ void SmartTvServer::initServer(string dir) {
}
-
-
diff --git a/smarttvfactory.h b/smarttvfactory.h
index c5f03fa..e010f98 100755
--- a/smarttvfactory.h
+++ b/smarttvfactory.h
@@ -48,8 +48,9 @@ class cStatus {
using namespace std;
-#define PLG_VERSION "0.9.9"
-#define SERVER "SmartTvWeb/0.9.9"
+#define PLG_VERSION "0.9.a-pre"
+#define SERVER "SmartTvWeb/0.9.a-pre"
+class cLiveRelay;
struct sClientEntry {
string mac;
@@ -77,13 +78,13 @@ class SmartTvServer : public cStatus {
void initServer(string c_dir);
void loop();
void cleanUp();
- int runAsThread();
+ int runAsThread();
void threadLoop();
Log mLog;
void readRecordings();
- int isServing();
+ int isServing();
string getConfigDir() { return mConfigDir; };
cSmartTvConfig* getConfig() { return mConfig; };
@@ -108,6 +109,10 @@ class SmartTvServer : public cStatus {
void OsdStatusMessage(const char *Message);
+ void clrWriteFlag(int fd);
+ void setWriteFlag(int fd);
+ int openPipe();
+
private:
void addHttpResource(int fd, cHttpResourceBase* resource);
void pushToClients(cHttpResourceBase* resource);
@@ -123,6 +128,9 @@ class SmartTvServer : public cStatus {
string processNestedItemList(string, cList<cNestedItem> *, vector<cCmd*>*);
+ void closeHttpResource(int rfd);
+ void acceptHttpResource(int &req_id);
+
pthread_t mThreadId;
int mRequestCount;
bool isInited;
diff --git a/smarttvweb.conf b/smarttvweb.conf
index 7976687..c8bb8c3 100644
--- a/smarttvweb.conf
+++ b/smarttvweb.conf
@@ -32,3 +32,16 @@ GroupSeparators Ignore
# Allow execution of reccmd.conf or commands.conf defined programs through the widget.
# Ensure that only authorized hosts get access to the plugin (e.g. firewall protected).
#Commands enable
+
+# To enable the plugin built-in solution for live, uncomment the following parameter.
+#UseStreamDev4Live false
+
+# Define the startup mode for the built-in live solution. Default is 2.
+# 0: just start
+# 1: start with first pcr
+# 2: start with first I Frame
+#BuiltInLiveStartMode 0
+
+# Number of TS backets in local buffer before start.
+#BuiltInLivePktBuf4Hd 150
+#BuiltInLivePktBuf4Sd 75
diff --git a/stvw_cfg.c b/stvw_cfg.c
index d0a330c..14acffc 100755
--- a/stvw_cfg.c
+++ b/stvw_cfg.c
@@ -33,7 +33,8 @@
cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NULL),
mLogFile(), mMediaFolder(), mSegmentDuration(), mHasMinBufferTime(), mHasBitrateCorrection(),
- mLiveChannels(), mGroupSep(IGNORE), mServerAddress(""), mServerPort(8000), mCmds(false) {
+ mLiveChannels(), mGroupSep(IGNORE), mServerAddress(""), mServerPort(8000), mCmds(false), mUseStreamDev4Live(true),
+ mBuiltInLiveStartMode (2), mBuiltInLivePktBuf4Hd(150), mBuiltInLivePktBuf4Sd(75) {
#ifndef STANDALONE
mLogFile= "";
@@ -68,6 +69,11 @@ void cSmartTvConfig::printConfig() {
*(mLog->log()) << " LiveChannels: " << mLiveChannels << endl;
*(mLog->log()) << " GroupSeparators: " << ((mGroupSep==IGNORE)? "Ignore" : ((mGroupSep==EMPTYIGNORE)? "EmptyIgnore": "EmptyFolderDown")) << endl;
*(mLog->log()) << " ServerAddress: " << mServerAddress << endl;
+ *(mLog->log()) << " UseStreamDev4Live: " << ((mUseStreamDev4Live) ? "true" :"false") << endl;
+ *(mLog->log()) << " BuiltInLiveStartMode: " << mBuiltInLiveStartMode << endl;
+ *(mLog->log()) << " BuiltInLivePktBuf4Hd: " << mBuiltInLivePktBuf4Hd << endl;
+ *(mLog->log()) << " BuiltInLivePktBuf4Sd: " << mBuiltInLivePktBuf4Sd << endl;
+
}
@@ -155,6 +161,27 @@ void cSmartTvConfig::readConfig() {
continue;
}
+ if (strcmp(attr, "UseStreamDev4Live") == 0) {
+ if (strcmp(value, "false") == 0)
+ mUseStreamDev4Live = false;
+ continue;
+ }
+
+ if (strcmp(attr, "BuiltInLiveStartMode") == 0) {
+ mBuiltInLiveStartMode = atoi(value);
+ if ((mBuiltInLiveStartMode <0) || (mBuiltInLiveStartMode > 2))
+ mBuiltInLiveStartMode = 0;
+ continue;
+ }
+
+ if (strcmp(attr, "BuiltInLivePktBuf4Hd") == 0) {
+ mBuiltInLivePktBuf4Hd = atoi(value);
+ continue;
+ }
+ if (strcmp(attr, "BuiltInLivePktBuf4Sd") == 0) {
+ mBuiltInLivePktBuf4Sd = atoi(value);
+ continue;
+ }
#ifndef STANDALONE
esyslog("WARNING in SmartTvWeb: Attribute= %s with value= %s was not processed, thus ignored.", attr, value);
diff --git a/stvw_cfg.h b/stvw_cfg.h
index a3fd01e..983a5f0 100755
--- a/stvw_cfg.h
+++ b/stvw_cfg.h
@@ -56,6 +56,10 @@ class cSmartTvConfig {
string mServerAddress;
int mServerPort;
bool mCmds;
+ bool mUseStreamDev4Live;
+ int mBuiltInLiveStartMode;
+ int mBuiltInLivePktBuf4Hd;
+ int mBuiltInLivePktBuf4Sd;
public:
cSmartTvConfig(string dir);
@@ -74,6 +78,11 @@ class cSmartTvConfig {
string getServerAddress() { return mServerAddress; };
int getServerPort() { return mServerPort; };
bool getCmds() { return mCmds; };
+ bool useStreamDev4Live() { return mUseStreamDev4Live; };
+ int getBuiltInLiveStartMode() {return mBuiltInLiveStartMode; };
+ int getBuiltInLivePktBuf4Hd() { return mBuiltInLivePktBuf4Hd; };
+ int getBuiltInLivePktBuf4Sd() { return mBuiltInLivePktBuf4Sd; };
+
};
#endif