diff options
-rw-r--r-- | Makefile | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | Makefile.new | 2 | ||||
-rwxr-xr-x | httpresource.c | 17 | ||||
-rwxr-xr-x | httpresource.h | 5 | ||||
-rw-r--r-- | httpresource_base.c | 46 | ||||
-rwxr-xr-x[-rw-r--r--] | httpresource_base.h | 19 | ||||
-rwxr-xr-x[-rw-r--r--] | log.c | 2 | ||||
-rw-r--r-- | responselive.c | 525 | ||||
-rw-r--r-- | responselive.h | 103 | ||||
-rwxr-xr-x | responsememblk.c | 8 | ||||
-rwxr-xr-x | smarttvfactory.c | 180 | ||||
-rwxr-xr-x | smarttvfactory.h | 16 | ||||
-rw-r--r-- | smarttvweb.conf | 13 | ||||
-rwxr-xr-x | stvw_cfg.c | 29 | ||||
-rwxr-xr-x | stvw_cfg.h | 9 |
15 files changed, 879 insertions, 97 deletions
@@ -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 @@ -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 @@ -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); @@ -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 |