2 Copyright 2008 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #ifndef VOMPSTANDALONE
24 #include <vdr/recording.h>
25 #include <vdr/channels.h>
26 #include <vdr/videodir.h>
27 #include <vdr/plugin.h>
28 #include <vdr/timers.h>
30 #include "recplayer.h"
31 #include "mvpreceiver.h"
34 #include "vompclientrrproc.h"
35 #include "vompclient.h"
40 VompClientRRProc::VompClientRRProc(VompClient& x)
43 log = Log::getInstance();
48 VompClientRRProc::~VompClientRRProc()
53 bool VompClientRRProc::init()
58 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
62 Now we have a queue system is used,
63 since on rare occasion the client fire two request at once
64 e.g. heavily channel switching
65 then processing only a single request would cause a deadlock in the client
70 req_queue.push(newRequest);
72 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
78 void VompClientRRProc::threadMethod()
81 log->log("RRProc", Log::DEBUG, "threadMethod startup");
83 if (req_queue.size() != 0)
85 log->log("RRProc", Log::ERR, "threadMethod err 1");
92 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
93 threadWaitForSignal(); // unlocks, waits, relocks
94 if (req_queue.size() == 0)
96 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
101 // signalled with something in queue
103 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
105 while (req_queue.size())
107 log->log("RRProc", Log::DEBUG, "thread while");
108 req = req_queue.front();
111 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
113 if (!processPacket())
115 log->log("RRProc", Log::ERR, "processPacket exited with fail");
122 // locked and run out of packets to process
126 bool VompClientRRProc::processPacket()
128 resp = new ResponsePacket();
129 if (!resp->init(req->requestID))
131 log->log("RRProc", Log::ERR, "response packet init fail");
134 if (req->data) free(req->data);
146 result = processLogin();
148 #ifndef VOMPSTANDALONE
150 result = processGetRecordingsList();
153 result = processDeleteRecording();
156 result = processGetChannelsList();
159 result = processStartStreamingChannel();
162 result = processGetBlock();
165 result = processStopStreaming();
168 result = processStartStreamingRecording();
172 result = processGetChannelSchedule();
175 result = processConfigSave();
178 result = processConfigLoad();
180 #ifndef VOMPSTANDALONE
182 result = processReScanRecording(); // FIXME obselete
185 result = processGetTimers();
188 result = processSetTimer();
191 result = processPositionFromFrameNumber();
194 result = processFrameNumberFromPosition();
197 result = processMoveRecording();
200 result = processGetIFrame();
203 result = processGetRecInfo();
206 result = processGetMarks();
209 result = processGetChannelPids();
212 result = processDeleteTimer();
216 result = processGetMediaList();
219 result = processGetPicture();
222 result = processGetImageBlock();
225 result = processGetLanguageList();
228 result = processGetLanguageContent();
235 if (req->data) free(req->data);
239 if (result) return true;
243 int VompClientRRProc::processLogin()
245 if (req->dataLength != 6) return 0;
249 char configFileName[PATH_MAX];
250 snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", x.configDir, req->data[0], req->data[1], req->data[2], req->data[3], req->data[4], req->data[5]);
251 x.config.init(configFileName);
253 // Send the login reply
255 time_t timeNow = time(NULL);
256 struct tm* timeStruct = localtime(&timeNow);
257 int timeOffset = timeStruct->tm_gmtoff;
259 resp->addULONG(timeNow);
260 resp->addLONG(timeOffset);
262 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
263 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
269 int VompClientRRProc::processConfigSave()
271 char* section = (char*)req->data;
275 for (UINT k = 0; k < req->dataLength; k++)
277 if (req->data[k] == '\0')
281 key = (char*)&req->data[k+1];
285 value = (char*)&req->data[k+1];
291 // if the last string (value) doesnt have null terminator, give up
292 if (req->data[req->dataLength - 1] != '\0') return 0;
294 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
295 if (x.config.setValueString(section, key, value))
305 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
310 int VompClientRRProc::processConfigLoad()
312 char* section = (char*)req->data;
315 for (UINT k = 0; k < req->dataLength; k++)
317 if (req->data[k] == '\0')
319 key = (char*)&req->data[k+1];
324 char* value = x.config.getValueString(section, key);
328 resp->addString(value);
329 log->log("RRProc", Log::DEBUG, "Written config load packet");
335 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
339 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
345 * media List Request:
348 * 4 flags (currently unused)
351 * Media List response:
359 * 4 strlen (incl. 0 Byte)
364 int VompClientRRProc::processGetMediaList()
366 if (req->dataLength < 4) {
367 log->log("RRProc", Log::ERR, "getMediaList packet too short %d", req->dataLength);
371 if (req->dataLength > 4) {
372 //we have a dirname provided
373 dirname=(char *)&req->data[4];
374 log->log("RRProc", Log::DEBUG, "getMediaList for %s", dirname);
377 MediaList * ml=MediaList::readList(x.baseConfig, dirname);
379 log->log("RRProc", Log::ERR, "getMediaList returned NULL");
383 //response code (not yet set)
387 resp->addULONG(ml->size());
389 for (MediaList::iterator nm=ml->begin(); nm<ml->end(); nm++)
392 log->log("RRProc", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
393 resp->addULONG(m->getType());
395 resp->addULONG(m->getTime());
398 int len=strlen(m->getFilename());
400 resp->addULONG(len+1);
401 resp->addString(m->getFilename());
406 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
408 log->log("RRProc", Log::DEBUG, "Written Media list");
414 * 4 flags (currently unused)
419 * get image response:
425 int VompClientRRProc::processGetPicture()
427 if (req->dataLength < 12) {
428 log->log("RRProc", Log::ERR, "getPicture packet too short %d", req->dataLength);
435 char * filename=NULL;
436 if (req->dataLength > 12) {
437 //we have a dirname provided
438 filename=(char *)&req->data[12];
439 log->log("RRProc", Log::DEBUG, "getPicture %s", filename);
442 log->log("RRProc", Log::ERR, "getPicture empty filename");
445 x.imageFile=fopen(filename,"r");
446 if (!x.imageFile) log->log("RRProc", Log::ERR, "getPicture unable to open %s",filename);
451 if ( fstat(fileno(x.imageFile),&st) == 0) size=st.st_size;
453 //response code (not yet set)
456 resp->addULONG(size);
458 log->log("RRProc", Log::DEBUG, "getPicture size %u", size);
461 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
463 log->log("RRProc", Log::DEBUG, "Written getPicture");
469 int VompClientRRProc::processGetImageBlock()
473 log->log("RRProc", Log::DEBUG, "Get image block called when no image active");
477 UCHAR* data = req->data;
479 ULLONG position = x.ntohll(*(ULLONG*)data);
480 data += sizeof(ULLONG);
481 ULONG amount = ntohl(*(ULONG*)data);
483 log->log("RRProc", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
485 UCHAR sendBuffer[amount];
486 ULONG amountReceived = 0; // compiler moan.
487 ULLONG cpos=ftell(x.imageFile);
488 if (position != cpos) {
489 fseek(x.imageFile,position-cpos,SEEK_CUR);
491 if (position != (ULLONG)ftell(x.imageFile)) {
492 log->log("RRProc", Log::DEBUG, "getImageblock pos = %llu not available", position);
495 amountReceived=fread(&sendBuffer[0],1,amount,x.imageFile);
501 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
505 resp->copyin(sendBuffer, amount);
506 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
510 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
516 int VompClientRRProc::processGetLanguageList()
518 x.i18n.findLanguages();
519 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
521 I18n::lang_code_list::const_iterator iter;
522 for (iter = languages.begin(); iter != languages.end(); ++iter)
524 resp->addString(iter->first.c_str());
525 resp->addString(iter->second.c_str());
528 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
532 int VompClientRRProc::processGetLanguageContent()
534 if (req->dataLength <= 0) return 0;
535 std::string code, result;
536 code.assign((char*)req->data, req->dataLength - 1);
537 x.i18n.findLanguages();
538 I18n::trans_table texts = x.i18n.getLanguageContent(code);
539 I18n::trans_table::const_iterator iter;
540 for (iter = texts.begin(); iter != texts.end(); ++iter)
542 resp->addString(iter->first.c_str());
543 resp->addString(iter->second.c_str());
546 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
550 #ifndef VOMPSTANDALONE
552 int VompClientRRProc::processGetRecordingsList()
555 int Percent = VideoDiskSpace(&FreeMB);
556 int Total = (FreeMB / (100 - Percent)) * 100;
558 resp->addULONG(Total);
559 resp->addULONG(FreeMB);
560 resp->addULONG(Percent);
562 cRecordings Recordings;
565 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
567 resp->addULONG(recording->start);
568 resp->addString(recording->Name());
569 resp->addString(recording->FileName());
573 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
575 log->log("RRProc", Log::DEBUG, "Written recordings list");
580 int VompClientRRProc::processDeleteRecording()
582 // data is a pointer to the fileName string
584 cRecordings Recordings;
585 Recordings.Load(); // probably have to do this
587 cRecording* recording = Recordings.GetByName((char*)req->data);
589 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
593 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
595 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
598 if (recording->Delete())
600 // Copy svdrp's way of doing this, see if it works
601 #if VDRVERSNUM > 10300
602 ::Recordings.DelByName(recording->FileName());
622 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
627 int VompClientRRProc::processMoveRecording()
629 log->log("RRProc", Log::DEBUG, "Process move recording");
630 char* fileName = (char*)req->data;
631 char* newPath = NULL;
633 for (UINT k = 0; k < req->dataLength; k++)
635 if (req->data[k] == '\0')
637 newPath = (char*)&req->data[k+1];
641 if (!newPath) return 0;
643 cRecordings Recordings;
644 Recordings.Load(); // probably have to do this
646 cRecording* recording = Recordings.GetByName((char*)fileName);
648 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
652 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
655 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
656 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
657 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
659 const char* t = recording->FileName();
661 char* dateDirName = NULL; int k;
662 char* titleDirName = NULL; int j;
664 // Find the datedirname
665 for(k = strlen(t) - 1; k >= 0; k--)
669 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
670 dateDirName = new char[strlen(&t[k+1]) + 1];
671 strcpy(dateDirName, &t[k+1]);
676 // Find the titledirname
678 for(j = k-1; j >= 0; j--)
682 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
683 titleDirName = new char[(k - j - 1) + 1];
684 memcpy(titleDirName, &t[j+1], k - j - 1);
685 titleDirName[k - j - 1] = '\0';
690 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
691 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
692 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
694 char* newPathConv = new char[strlen(newPath)+1];
695 strcpy(newPathConv, newPath);
696 ExchangeChars(newPathConv, true);
697 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
699 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
700 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
701 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
702 delete[] newPathConv;
704 log->log("RRProc", Log::DEBUG, "%s", newContainer);
707 int statret = stat(newContainer, &dstat);
708 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
710 log->log("RRProc", Log::DEBUG, "new dir does not exist");
711 int mkdirret = mkdir(newContainer, 0755);
714 delete[] dateDirName;
715 delete[] titleDirName;
716 delete[] newContainer;
720 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
724 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
726 delete[] dateDirName;
727 delete[] titleDirName;
728 delete[] newContainer;
732 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
736 // Ok, the directory container has been made, or it pre-existed.
738 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
739 sprintf(newDir, "%s/%s", newContainer, dateDirName);
741 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
742 int renameret = rename(t, newDir);
745 // Success. Test for remove old dir containter
746 char* oldTitleDir = new char[k+1];
747 memcpy(oldTitleDir, t, k);
748 oldTitleDir[k] = '\0';
749 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
750 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
751 delete[] oldTitleDir;
756 #if VDRVERSNUM > 10311
758 ::Recordings.Update();
760 // Success. Send a different packet from just a ulong
761 resp->addULONG(1); // success
762 resp->addString(newDir);
770 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
772 delete[] dateDirName;
773 delete[] titleDirName;
774 delete[] newContainer;
781 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
788 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
794 int VompClientRRProc::processGetChannelsList()
798 char* chanConfig = x.config.getValueString("General", "Channels");
800 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
802 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
804 #if VDRVERSNUM < 10300
805 if (!channel->GroupSep() && (!channel->Ca() || allChans))
807 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
810 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
812 if (channel->Vpid()) type = 1;
813 #if VDRVERSNUM < 10300
816 else if (channel->Apid(0)) type = 2;
820 resp->addULONG(channel->Number());
821 resp->addULONG(type);
822 resp->addString(channel->Name());
827 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
829 log->log("RRProc", Log::DEBUG, "Written channels list");
834 int VompClientRRProc::processGetChannelPids()
836 ULONG channelNumber = ntohl(*(ULONG*)req->data);
838 cChannel* channel = x.channelFromNumber(channelNumber);
843 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
852 #if VDRVERSNUM < 10300
854 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
855 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
857 if (channel->Apid2())
859 else if (channel->Apid1())
866 for (const int *Apid = channel->Apids(); *Apid; Apid++)
870 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
874 for (const int *Spid = channel->Spids(); *Spid; Spid++)
881 // Format of response
900 resp->addULONG(channel->Vpid());
901 resp->addULONG(numApids);
903 #if VDRVERSNUM < 10300
906 resp->addULONG(channel->Apid1());
911 resp->addULONG(channel->Apid2());
917 for (ULONG i = 0; i < numApids; i++)
919 resp->addULONG(channel->Apid(i));
920 resp->addString(channel->Alang(i));
922 resp->addULONG(numDpids);
923 for (ULONG i = 0; i < numDpids; i++)
925 resp->addULONG(channel->Dpid(i));
926 resp->addString(channel->Dlang(i));
928 resp->addULONG(numSpids);
929 for (ULONG i = 0; i < numSpids; i++)
931 resp->addULONG(channel->Spid(i));
932 resp->addString(channel->Slang(i));
935 resp->addULONG(channel->Tpid());
939 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
941 log->log("RRProc", Log::DEBUG, "Written channels pids");
946 int VompClientRRProc::processStartStreamingChannel()
950 log->log("RRProc", Log::ERR, "Client called start streaming twice");
954 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
955 ULONG channelNumber = ntohl(*(ULONG*)req->data);
957 cChannel* channel = x.channelFromNumber(channelNumber);
962 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
966 // get the priority we should use
968 int priority = x.config.getValueLong("General", "Live priority", &fail);
971 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
975 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
980 if (priority < 0) priority = 0;
981 if (priority > 99) priority = 99;
983 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
984 x.lp = MVPReceiver::create(channel, priority);
990 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
994 if (!x.lp->init(&x.tcp, req->requestID))
1000 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1006 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1010 int VompClientRRProc::processStopStreaming()
1012 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1018 else if (x.recplayer)
1020 x.writeResumeData();
1023 delete x.recordingManager;
1025 x.recordingManager = NULL;
1030 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1034 int VompClientRRProc::processGetBlock()
1036 if (!x.lp && !x.recplayer)
1038 log->log("RRProc", Log::DEBUG, "Get block called when no streaming happening!");
1042 UCHAR* data = req->data;
1044 ULLONG position = x.ntohll(*(ULLONG*)data);
1045 data += sizeof(ULLONG);
1046 ULONG amount = ntohl(*(ULONG*)data);
1048 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1050 UCHAR sendBuffer[amount];
1051 ULONG amountReceived = 0; // compiler moan.
1054 log->log("RRProc", Log::DEBUG, "getting from live");
1055 amountReceived = x.lp->getBlock(&sendBuffer[0], amount);
1057 if (!amountReceived)
1059 // vdr has possibly disconnected the receiver
1060 log->log("RRProc", Log::DEBUG, "VDR has disconnected the live receiver");
1065 else if (x.recplayer)
1067 log->log("RRProc", Log::DEBUG, "getting from recording");
1068 amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1071 if (!amountReceived)
1074 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1078 resp->copyin(sendBuffer, amountReceived);
1079 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1083 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1084 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1088 int VompClientRRProc::processStartStreamingRecording()
1090 // data is a pointer to the fileName string
1092 x.recordingManager = new cRecordings;
1093 x.recordingManager->Load();
1095 cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1097 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1101 x.recplayer = new RecPlayer(recording);
1103 resp->addULLONG(x.recplayer->getLengthBytes());
1104 resp->addULONG(x.recplayer->getLengthFrames());
1106 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1108 log->log("RRProc", Log::DEBUG, "written totalLength");
1112 delete x.recordingManager;
1113 x.recordingManager = NULL;
1118 int VompClientRRProc::processPositionFromFrameNumber()
1122 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1126 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1130 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1133 resp->addULLONG(retval);
1135 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1137 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1141 int VompClientRRProc::processFrameNumberFromPosition()
1145 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1149 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1153 retval = x.recplayer->frameNumberFromPosition(position);
1156 resp->addULONG(retval);
1158 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1160 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1164 int VompClientRRProc::processGetIFrame()
1166 bool success = false;
1168 ULONG* data = (ULONG*)req->data;
1170 ULONG frameNumber = ntohl(*data);
1172 ULONG direction = ntohl(*data);
1174 ULLONG rfilePosition = 0;
1175 ULONG rframeNumber = 0;
1176 ULONG rframeLength = 0;
1180 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1184 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1187 // returns file position, frame number, length
1191 resp->addULLONG(rfilePosition);
1192 resp->addULONG(rframeNumber);
1193 resp->addULONG(rframeLength);
1201 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1203 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1207 int VompClientRRProc::processGetChannelSchedule()
1209 ULONG* data = (ULONG*)req->data;
1211 ULONG channelNumber = ntohl(*data);
1213 ULONG startTime = ntohl(*data);
1215 ULONG duration = ntohl(*data);
1217 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1219 cChannel* channel = x.channelFromNumber(channelNumber);
1224 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1226 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1230 log->log("RRProc", Log::DEBUG, "Got channel");
1232 #if VDRVERSNUM < 10300
1233 cMutexLock MutexLock;
1234 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1236 cSchedulesLock MutexLock;
1237 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1243 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1245 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1249 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1251 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1256 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1258 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1262 log->log("RRProc", Log::DEBUG, "Got schedule object");
1264 const char* empty = "";
1265 bool atLeastOneEvent = false;
1268 ULONG thisEventTime;
1269 ULONG thisEventDuration;
1270 const char* thisEventTitle;
1271 const char* thisEventSubTitle;
1272 const char* thisEventDescription;
1274 #if VDRVERSNUM < 10300
1276 const cEventInfo *event;
1277 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1279 event = Schedule->GetEventNumber(eventNumber);
1281 thisEventID = event->GetEventID();
1282 thisEventTime = event->GetTime();
1283 thisEventDuration = event->GetDuration();
1284 thisEventTitle = event->GetTitle();
1285 thisEventSubTitle = event->GetSubtitle();
1286 thisEventDescription = event->GetExtendedDescription();
1290 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1292 thisEventID = event->EventID();
1293 thisEventTime = event->StartTime();
1294 thisEventDuration = event->Duration();
1295 thisEventTitle = event->Title();
1296 thisEventSubTitle = NULL;
1297 thisEventDescription = event->Description();
1301 //in the past filter
1302 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1305 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1308 if (thisEventTime >= (startTime + duration)) continue;
1310 if (!thisEventTitle) thisEventTitle = empty;
1311 if (!thisEventSubTitle) thisEventSubTitle = empty;
1312 if (!thisEventDescription) thisEventDescription = empty;
1314 resp->addULONG(thisEventID);
1315 resp->addULONG(thisEventTime);
1316 resp->addULONG(thisEventDuration);
1318 resp->addString(thisEventTitle);
1319 resp->addString(thisEventSubTitle);
1320 resp->addString(thisEventDescription);
1322 atLeastOneEvent = true;
1325 log->log("RRProc", Log::DEBUG, "Got all event data");
1327 if (!atLeastOneEvent)
1330 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1334 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1336 log->log("RRProc", Log::DEBUG, "written schedules packet");
1341 int VompClientRRProc::processGetTimers()
1344 int numTimers = Timers.Count();
1346 resp->addULONG(numTimers);
1348 for (int i = 0; i < numTimers; i++)
1350 timer = Timers.Get(i);
1352 #if VDRVERSNUM < 10300
1353 resp->addULONG(timer->Active());
1355 resp->addULONG(timer->HasFlags(tfActive));
1357 resp->addULONG(timer->Recording());
1358 resp->addULONG(timer->Pending());
1359 resp->addULONG(timer->Priority());
1360 resp->addULONG(timer->Lifetime());
1361 resp->addULONG(timer->Channel()->Number());
1362 resp->addULONG(timer->StartTime());
1363 resp->addULONG(timer->StopTime());
1364 resp->addULONG(timer->Day());
1365 resp->addULONG(timer->WeekDays());
1366 resp->addString(timer->File());
1370 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1372 log->log("RRProc", Log::DEBUG, "Written timers list");
1377 int VompClientRRProc::processSetTimer()
1379 char* timerString = new char[strlen((char*)req->data) + 1];
1380 strcpy(timerString, (char*)req->data);
1382 #if VDRVERSNUM < 10300
1384 // If this is VDR 1.2 the date part of the timer string must be reduced
1385 // to just DD rather than YYYY-MM-DD
1387 int s = 0; // source
1388 int d = 0; // destination
1390 while(c != 2) // copy up to date section, including the second ':'
1392 timerString[d] = req->data[s];
1393 if (req->data[s] == ':') c++;
1397 // now it has copied up to the date section
1399 while(c != 2) // waste YYYY-MM-
1401 if (req->data[s] == '-') c++;
1404 // now source is at the DD
1405 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1406 d += req->dataLength - s;
1407 timerString[d] = '\0';
1409 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1410 log->log("RRProc", Log::DEBUG, "%s", timerString);
1414 cTimer *timer = new cTimer;
1415 if (timer->Parse((char*)timerString))
1417 cTimer *t = Timers.GetTimer(timer);
1421 #if VDRVERSNUM < 10300
1424 Timers.SetModified();
1428 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1435 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1442 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1448 int VompClientRRProc::processDeleteTimer()
1450 log->log("RRProc", Log::DEBUG, "Delete timer called");
1455 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1456 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1457 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1458 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1459 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1462 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1464 if ( (ti->Channel()->Number() == delChannel)
1465 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1466 && (ti->StartTime() == delStart)
1467 && (ti->StopTime() == delStop) )
1475 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1479 if (!Timers.BeingEdited())
1481 if (!ti->Recording())
1484 Timers.SetModified();
1487 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1492 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1495 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1501 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1504 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1509 int VompClientRRProc::processGetRecInfo()
1511 // data is a pointer to the fileName string
1513 cRecordings Recordings;
1514 Recordings.Load(); // probably have to do this
1516 cRecording *recording = Recordings.GetByName((char*)req->data);
1518 time_t timerStart = 0;
1519 time_t timerStop = 0;
1520 char* summary = NULL;
1521 ULONG resumePoint = 0;
1525 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1528 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1533 4 bytes: start time for timer
1534 4 bytes: end time for timer
1535 4 bytes: resume point
1537 4 bytes: num components
1547 // Get current timer
1549 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1552 timerStart = rc->Timer()->StartTime();
1553 timerStop = rc->Timer()->StopTime();
1554 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1557 resp->addULONG(timerStart);
1558 resp->addULONG(timerStop);
1562 char* value = x.config.getValueString("ResumeData", (char*)req->data);
1565 resumePoint = strtoul(value, NULL, 10);
1568 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1570 resp->addULONG(resumePoint);
1575 #if VDRVERSNUM < 10300
1576 summary = (char*)recording->Summary();
1578 const cRecordingInfo *Info = recording->Info();
1579 summary = (char*)Info->ShortText();
1580 if (isempty(summary)) summary = (char*)Info->Description();
1582 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1585 resp->addString(summary);
1589 resp->addString("");
1594 #if VDRVERSNUM < 10300
1596 // Send 0 for numchannels - this signals the client this info is not available
1600 const cComponents* components = Info->Components();
1602 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1610 resp->addULONG(components->NumComponents());
1612 tComponent* component;
1613 for (int i = 0; i < components->NumComponents(); i++)
1615 component = components->Component(i);
1617 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1619 resp->addUCHAR(component->stream);
1620 resp->addUCHAR(component->type);
1622 if (component->language)
1624 resp->addString(component->language);
1628 resp->addString("");
1630 if (component->description)
1632 resp->addString(component->description);
1636 resp->addString("");
1646 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1648 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1658 int VompClientRRProc::processReScanRecording()
1662 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1666 x.recplayer->scan();
1668 resp->addULLONG(x.recplayer->getLengthBytes());
1669 resp->addULONG(x.recplayer->getLengthFrames());
1671 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1672 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1676 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1678 int VompClientRRProc::processGetMarks()
1680 // data is a pointer to the fileName string
1683 cRecordings Recordings;
1684 Recordings.Load(); // probably have to do this
1686 cRecording *recording = Recordings.GetByName((char*)req->data);
1688 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1692 Marks.Load(recording->FileName());
1695 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1697 log->log("RRProc", Log::DEBUG, "found Mark %i", m->position);
1699 resp->addULONG(m->position);
1704 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1710 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1712 log->log("RRProc", Log::DEBUG, "Written Marks list");
1717 #endif // !VOMPSTANDALONE