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 <vdr/remote.h>
31 #include "recplayer.h"
32 #include "mvpreceiver.h"
35 #include "vompclientrrproc.h"
36 #include "vompclient.h"
39 #include "mediaplayer.h"
40 #include "servermediafile.h"
42 #include "vdrcommand.h"
46 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION = 0x00000301;
48 // cc is release protocol version, increase with every release, that changes protocol
49 // dd is development protocol version, set to zero at every release,
50 // increase for every protocol change in git
51 // bb not equal zero should indicate a non loggytronic protocol
52 // aa is reserved for future use
54 ULONG VompClientRRProc::getProtocolVersion()
56 return VOMP_PROTOCOL_VERSION;
59 VompClientRRProc::VompClientRRProc(VompClient& x)
62 log = Log::getInstance();
67 VompClientRRProc::~VompClientRRProc()
72 bool VompClientRRProc::init()
74 int a = threadStart();
79 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
83 Now we have a queue system is used,
84 since on rare occasion the client fire two request at once
85 e.g. heavily channel switching
86 then processing only a single request would cause a deadlock in the client
90 log->log("RRProc", Log::DEBUG, "recvReq");
92 req_queue.push(newRequest);
94 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
100 void VompClientRRProc::threadMethod()
103 log->log("RRProc", Log::DEBUG, "threadMethod startup");
105 if (req_queue.size() != 0)
108 - log->log("RRProc", Log::ERR, "threadMethod err 1");
112 That was how the code used to be.
114 TODO: Work out why this happens.
117 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");
118 while (req_queue.size())
120 //log->log("RRProc", Log::DEBUG, "thread while");
121 req = req_queue.front();
124 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
126 if (!processPacket())
128 log->log("RRProc", Log::ERR, "processPacket exited with fail");
134 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");
140 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
141 threadWaitForSignal(); // unlocks, waits, relocks
142 if (req_queue.size() == 0)
144 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
149 // signalled with something in queue
151 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
153 while (req_queue.size())
155 //log->log("RRProc", Log::DEBUG, "thread while");
156 req = req_queue.front();
159 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
161 if (!processPacket())
163 log->log("RRProc", Log::ERR, "processPacket exited with fail");
170 // locked and run out of packets to process
174 bool VompClientRRProc::processPacket()
176 resp = new ResponsePacket();
177 if (!resp->init(req->requestID))
179 log->log("RRProc", Log::ERR, "response packet init fail");
182 if (req->data) free(req->data);
194 result = processLogin();
196 #ifndef VOMPSTANDALONE
198 result = processGetRecordingsList();
201 result = processDeleteRecording();
204 result = processGetChannelsList();
207 result = processStartStreamingChannel();
210 result = processGetBlock();
213 result = processStopStreaming();
216 result = processStartStreamingRecording();
219 result = processGetChannelSchedule();
223 result = processConfigSave();
226 result = processConfigLoad();
228 #ifndef VOMPSTANDALONE
230 result = processReScanRecording(); // FIXME obselete
233 result = processGetTimers();
236 result = processSetTimer();
239 result = processPositionFromFrameNumber();
242 result = processFrameNumberFromPosition();
245 result = processMoveRecording();
248 result = processGetIFrame();
251 result = processGetRecInfo();
254 result = processGetMarks();
257 result = processGetChannelPids();
260 result = processDeleteTimer();
263 result = processVDRShutdown();
266 case VDR_GETMEDIALIST:
267 result = processGetMediaList();
270 result = processOpenMedia();
272 case VDR_GETMEDIABLOCK:
273 result = processGetMediaBlock();
276 result = processGetLanguageList();
279 result = processGetLanguageContent();
281 case VDR_GETMEDIAINFO:
282 result = processGetMediaInfo();
284 case VDR_CLOSECHANNEL:
285 result = processCloseMediaChannel();
288 result = processSetCharset();
295 if (req->data) free(req->data);
299 if (result) return true;
303 int VompClientRRProc::processLogin()
305 if (req->dataLength != 6) return 0;
309 char configFileName[PATH_MAX];
310 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]);
311 x.config.init(configFileName);
313 // Send the login reply
315 time_t timeNow = time(NULL);
316 struct tm* timeStruct = localtime(&timeNow);
317 int timeOffset = timeStruct->tm_gmtoff;
319 resp->addULONG(timeNow);
320 resp->addLONG(timeOffset);
321 resp->addULONG(VOMP_PROTOCOL_VERSION);
323 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
324 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
327 x.netLog(); // safe to run here since the client won't start net logging for a while yet
332 int VompClientRRProc::processSetCharset()
334 int charset = ntohl(*(ULONG*)req->data);
335 if (charset>0 && charset<3)
337 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
338 x.setCharset(charset);
343 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
347 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
351 int VompClientRRProc::processConfigSave()
353 char* section = (char*)req->data;
357 for (UINT k = 0; k < req->dataLength; k++)
359 if (req->data[k] == '\0')
363 key = (char*)&req->data[k+1];
367 value = (char*)&req->data[k+1];
373 // if the last string (value) doesnt have null terminator, give up
374 if (req->data[req->dataLength - 1] != '\0') return 0;
376 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
377 if (x.config.setValueString(section, key, value))
387 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
392 int VompClientRRProc::processConfigLoad()
394 char* section = (char*)req->data;
397 for (UINT k = 0; k < req->dataLength; k++)
399 if (req->data[k] == '\0')
401 key = (char*)&req->data[k+1];
406 char* value = x.config.getValueString(section, key);
410 resp->addString(value);//client coding, do not touch
411 log->log("RRProc", Log::DEBUG, "Written config load packet");
417 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
421 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
427 //helper for sending from a serialize buffer
428 //insert the used len into the first 4 Bytes of the buffer
429 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
430 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
432 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
436 * media List Request:
437 * Media List response:
440 #define MLISTBUF 500000
441 int VompClientRRProc::processGetMediaList()
443 SerializeBuffer buffer(req->data,req->dataLength);
444 MediaURI uri(0,NULL,NULL);
445 VDR_GetMediaListRequest request(&uri);
446 if (request.deserialize(&buffer) != 0) {
447 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
450 const char *dirname=uri.getName();
451 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
454 if (dirname == NULL) {
455 ml=x.media->getRootList();
457 ml=x.media->getMediaList(&uri);
460 log->log("Client", Log::ERR, "getMediaList returned NULL");
463 SerializeBuffer rbuf(MLISTBUF,false,true);
464 ULONG flags=0; //TODO: real error handling by setting flags
465 VDR_GetMediaListResponse response(&flags,ml);
466 if (response.serialize(&rbuf) != 0) {
467 log->log("Client", Log::ERR, "getMediaList returned NULL");
471 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
476 log->log("Client", Log::DEBUG, "Written Media list");
481 * openMedia response:
483 int VompClientRRProc::processOpenMedia()
485 SerializeBuffer buffer(req->data,req->dataLength);
486 MediaURI uri(0,NULL,NULL);
490 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
491 if (request.deserialize(&buffer) != 0) {
492 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
495 const char *name=uri.getName();
496 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
498 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
503 log->log("Client", Log::ERR, "openMediaRequest unable to open");
505 VDR_OpenMediumResponse response(&flags,&size);
506 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
507 if (response.serialize(&rbuf) != 0) {
508 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
511 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
518 * packet - no serialized response!
520 int VompClientRRProc::processGetMediaBlock()
522 SerializeBuffer buffer(req->data,req->dataLength);
526 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
527 if (request.deserialize(&buffer) != 0) {
528 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
531 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
533 UCHAR sendBuffer[amount ];
534 ULONG amountReceived = 0;
535 UCHAR *rbuf=sendBuffer;
536 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
537 if (!amountReceived || rt != 0)
539 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
543 if (rbuf != sendBuffer) {
544 //the provider did not use the optimized handling with using my buffer
545 resp->copyin(rbuf,amountReceived);
548 // the provider did not allocate a new buffer
549 resp->copyin(sendBuffer,amountReceived);
553 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
554 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
561 int VompClientRRProc::processGetMediaInfo()
563 SerializeBuffer buffer(req->data,req->dataLength);
565 VDR_GetMediaInfoRequest request(&channel);
566 if (request.deserialize(&buffer) != 0) {
567 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
570 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
573 int rt=x.media->getMediaInfo(channel,&mi);
576 log->log("Client", Log::ERR, "getMediaInfo unable to get");
578 VDR_GetMediaInfoResponse response(&flags,&mi);
579 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
580 if (response.serialize(&rbuf) != 0) {
581 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
593 int VompClientRRProc::processCloseMediaChannel()
595 SerializeBuffer buffer(req->data,req->dataLength);
597 VDR_CloseMediaChannelRequest request(&channel);
598 if (request.deserialize(&buffer) != 0) {
599 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
603 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
604 int rt=x.media->closeMediaChannel(channel);
607 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
609 VDR_CloseMediaChannelResponse response(&flags);
610 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
611 if (response.serialize(&rbuf) != 0) {
612 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
621 int VompClientRRProc::processGetLanguageList()
623 x.i18n.findLanguages();
624 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
626 I18n::lang_code_list::const_iterator iter;
627 for (iter = languages.begin(); iter != languages.end(); ++iter)
629 resp->addString(iter->first.c_str()); // Source code is acsii
630 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
633 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
637 int VompClientRRProc::processGetLanguageContent()
639 if (req->dataLength <= 0) return 0;
640 std::string code, result;
641 code.assign((char*)req->data, req->dataLength - 1);
642 x.i18n.findLanguages();
643 I18n::trans_table texts = x.i18n.getLanguageContent(code);
644 I18n::trans_table::const_iterator iter;
645 for (iter = texts.begin(); iter != texts.end(); ++iter)
647 resp->addString(iter->first.c_str());// source code is acsii since it is english
648 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
651 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
655 #ifndef VOMPSTANDALONE
657 int VompClientRRProc::processGetRecordingsList()
660 int Percent = VideoDiskSpace(&FreeMB);
661 int Total = (FreeMB / (100 - Percent)) * 100;
663 resp->addULONG(Total);
664 resp->addULONG(FreeMB);
665 resp->addULONG(Percent);
667 cRecordings Recordings;
670 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
672 #if VDRVERSNUM < 10721
673 resp->addULONG(recording->start);
675 resp->addULONG(recording->Start());
677 resp->addUCHAR(recording->IsNew() ? 1 : 0);
678 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
679 resp->addString(recording->FileName());//file name are not visible by user do not touch
683 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
685 log->log("RRProc", Log::DEBUG, "Written recordings list");
690 int VompClientRRProc::processDeleteRecording()
692 // data is a pointer to the fileName string
694 cRecordings Recordings;
695 Recordings.Load(); // probably have to do this
697 cRecording* recording = Recordings.GetByName((char*)req->data);
699 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
703 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
705 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
708 if (recording->Delete())
710 // Copy svdrp's way of doing this, see if it works
711 #if VDRVERSNUM > 10300
712 ::Recordings.DelByName(recording->FileName());
732 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
737 int VompClientRRProc::processMoveRecording()
739 log->log("RRProc", Log::DEBUG, "Process move recording");
740 char* fileName = (char*)req->data;
741 char* newPath = NULL;
743 for (UINT k = 0; k < req->dataLength; k++)
745 if (req->data[k] == '\0')
747 newPath = (char*)&req->data[k+1];
751 if (!newPath) return 0;
753 cRecordings Recordings;
754 Recordings.Load(); // probably have to do this
756 cRecording* recording = Recordings.GetByName((char*)fileName);
758 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
762 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
765 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
766 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
767 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
769 const char* t = recording->FileName();
771 char* dateDirName = NULL; int k;
772 char* titleDirName = NULL; int j;
774 // Find the datedirname
775 for(k = strlen(t) - 1; k >= 0; k--)
779 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
780 dateDirName = new char[strlen(&t[k+1]) + 1];
781 strcpy(dateDirName, &t[k+1]);
786 // Find the titledirname
788 for(j = k-1; j >= 0; j--)
792 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
793 titleDirName = new char[(k - j - 1) + 1];
794 memcpy(titleDirName, &t[j+1], k - j - 1);
795 titleDirName[k - j - 1] = '\0';
800 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
801 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
802 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
804 char* newPathConv = new char[strlen(newPath)+1];
805 strcpy(newPathConv, newPath);
806 ExchangeChars(newPathConv, true);
807 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
809 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
810 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
811 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
812 delete[] newPathConv;
814 log->log("RRProc", Log::DEBUG, "%s", newContainer);
817 int statret = stat(newContainer, &dstat);
818 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
820 log->log("RRProc", Log::DEBUG, "new dir does not exist");
821 int mkdirret = mkdir(newContainer, 0755);
824 delete[] dateDirName;
825 delete[] titleDirName;
826 delete[] newContainer;
830 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
834 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
836 delete[] dateDirName;
837 delete[] titleDirName;
838 delete[] newContainer;
842 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
846 // Ok, the directory container has been made, or it pre-existed.
848 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
849 sprintf(newDir, "%s/%s", newContainer, dateDirName);
851 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
852 int renameret = rename(t, newDir);
855 // Success. Test for remove old dir containter
856 char* oldTitleDir = new char[k+1];
857 memcpy(oldTitleDir, t, k);
858 oldTitleDir[k] = '\0';
859 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
860 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
861 delete[] oldTitleDir;
866 #if VDRVERSNUM > 10311
868 ::Recordings.Update();
870 // Success. Send a different packet from just a ulong
871 resp->addULONG(1); // success
872 resp->addString(newDir); //system depent do not convert
880 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
882 delete[] dateDirName;
883 delete[] titleDirName;
884 delete[] newContainer;
891 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
898 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
904 int VompClientRRProc::processGetChannelsList()
908 char* chanConfig = x.config.getValueString("General", "Channels");
910 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
912 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
914 #if VDRVERSNUM < 10300
915 if (!channel->GroupSep() && (!channel->Ca() || allChans))
917 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
920 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
922 if (channel->Vpid()) type = 1;
923 #if VDRVERSNUM < 10300
926 else if (channel->Apid(0)) type = 2;
930 resp->addULONG(channel->Number());
931 resp->addULONG(type);
932 resp->addString(x.charconvsys->Convert(channel->Name()));
933 #if VDRVERSNUM < 10703
936 resp->addULONG(channel->Vtype());
942 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
944 log->log("RRProc", Log::DEBUG, "Written channels list");
949 int VompClientRRProc::processGetChannelPids()
951 ULONG channelNumber = ntohl(*(ULONG*)req->data);
953 cChannel* channel = x.channelFromNumber(channelNumber);
958 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
967 #if VDRVERSNUM < 10300
969 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
970 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
972 if (channel->Apid2())
974 else if (channel->Apid1())
981 for (const int *Apid = channel->Apids(); *Apid; Apid++)
985 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
989 for (const int *Spid = channel->Spids(); *Spid; Spid++)
996 // Format of response
1015 resp->addULONG(channel->Vpid());
1016 #if VDRVERSNUM < 10703
1019 resp->addULONG(channel->Vtype());
1021 resp->addULONG(numApids);
1023 #if VDRVERSNUM < 10300
1026 resp->addULONG(channel->Apid1());
1027 resp->addString("");
1031 resp->addULONG(channel->Apid2());
1032 resp->addString("");
1037 for (ULONG i = 0; i < numApids; i++)
1039 resp->addULONG(channel->Apid(i));
1040 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1042 resp->addULONG(numDpids);
1043 for (ULONG i = 0; i < numDpids; i++)
1045 resp->addULONG(channel->Dpid(i));
1046 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1048 resp->addULONG(numSpids);
1049 for (ULONG i = 0; i < numSpids; i++)
1051 resp->addULONG(channel->Spid(i));
1052 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1055 resp->addULONG(channel->Tpid());
1056 // Format of extended response, for compatibility with older client at the end
1068 #if VDRVERSNUM < 10300
1078 for (ULONG i = 0; i < numApids; i++)
1080 #if VDRVERSNUM < 10715
1083 resp->addULONG(channel->Atype(i));
1086 for (ULONG i = 0; i < numDpids; i++)
1088 #if VDRVERSNUM < 10715
1089 resp->addULONG(0x6A /*AC3*/);
1091 resp->addULONG(channel->Dtype(i));
1094 for (ULONG i = 0; i < numSpids; i++)
1096 #if VDRVERSNUM < 10715
1101 resp->addULONG(channel->SubtitlingType(i));
1102 resp->addULONG(channel->CompositionPageId(i));
1103 resp->addULONG(channel->AncillaryPageId(i));
1110 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1112 log->log("RRProc", Log::DEBUG, "Written channels pids");
1117 int VompClientRRProc::processStartStreamingChannel()
1121 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1125 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1126 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1128 cChannel* channel = x.channelFromNumber(channelNumber);
1133 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1137 // get the priority we should use
1139 int priority = x.config.getValueLong("General", "Live priority", &fail);
1142 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1146 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1150 // a bit of sanity..
1151 #if VDRVERSNUM < 10725
1152 if (priority < 0) priority = 0;
1154 if (priority < -99) priority = -99;
1156 if (priority > 99) priority = 99;
1158 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1159 x.lp = MVPReceiver::create(channel, priority);
1165 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1169 if (!x.lp->init(&x.tcp, req->requestID))
1175 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1181 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1185 int VompClientRRProc::processStopStreaming()
1187 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1190 x.lp->detachMVPReceiver();
1194 else if (x.recplayer)
1196 x.writeResumeData();
1199 delete x.recordingManager;
1201 x.recordingManager = NULL;
1206 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1210 int VompClientRRProc::processGetBlock()
1214 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1220 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1224 UCHAR* data = req->data;
1226 ULLONG position = x.ntohll(*(ULLONG*)data);
1227 data += sizeof(ULLONG);
1228 ULONG amount = ntohl(*(ULONG*)data);
1230 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1232 UCHAR sendBuffer[amount];
1233 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1235 if (!amountReceived)
1238 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1242 resp->copyin(sendBuffer, amountReceived);
1243 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1247 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1248 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1252 int VompClientRRProc::processStartStreamingRecording()
1254 // data is a pointer to the fileName string
1256 x.recordingManager = new cRecordings;
1257 x.recordingManager->Load();
1259 cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1261 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1265 x.recplayer = new RecPlayer(recording);
1267 resp->addULLONG(x.recplayer->getLengthBytes());
1268 resp->addULONG(x.recplayer->getLengthFrames());
1270 #if VDRVERSNUM < 10703
1271 resp->addUCHAR(true);//added for TS
1273 resp->addUCHAR(recording->IsPesRecording());//added for TS
1277 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1279 log->log("RRProc", Log::DEBUG, "written totalLength");
1283 delete x.recordingManager;
1284 x.recordingManager = NULL;
1289 int VompClientRRProc::processPositionFromFrameNumber()
1293 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1297 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1301 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1304 resp->addULLONG(retval);
1306 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1308 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1312 int VompClientRRProc::processFrameNumberFromPosition()
1316 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1320 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1324 retval = x.recplayer->frameNumberFromPosition(position);
1327 resp->addULONG(retval);
1329 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1331 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1335 int VompClientRRProc::processGetIFrame()
1337 bool success = false;
1339 ULONG* data = (ULONG*)req->data;
1341 ULONG frameNumber = ntohl(*data);
1343 ULONG direction = ntohl(*data);
1345 ULLONG rfilePosition = 0;
1346 ULONG rframeNumber = 0;
1347 ULONG rframeLength = 0;
1351 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1355 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1358 // returns file position, frame number, length
1362 resp->addULLONG(rfilePosition);
1363 resp->addULONG(rframeNumber);
1364 resp->addULONG(rframeLength);
1372 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1374 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1378 int VompClientRRProc::processGetChannelSchedule()
1380 ULONG* data = (ULONG*)req->data;
1382 ULONG channelNumber = ntohl(*data);
1384 ULONG startTime = ntohl(*data);
1386 ULONG duration = ntohl(*data);
1388 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1390 cChannel* channel = x.channelFromNumber(channelNumber);
1395 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1397 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1401 log->log("RRProc", Log::DEBUG, "Got channel");
1403 #if VDRVERSNUM < 10300
1404 cMutexLock MutexLock;
1405 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1407 cSchedulesLock MutexLock;
1408 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1414 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1416 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1420 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1422 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1427 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1429 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1433 log->log("RRProc", Log::DEBUG, "Got schedule object");
1435 const char* empty = "";
1436 bool atLeastOneEvent = false;
1439 ULONG thisEventTime;
1440 ULONG thisEventDuration;
1441 const char* thisEventTitle;
1442 const char* thisEventSubTitle;
1443 const char* thisEventDescription;
1445 #if VDRVERSNUM < 10300
1447 const cEventInfo *event;
1448 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1450 event = Schedule->GetEventNumber(eventNumber);
1452 thisEventID = event->GetEventID();
1453 thisEventTime = event->GetTime();
1454 thisEventDuration = event->GetDuration();
1455 thisEventTitle = event->GetTitle();
1456 thisEventSubTitle = event->GetSubtitle();
1457 thisEventDescription = event->GetExtendedDescription();
1461 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1463 thisEventID = event->EventID();
1464 thisEventTime = event->StartTime();
1465 thisEventDuration = event->Duration();
1466 thisEventTitle = event->Title();
1467 thisEventSubTitle = NULL;
1468 thisEventDescription = event->Description();
1472 //in the past filter
1473 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1476 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1479 if (thisEventTime >= (startTime + duration)) continue;
1481 if (!thisEventTitle) thisEventTitle = empty;
1482 if (!thisEventSubTitle) thisEventSubTitle = empty;
1483 if (!thisEventDescription) thisEventDescription = empty;
1485 resp->addULONG(thisEventID);
1486 resp->addULONG(thisEventTime);
1487 resp->addULONG(thisEventDuration);
1489 resp->addString(x.charconvsys->Convert(thisEventTitle));
1490 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1491 resp->addString(x.charconvsys->Convert(thisEventDescription));
1493 atLeastOneEvent = true;
1496 log->log("RRProc", Log::DEBUG, "Got all event data");
1498 if (!atLeastOneEvent)
1501 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1505 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1507 log->log("RRProc", Log::DEBUG, "written schedules packet");
1512 int VompClientRRProc::processGetTimers()
1515 int numTimers = Timers.Count();
1517 resp->addULONG(numTimers);
1519 for (int i = 0; i < numTimers; i++)
1521 timer = Timers.Get(i);
1523 #if VDRVERSNUM < 10300
1524 resp->addULONG(timer->Active());
1526 resp->addULONG(timer->HasFlags(tfActive));
1528 resp->addULONG(timer->Recording());
1529 resp->addULONG(timer->Pending());
1530 resp->addULONG(timer->Priority());
1531 resp->addULONG(timer->Lifetime());
1532 resp->addULONG(timer->Channel()->Number());
1533 resp->addULONG(timer->StartTime());
1534 resp->addULONG(timer->StopTime());
1535 resp->addULONG(timer->Day());
1536 resp->addULONG(timer->WeekDays());
1537 resp->addString(timer->File()); //Filename is system specific and not visible by user
1541 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1543 log->log("RRProc", Log::DEBUG, "Written timers list");
1548 int VompClientRRProc::processSetTimer()
1550 char* timerString = new char[strlen((char*)req->data) + 1];
1551 strcpy(timerString, (char*)req->data);
1553 #if VDRVERSNUM < 10300
1555 // If this is VDR 1.2 the date part of the timer string must be reduced
1556 // to just DD rather than YYYY-MM-DD
1558 int s = 0; // source
1559 int d = 0; // destination
1561 while(c != 2) // copy up to date section, including the second ':'
1563 timerString[d] = req->data[s];
1564 if (req->data[s] == ':') c++;
1568 // now it has copied up to the date section
1570 while(c != 2) // waste YYYY-MM-
1572 if (req->data[s] == '-') c++;
1575 // now source is at the DD
1576 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1577 d += req->dataLength - s;
1578 timerString[d] = '\0';
1580 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1583 log->log("RRProc", Log::DEBUG, "%s", timerString);
1585 cTimer *timer = new cTimer;
1586 if (timer->Parse((char*)timerString))
1588 cTimer *t = Timers.GetTimer(timer);
1592 #if VDRVERSNUM < 10300
1595 Timers.SetModified();
1599 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1600 return 1; // FIXME - cTimer* timer is leaked here!
1606 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1613 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1619 int VompClientRRProc::processDeleteTimer()
1621 log->log("RRProc", Log::DEBUG, "Delete timer called");
1626 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1627 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1628 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1629 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1630 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1633 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1635 if ( (ti->Channel()->Number() == delChannel)
1636 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1637 && (ti->StartTime() == delStart)
1638 && (ti->StopTime() == delStop) )
1646 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1650 if (!Timers.BeingEdited())
1652 if (!ti->Recording())
1655 Timers.SetModified();
1658 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1663 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1666 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1672 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1675 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1680 int VompClientRRProc::processGetRecInfo()
1682 // data is a pointer to the fileName string
1684 cRecordings Recordings;
1685 Recordings.Load(); // probably have to do this
1687 cRecording *recording = Recordings.GetByName((char*)req->data);
1689 time_t timerStart = 0;
1690 time_t timerStop = 0;
1691 char* summary = NULL;
1692 char* shorttext = NULL;
1693 char* description = NULL;
1694 bool newsummary=false;
1695 ULONG resumePoint = 0;
1699 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1702 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1707 4 bytes: start time for timer
1708 4 bytes: end time for timer
1709 4 bytes: resume point
1711 4 bytes: num components
1718 8 bytes: frames per second
1721 // Get current timer
1723 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1726 timerStart = rc->Timer()->StartTime();
1727 timerStop = rc->Timer()->StopTime();
1728 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1731 resp->addULONG(timerStart);
1732 resp->addULONG(timerStop);
1736 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1739 resumePoint = strtoul(value, NULL, 10);
1743 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1746 ResumeId = atoi(ResumeIdC);
1750 ResumeId = 0; //default if not defined in vomp-MAC.conf
1752 while (ResumeIDLock)
1753 cCondWait::SleepMs(100);
1754 ResumeIDLock = true;
1755 int OldSetupResumeID = Setup.ResumeID;
1756 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1757 #if VDRVERSNUM < 10703
1758 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1760 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1762 Setup.ResumeID = OldSetupResumeID; //and restore it back
1763 ResumeIDLock = false;
1765 int resume = ResumeFile.Read();
1766 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1768 resumePoint = ResumeFile.Read();
1770 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1772 resp->addULONG(resumePoint);
1776 #if VDRVERSNUM < 10300
1777 summary = (char*)recording->Summary();
1779 const cRecordingInfo *Info = recording->Info();
1780 shorttext = (char*)Info->ShortText();
1781 description = (char*) (char*)Info->Description();
1782 if (isempty(shorttext)) summary=description;
1783 else if (isempty(description)) summary=shorttext;
1785 int length=strlen(description)+strlen(shorttext)+4;
1786 summary=new char[length];
1787 snprintf(summary,length,"%s\n\n%s",shorttext,description);
1791 if (isempty(summary)) summary = (char*)Info->Description();
1793 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1796 resp->addString(x.charconvsys->Convert(summary));
1797 if (newsummary) delete [] summary;
1801 resp->addString("");
1806 #if VDRVERSNUM < 10300
1808 // Send 0 for numchannels - this signals the client this info is not available
1812 const cComponents* components = Info->Components();
1814 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1822 resp->addULONG(components->NumComponents());
1824 tComponent* component;
1825 for (int i = 0; i < components->NumComponents(); i++)
1827 component = components->Component(i);
1829 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1831 resp->addUCHAR(component->stream);
1832 resp->addUCHAR(component->type);
1834 if (component->language)
1836 resp->addString(x.charconvsys->Convert(component->language));
1840 resp->addString("");
1842 if (component->description)
1844 resp->addString(x.charconvsys->Convert(component->description));
1848 resp->addString("");
1854 double framespersec;
1855 #if VDRVERSNUM < 10703
1856 framespersec = FRAMESPERSEC;
1858 framespersec = Info->FramesPerSecond();
1860 resp->adddouble(framespersec);
1865 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1867 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1877 int VompClientRRProc::processReScanRecording()
1881 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1885 x.recplayer->scan();
1887 resp->addULLONG(x.recplayer->getLengthBytes());
1888 resp->addULONG(x.recplayer->getLengthFrames());
1890 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1891 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1895 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1897 int VompClientRRProc::processGetMarks()
1899 // data is a pointer to the fileName string
1902 cRecordings Recordings;
1903 Recordings.Load(); // probably have to do this
1905 cRecording *recording = Recordings.GetByName((char*)req->data);
1907 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1911 #if VDRVERSNUM < 10703
1912 Marks.Load(recording->FileName());
1914 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
1918 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1920 #if VDRVERSNUM < 10721
1921 ULLONG mposition = m->position;
1923 ULLONG mposition = m->Position();
1925 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
1927 resp->addULONG(mposition);
1932 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1938 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1940 log->log("RRProc", Log::DEBUG, "Written Marks list");
1945 int VompClientRRProc::processVDRShutdown()
1947 log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
1948 VompClient::decClients(); // Temporarily make this client disappear
1949 cRemote::Put(kPower);
1950 VompClient::incClients();
1952 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1956 #endif // !VOMPSTANDALONE