diff -NaurwB streamdev/Makefile streamdev-patched/Makefile --- streamdev/Makefile 2009-11-04 12:12:20.000000000 +0100 +++ streamdev-patched/Makefile 2010-02-09 15:58:13.000000000 +0100 @@ -65,7 +65,7 @@ server/server.o server/component.o server/connection.o \ server/componentVTP.o server/componentHTTP.o server/componentIGMP.o \ server/connectionVTP.o server/connectionHTTP.o server/connectionIGMP.o \ - server/streamer.o server/livestreamer.o server/livefilter.o \ + server/streamer.o server/livestreamer.o server/livefilter.o server/recordingstreamer.o \ server/suspend.o server/setup.o server/menuHTTP.o server/recplayer.o \ remux/tsremux.o remux/ts2pes.o remux/ts2ps.o remux/ts2es.o remux/extern.o diff -NaurwB streamdev/server/connectionVTP.c streamdev-patched/server/connectionVTP.c --- streamdev/server/connectionVTP.c 2010-01-29 13:03:02.000000000 +0100 +++ streamdev-patched/server/connectionVTP.c 2010-02-10 08:56:22.000000000 +0100 @@ -4,6 +4,7 @@ #include "server/connectionVTP.h" #include "server/livestreamer.h" +#include "server/recordingstreamer.h" #include "server/suspend.h" #include "setup.h" @@ -741,11 +742,11 @@ m_FilterSocket(NULL), m_FilterStreamer(NULL), m_RecSocket(NULL), + m_RecStreamer(NULL), m_DataSocket(NULL), m_LastCommand(NULL), m_StreamType(stTSPIDS), m_FiltersSupport(false), - m_RecPlayer(NULL), m_LSTEHandler(NULL), m_LSTCHandler(NULL), m_LSTTHandler(NULL), @@ -759,6 +760,7 @@ free(m_LastCommand); delete m_LiveStreamer; delete m_LiveSocket; + delete m_RecStreamer; delete m_RecSocket; delete m_FilterStreamer; delete m_FilterSocket; @@ -767,7 +769,6 @@ delete m_LSTCHandler; delete m_LSTEHandler; delete m_LSTRHandler; - delete m_RecPlayer; } inline bool cConnectionVTP::Abort(void) const @@ -833,9 +834,10 @@ if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(param); else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(param); else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(param); - else if (strcasecmp(Cmd, "READ") == 0) return CmdREAD(param); else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param); else if (strcasecmp(Cmd, "PLAY") == 0) return CmdPLAY(param); + else if (strcasecmp(Cmd, "SEEK") == 0) return CmdSEEK(param); + else if (strcasecmp(Cmd, "SIZE") == 0) return CmdSIZE(param); else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(param); else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param); else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param); @@ -1012,6 +1014,8 @@ if (!m_RecSocket->SetDSCP()) LOG_ERROR_STR("unable to set DSCP sockopt"); + if(m_RecSocket) + m_RecStreamer->Start(m_RecSocket); return Respond(220, "Port command ok, data connection opened"); break; @@ -1034,35 +1038,6 @@ } } -bool cConnectionVTP::CmdREAD(char *Opts) -{ - if (*Opts) { - char *tail; - uint64_t position = strtoll(Opts, &tail, 10); - if (tail && tail != Opts) { - tail = skipspace(tail); - if (tail && tail != Opts) { - int size = strtol(tail, NULL, 10); - uint8_t* data = (uint8_t*)malloc(size+4); - unsigned long count_readed = m_RecPlayer->getBlock(data, position, size); - unsigned long count_written = m_RecSocket->SysWrite(data, count_readed); - - free(data); - return Respond(220, "%lu Bytes submitted", count_written); - } - else { - return Respond(501, "Missing position"); - } - } - else { - return Respond(501, "Missing size"); - } - } - else { - return Respond(501, "Missing position"); - } -} - bool cConnectionVTP::CmdTUNE(char *Opts) { const cChannel *chan; @@ -1096,27 +1071,40 @@ bool cConnectionVTP::CmdPLAY(char *Opts) { - if (*Opts) { - if (isnumber(Opts)) { - cRecording *recording = Recordings.Get(strtol(Opts, NULL, 10) - 1); - if (recording) { - if (m_RecPlayer) { - delete m_RecPlayer; - } - m_RecPlayer = new RecPlayer(recording); - return Respond(220, "%llu (Bytes), %u (Frames)", (long long unsigned int) m_RecPlayer->getLengthBytes(), (unsigned int) m_RecPlayer->getLengthFrames()); - } - else { - return Respond(550, "Recording \"%s\" not found", Opts); - } - } - else { - return Respond(500, "Use: PLAY record"); + const cRecording *recording; + cDevice *dev; + + if ((recording = Recordings.Get(strtol(Opts, NULL, 10) - 1)) == NULL) + return Respond(550, "Undefined recording \"%s\"", Opts); + + delete m_RecStreamer; + m_RecStreamer = new cStreamdevRecStreamer(recording); + if(m_RecSocket) + m_RecStreamer->Start(m_RecSocket); + + return Respond(220, "Recording opened"); } + +bool cConnectionVTP::CmdSEEK(char *Opts) +{ + if (m_RecStreamer) + { + uint64_t TotalBytesWritten = m_RecStreamer->getTotalBytesWritten(); + uint64_t newPosition = atoll(Opts); + m_RecStreamer->seekPosition(newPosition); + + return Respond(220, "%llu (TCP Stack Position) %llu (New Position)", TotalBytesWritten, newPosition); } - else { - return Respond(500, "Use: PLAY record"); + + return Respond(550, "Curretly no record playing"); } + +bool cConnectionVTP::CmdSIZE(char *Opts) +{ + if (m_RecStreamer) + return Respond(220, "%llu (Bytes), %u (Frames)", (long long unsigned int) m_RecStreamer->getLengthBytes(), (unsigned int) m_RecStreamer->getLengthFrames()); + + return Respond(550, "Curretly no record playing"); } bool cConnectionVTP::CmdADDP(char *Opts) @@ -1215,7 +1203,7 @@ DELETENULL(m_FilterSocket); break; case siReplay: - DELETENULL(m_RecPlayer); + DELETENULL(m_RecStreamer); DELETENULL(m_RecSocket); break; case siDataRespond: diff -NaurwB streamdev/server/connectionVTP.h streamdev-patched/server/connectionVTP.h --- streamdev/server/connectionVTP.h 2009-07-01 12:46:16.000000000 +0200 +++ streamdev-patched/server/connectionVTP.h 2010-02-09 18:08:35.000000000 +0100 @@ -7,6 +7,7 @@ class cTBSocket; class cStreamdevLiveStreamer; class cStreamdevFilterStreamer; +class cStreamdevRecStreamer; class cLSTEHandler; class cLSTCHandler; class cLSTTHandler; @@ -24,12 +25,12 @@ cTBSocket *m_FilterSocket; cStreamdevFilterStreamer *m_FilterStreamer; cTBSocket *m_RecSocket; + cStreamdevRecStreamer *m_RecStreamer; cTBSocket *m_DataSocket; char *m_LastCommand; eStreamType m_StreamType; bool m_FiltersSupport; - RecPlayer *m_RecPlayer; // Members adopted for SVDRP cLSTEHandler *m_LSTEHandler; @@ -56,9 +57,10 @@ bool CmdCAPS(char *Opts); bool CmdPROV(char *Opts); bool CmdPORT(char *Opts); - bool CmdREAD(char *Opts); bool CmdTUNE(char *Opts); bool CmdPLAY(char *Opts); + bool CmdSEEK(char *Opts); + bool CmdSIZE(char *Opts); bool CmdADDP(char *Opts); bool CmdDELP(char *Opts); bool CmdADDF(char *Opts); diff -NaurwB streamdev/server/recordingstreamer.c streamdev-patched/server/recordingstreamer.c --- streamdev/server/recordingstreamer.c 1970-01-01 01:00:00.000000000 +0100 +++ streamdev-patched/server/recordingstreamer.c 2010-02-10 08:58:01.000000000 +0100 @@ -0,0 +1,89 @@ +#include <vdr/recording.h> + +#include "tools/socket.h" +#include "tools/select.h" + +#include "server/recordingstreamer.h" +#include "recplayer.h" +#include "common.h" + +// --- cStreamdevRecStreamer ------------------------------------------------- + +cStreamdevRecStreamer::cStreamdevRecStreamer(const cRecording *Recording): + cThread("streamdev-recordingstreaming"), + m_Recording(Recording), + m_RecPlayer(new cRecPlayer(Recording)), + m_Position(0), + m_TotalBytesWritten(0) +{ +} + +cStreamdevRecStreamer::~cStreamdevRecStreamer() +{ + Dprintf("Desctructing Recording streamer\n"); + Stop(); + + DELETENULL(m_RecPlayer); +} + +void cStreamdevRecStreamer::Start(cTBSocket *Socket) +{ + Dprintf("start streamer\n"); + m_Socket = Socket; + cThread::Start(); +} + +void cStreamdevRecStreamer::Stop(void) +{ + if (Running()) { + Dprintf("stopping streamer\n"); + Cancel(3); + } +} + +uint64_t cStreamdevRecStreamer::getLengthBytes() +{ + return m_RecPlayer->getLengthBytes(); +} + +uint32_t cStreamdevRecStreamer::getLengthFrames() +{ + return m_RecPlayer->getLengthFrames(); +} + +void cStreamdevRecStreamer::seekPosition(uint64_t position) +{ + m_Position = position; +} + +void cStreamdevRecStreamer::Action(void) +{ + cTBSelect sel; + uint8_t data[32768]; + Dprintf("Writer start\n"); + + sel.Clear(); + sel.Add(*m_Socket, true); + while (Running()) { + + if (sel.Select(15000) == -1) { + esyslog("ERROR: streamdev-server: couldn't send recording data: %m"); + continue; /* Continue here instead of break, the recording playback can be paused */ + } + + if (sel.CanWrite(*m_Socket)) { + int written; + unsigned long readed = m_RecPlayer->getBlock(&*data, m_Position, sizeof(data)); + if (readed <= 0) + continue; + + if ((written = m_Socket->Write(&*data, readed)) == -1) { + esyslog("ERROR: streamdev-server: couldn't send %d bytes: %m", written); + break; + } + + m_Position += written; + m_TotalBytesWritten += written; + } + } +} diff -NaurwB streamdev/server/recordingstreamer.h streamdev-patched/server/recordingstreamer.h --- streamdev/server/recordingstreamer.h 1970-01-01 01:00:00.000000000 +0100 +++ streamdev-patched/server/recordingstreamer.h 2010-02-10 08:58:13.000000000 +0100 @@ -0,0 +1,38 @@ +#ifndef VDR_STREAMDEV_RECORDINGSTREAMER_H +#define VDR_STREAMDEV_RECORDINGSTREAMER_H + +#include <vdr/thread.h> +#include <vdr/config.h> +#include <vdr/receiver.h> + +#include "common.h" + +class cRecording; +class cRecPlayer; + +// --- cStreamdevRecStreamer ------------------------------------------------- + +class cStreamdevRecStreamer: public cThread { +private: + const cRecording *m_Recording; + cRecPlayer *m_RecPlayer; + cTBSocket *m_Socket; + uint64_t m_Position; + uint64_t m_TotalBytesWritten; + +protected: + virtual void Action(void); + +public: + cStreamdevRecStreamer(const cRecording *Recording); + virtual ~cStreamdevRecStreamer(); + + void Start(cTBSocket *Socket); + void Stop(void); + uint64_t getTotalBytesWritten() { return m_TotalBytesWritten; } + uint64_t getLengthBytes(); + uint32_t getLengthFrames(); + void seekPosition(uint64_t position); +}; + +#endif // VDR_STREAMDEV_RECORDINGSTREAMER_H diff -NaurwB streamdev/server/recplayer.c streamdev-patched/server/recplayer.c --- streamdev/server/recplayer.c 2009-07-01 13:00:49.000000000 +0200 +++ streamdev-patched/server/recplayer.c 2010-02-10 08:24:01.000000000 +0100 @@ -24,7 +24,7 @@ #define _XOPEN_SOURCE 600 #include <fcntl.h> -RecPlayer::RecPlayer(cRecording* rec) +cRecPlayer::cRecPlayer(const cRecording* rec) { file = NULL; fileOpen = 0; @@ -44,7 +44,7 @@ scan(); } -void RecPlayer::scan() +void cRecPlayer::scan() { if (file) fclose(file); totalLength = 0; @@ -60,7 +60,7 @@ #if APIVERSNUM < 10703 snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i); - //log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName); + //log->log("cRecPlayer", Log::DEBUG, "FILENAME: %s", fileName); file = fopen(fileName, "r"); #else snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i); @@ -77,7 +77,7 @@ fseek(file, 0, SEEK_END); totalLength += ftell(file); totalFrames = indexFile->Last(); - //log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames); + //log->log("cRecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames); segments[i]->end = totalLength; fclose(file); } @@ -85,15 +85,15 @@ file = NULL; } -RecPlayer::~RecPlayer() +cRecPlayer::~cRecPlayer() { - //log->log("RecPlayer", Log::DEBUG, "destructor"); + //log->log("cRecPlayer", Log::DEBUG, "destructor"); int i = 1; while(segments[i++]) delete segments[i]; if (file) fclose(file); } -int RecPlayer::openFile(int index) +int cRecPlayer::openFile(int index) { if (file) fclose(file); @@ -113,7 +113,7 @@ snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index); isyslog("openFile called for index %i string:%s", index, fileName); - //log->log("RecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName); + //log->log("cRecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName); file = fopen(fileName, "r"); if (file) @@ -122,38 +122,58 @@ return 1; } - //log->log("RecPlayer", Log::DEBUG, "file failed to open"); + //log->log("cRecPlayer", Log::DEBUG, "file failed to open"); fileOpen = 0; return 0; } -uint64_t RecPlayer::getLengthBytes() +uint64_t cRecPlayer::getLengthBytes() { + int totalLength = 0; + char fileName[2048]; + struct stat st; + + for(int i = 1; i < 1000; i++) + { +#if APIVERSNUM < 10703 + snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i); +#else + snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i); + if (stat(fileName, &st) < 0) { + snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i); + } +#endif + + if (stat(fileName, &st) == 0) { + totalLength += st.st_size; + } + } + return totalLength; } -uint32_t RecPlayer::getLengthFrames() +uint32_t cRecPlayer::getLengthFrames() { return totalFrames; } -unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsigned long amount) +unsigned long cRecPlayer::getBlock(uint8_t* buffer, uint64_t position, unsigned long amount) { if ((amount > totalLength) || (amount > 500000)) { - //log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount); + //log->log("cRecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount); return 0; } if (position >= totalLength) { - //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!"); + //log->log("cRecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!"); return 0; } if ((position + amount) > totalLength) { - //log->log("RecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount"); + //log->log("cRecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount"); amount = totalLength - position; } @@ -208,17 +228,17 @@ return got; } -uint64_t RecPlayer::getLastPosition() +uint64_t cRecPlayer::getLastPosition() { return lastPosition; } -cRecording* RecPlayer::getCurrentRecording() +const cRecording* cRecPlayer::getCurrentRecording() { return recording; } -uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber) +uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber) { if (!indexFile) return 0; @@ -235,21 +255,21 @@ return 0; } -// log->log("RecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset); +// log->log("cRecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset); if (!segments[retFileNumber]) return 0; uint64_t position = segments[retFileNumber]->start + retFileOffset; -// log->log("RecPlayer", Log::DEBUG, "Pos: %llu", position); +// log->log("cRecPlayer", Log::DEBUG, "Pos: %llu", position); return position; } -uint32_t RecPlayer::frameNumberFromPosition(uint64_t position) +uint32_t cRecPlayer::frameNumberFromPosition(uint64_t position) { if (!indexFile) return 0; if (position >= totalLength) { - //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!"); + //log->log("cRecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!"); return 0; } @@ -265,7 +285,7 @@ } -bool RecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength) +bool cRecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength) { // 0 = backwards // 1 = forwards @@ -276,7 +296,7 @@ int indexReturnFrameNumber; indexReturnFrameNumber = (uint32_t)indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), NULL, NULL, &iframeLength); - //log->log("RecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength); + //log->log("cRecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength); if (indexReturnFrameNumber == -1) return false; diff -NaurwB streamdev/server/recplayer.h streamdev-patched/server/recplayer.h --- streamdev/server/recplayer.h 2009-07-01 13:00:49.000000000 +0200 +++ streamdev-patched/server/recplayer.h 2010-02-09 17:18:23.000000000 +0100 @@ -33,24 +33,24 @@ uint64_t end; }; -class RecPlayer +class cRecPlayer { public: - RecPlayer(cRecording* rec); - ~RecPlayer(); + cRecPlayer(const cRecording* rec); + ~cRecPlayer(); uint64_t getLengthBytes(); uint32_t getLengthFrames(); - unsigned long getBlock(unsigned char* buffer, uint64_t position, unsigned long amount); + unsigned long getBlock(uint8_t* buffer, uint64_t position, unsigned long amount); int openFile(int index); uint64_t getLastPosition(); - cRecording* getCurrentRecording(); + const cRecording* getCurrentRecording(); void scan(); uint64_t positionFromFrameNumber(uint32_t frameNumber); uint32_t frameNumberFromPosition(uint64_t position); bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength); private: - cRecording* recording; + const cRecording* recording; cIndexFile* indexFile; FILE* file; int fileOpen;