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"
38 #include "mediaplayer.h"
39 #include "servermediafile.h"
41 #include "vdrcommand.h"
45 #define VOMP_PROTOCOLL_VERSION 0x00000100
47 // cc is release protocol version, increase with every release, that changes protocoll
48 // dd is development protocol version, set to zero at every release,
49 // increase for every protocoll change in git
50 // bb not equal zero should indicate a non loggytronic protocoll
51 // aa is reserved for future use
53 VompClientRRProc::VompClientRRProc(VompClient& x)
56 log = Log::getInstance();
61 VompClientRRProc::~VompClientRRProc()
66 bool VompClientRRProc::init()
68 int a = threadStart();
73 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
77 Now we have a queue system is used,
78 since on rare occasion the client fire two request at once
79 e.g. heavily channel switching
80 then processing only a single request would cause a deadlock in the client
84 log->log("RRProc", Log::DEBUG, "recvReq");
86 req_queue.push(newRequest);
88 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
94 void VompClientRRProc::threadMethod()
97 log->log("RRProc", Log::DEBUG, "threadMethod startup");
99 if (req_queue.size() != 0)
102 - log->log("RRProc", Log::ERR, "threadMethod err 1");
106 That was how the code used to be.
108 TODO: Work out why this happens.
111 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");
112 while (req_queue.size())
114 //log->log("RRProc", Log::DEBUG, "thread while");
115 req = req_queue.front();
118 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
120 if (!processPacket())
122 log->log("RRProc", Log::ERR, "processPacket exited with fail");
128 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");
134 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
135 threadWaitForSignal(); // unlocks, waits, relocks
136 if (req_queue.size() == 0)
138 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
143 // signalled with something in queue
145 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
147 while (req_queue.size())
149 //log->log("RRProc", Log::DEBUG, "thread while");
150 req = req_queue.front();
153 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
155 if (!processPacket())
157 log->log("RRProc", Log::ERR, "processPacket exited with fail");
164 // locked and run out of packets to process
168 bool VompClientRRProc::processPacket()
170 resp = new ResponsePacket();
171 if (!resp->init(req->requestID))
173 log->log("RRProc", Log::ERR, "response packet init fail");
176 if (req->data) free(req->data);
188 result = processLogin();
190 #ifndef VOMPSTANDALONE
192 result = processGetRecordingsList();
195 result = processDeleteRecording();
198 result = processGetChannelsList();
201 result = processStartStreamingChannel();
204 result = processGetBlock();
207 result = processStopStreaming();
210 result = processStartStreamingRecording();
213 result = processGetChannelSchedule();
217 result = processConfigSave();
220 result = processConfigLoad();
222 #ifndef VOMPSTANDALONE
224 result = processReScanRecording(); // FIXME obselete
227 result = processGetTimers();
230 result = processSetTimer();
233 result = processPositionFromFrameNumber();
236 result = processFrameNumberFromPosition();
239 result = processMoveRecording();
242 result = processGetIFrame();
245 result = processGetRecInfo();
248 result = processGetMarks();
251 result = processGetChannelPids();
254 result = processDeleteTimer();
257 case VDR_GETMEDIALIST:
258 result = processGetMediaList();
261 result = processOpenMedia();
263 case VDR_GETMEDIABLOCK:
264 result = processGetMediaBlock();
267 result = processGetLanguageList();
270 result = processGetLanguageContent();
272 case VDR_GETMEDIAINFO:
273 result = processGetMediaInfo();
275 case VDR_CLOSECHANNEL:
276 result = processCloseMediaChannel();
279 result = processSetCharset();
286 if (req->data) free(req->data);
290 if (result) return true;
294 int VompClientRRProc::processLogin()
296 if (req->dataLength != 6) return 0;
300 char configFileName[PATH_MAX];
301 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]);
302 x.config.init(configFileName);
304 // Send the login reply
306 time_t timeNow = time(NULL);
307 struct tm* timeStruct = localtime(&timeNow);
308 int timeOffset = timeStruct->tm_gmtoff;
310 resp->addULONG(timeNow);
311 resp->addLONG(timeOffset);
312 resp->addULONG(VOMP_PROTOCOLL_VERSION);
314 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
315 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
318 x.netLog(); // safe to run here since the client won't start net logging for a while yet
323 int VompClientRRProc::processSetCharset()
325 int charset = ntohl(*(ULONG*)req->data);
326 if (charset>0 && charset<3)
328 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
329 x.setCharset(charset);
334 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
338 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
342 int VompClientRRProc::processConfigSave()
344 char* section = (char*)req->data;
348 for (UINT k = 0; k < req->dataLength; k++)
350 if (req->data[k] == '\0')
354 key = (char*)&req->data[k+1];
358 value = (char*)&req->data[k+1];
364 // if the last string (value) doesnt have null terminator, give up
365 if (req->data[req->dataLength - 1] != '\0') return 0;
367 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
368 if (x.config.setValueString(section, key, value))
378 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
383 int VompClientRRProc::processConfigLoad()
385 char* section = (char*)req->data;
388 for (UINT k = 0; k < req->dataLength; k++)
390 if (req->data[k] == '\0')
392 key = (char*)&req->data[k+1];
397 char* value = x.config.getValueString(section, key);
401 resp->addString(value);//client coding, do not touch
402 log->log("RRProc", Log::DEBUG, "Written config load packet");
408 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
412 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
418 //helper for sending from a serialize buffer
419 //insert the used len into the first 4 Bytes of the buffer
420 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
421 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
423 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
427 * media List Request:
428 * Media List response:
431 #define MLISTBUF 500000
432 int VompClientRRProc::processGetMediaList()
434 SerializeBuffer buffer(req->data,req->dataLength);
435 MediaURI uri(0,NULL,NULL);
436 VDR_GetMediaListRequest request(&uri);
437 if (request.deserialize(&buffer) != 0) {
438 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
441 const char *dirname=uri.getName();
442 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
445 if (dirname == NULL) {
446 ml=x.media->getRootList();
448 ml=x.media->getMediaList(&uri);
451 log->log("Client", Log::ERR, "getMediaList returned NULL");
454 SerializeBuffer rbuf(MLISTBUF,false,true);
455 ULONG flags=0; //TODO: real error handling by setting flags
456 VDR_GetMediaListResponse response(&flags,ml);
457 if (response.serialize(&rbuf) != 0) {
458 log->log("Client", Log::ERR, "getMediaList returned NULL");
462 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
467 log->log("Client", Log::DEBUG, "Written Media list");
472 * openMedia response:
474 int VompClientRRProc::processOpenMedia()
476 SerializeBuffer buffer(req->data,req->dataLength);
477 MediaURI uri(0,NULL,NULL);
481 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
482 if (request.deserialize(&buffer) != 0) {
483 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
486 const char *name=uri.getName();
487 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
489 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
494 log->log("Client", Log::ERR, "openMediaRequest unable to open");
496 VDR_OpenMediumResponse response(&flags,&size);
497 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
498 if (response.serialize(&rbuf) != 0) {
499 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
502 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
509 * packet - no serialized response!
511 int VompClientRRProc::processGetMediaBlock()
513 SerializeBuffer buffer(req->data,req->dataLength);
517 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
518 if (request.deserialize(&buffer) != 0) {
519 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
522 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
524 UCHAR sendBuffer[amount ];
525 ULONG amountReceived = 0;
526 UCHAR *rbuf=sendBuffer;
527 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
528 if (!amountReceived || rt != 0)
530 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
534 if (rbuf != sendBuffer) {
535 //the provider did not use the optimized handling with using my buffer
536 resp->copyin(rbuf,amountReceived);
539 // the provider did not allocate a new buffer
540 resp->copyin(sendBuffer,amountReceived);
544 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
545 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
552 int VompClientRRProc::processGetMediaInfo()
554 SerializeBuffer buffer(req->data,req->dataLength);
556 VDR_GetMediaInfoRequest request(&channel);
557 if (request.deserialize(&buffer) != 0) {
558 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
561 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
564 int rt=x.media->getMediaInfo(channel,&mi);
567 log->log("Client", Log::ERR, "getMediaInfo unable to get");
569 VDR_GetMediaInfoResponse response(&flags,&mi);
570 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
571 if (response.serialize(&rbuf) != 0) {
572 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
584 int VompClientRRProc::processCloseMediaChannel()
586 SerializeBuffer buffer(req->data,req->dataLength);
588 VDR_CloseMediaChannelRequest request(&channel);
589 if (request.deserialize(&buffer) != 0) {
590 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
594 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
595 int rt=x.media->closeMediaChannel(channel);
598 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
600 VDR_CloseMediaChannelResponse response(&flags);
601 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
602 if (response.serialize(&rbuf) != 0) {
603 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
612 int VompClientRRProc::processGetLanguageList()
614 x.i18n.findLanguages();
615 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
617 I18n::lang_code_list::const_iterator iter;
618 for (iter = languages.begin(); iter != languages.end(); ++iter)
620 resp->addString(iter->first.c_str()); // Source code is acsii
621 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
624 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
628 int VompClientRRProc::processGetLanguageContent()
630 if (req->dataLength <= 0) return 0;
631 std::string code, result;
632 code.assign((char*)req->data, req->dataLength - 1);
633 x.i18n.findLanguages();
634 I18n::trans_table texts = x.i18n.getLanguageContent(code);
635 I18n::trans_table::const_iterator iter;
636 for (iter = texts.begin(); iter != texts.end(); ++iter)
638 resp->addString(iter->first.c_str());// source code is acsii since it is english
639 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
642 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
646 #ifndef VOMPSTANDALONE
648 int VompClientRRProc::processGetRecordingsList()
651 int Percent = VideoDiskSpace(&FreeMB);
652 int Total = (FreeMB / (100 - Percent)) * 100;
654 resp->addULONG(Total);
655 resp->addULONG(FreeMB);
656 resp->addULONG(Percent);
658 cRecordings Recordings;
661 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
663 #if VDRVERSNUM < 10721
664 resp->addULONG(recording->start);
666 resp->addULONG(recording->Start());
668 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
669 resp->addString(recording->FileName());//file name are not visible by user do not touch
673 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
675 log->log("RRProc", Log::DEBUG, "Written recordings list");
680 int VompClientRRProc::processDeleteRecording()
682 // data is a pointer to the fileName string
684 cRecordings Recordings;
685 Recordings.Load(); // probably have to do this
687 cRecording* recording = Recordings.GetByName((char*)req->data);
689 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
693 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
695 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
698 if (recording->Delete())
700 // Copy svdrp's way of doing this, see if it works
701 #if VDRVERSNUM > 10300
702 ::Recordings.DelByName(recording->FileName());
722 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
727 int VompClientRRProc::processMoveRecording()
729 log->log("RRProc", Log::DEBUG, "Process move recording");
730 char* fileName = (char*)req->data;
731 char* newPath = NULL;
733 for (UINT k = 0; k < req->dataLength; k++)
735 if (req->data[k] == '\0')
737 newPath = (char*)&req->data[k+1];
741 if (!newPath) return 0;
743 cRecordings Recordings;
744 Recordings.Load(); // probably have to do this
746 cRecording* recording = Recordings.GetByName((char*)fileName);
748 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
752 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
755 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
756 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
757 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
759 const char* t = recording->FileName();
761 char* dateDirName = NULL; int k;
762 char* titleDirName = NULL; int j;
764 // Find the datedirname
765 for(k = strlen(t) - 1; k >= 0; k--)
769 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
770 dateDirName = new char[strlen(&t[k+1]) + 1];
771 strcpy(dateDirName, &t[k+1]);
776 // Find the titledirname
778 for(j = k-1; j >= 0; j--)
782 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
783 titleDirName = new char[(k - j - 1) + 1];
784 memcpy(titleDirName, &t[j+1], k - j - 1);
785 titleDirName[k - j - 1] = '\0';
790 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
791 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
792 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
794 char* newPathConv = new char[strlen(newPath)+1];
795 strcpy(newPathConv, newPath);
796 ExchangeChars(newPathConv, true);
797 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
799 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
800 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
801 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
802 delete[] newPathConv;
804 log->log("RRProc", Log::DEBUG, "%s", newContainer);
807 int statret = stat(newContainer, &dstat);
808 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
810 log->log("RRProc", Log::DEBUG, "new dir does not exist");
811 int mkdirret = mkdir(newContainer, 0755);
814 delete[] dateDirName;
815 delete[] titleDirName;
816 delete[] newContainer;
820 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
824 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
826 delete[] dateDirName;
827 delete[] titleDirName;
828 delete[] newContainer;
832 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
836 // Ok, the directory container has been made, or it pre-existed.
838 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
839 sprintf(newDir, "%s/%s", newContainer, dateDirName);
841 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
842 int renameret = rename(t, newDir);
845 // Success. Test for remove old dir containter
846 char* oldTitleDir = new char[k+1];
847 memcpy(oldTitleDir, t, k);
848 oldTitleDir[k] = '\0';
849 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
850 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
851 delete[] oldTitleDir;
856 #if VDRVERSNUM > 10311
858 ::Recordings.Update();
860 // Success. Send a different packet from just a ulong
861 resp->addULONG(1); // success
862 resp->addString(newDir); //system depent do not convert
870 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
872 delete[] dateDirName;
873 delete[] titleDirName;
874 delete[] newContainer;
881 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
888 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
894 int VompClientRRProc::processGetChannelsList()
898 char* chanConfig = x.config.getValueString("General", "Channels");
900 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
902 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
904 #if VDRVERSNUM < 10300
905 if (!channel->GroupSep() && (!channel->Ca() || allChans))
907 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
910 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
912 if (channel->Vpid()) type = 1;
913 #if VDRVERSNUM < 10300
916 else if (channel->Apid(0)) type = 2;
920 resp->addULONG(channel->Number());
921 resp->addULONG(type);
922 resp->addString(x.charconvsys->Convert(channel->Name()));
923 #if VDRVERSNUM < 10703
926 resp->addULONG(channel->Vtype());
932 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
934 log->log("RRProc", Log::DEBUG, "Written channels list");
939 int VompClientRRProc::processGetChannelPids()
941 ULONG channelNumber = ntohl(*(ULONG*)req->data);
943 cChannel* channel = x.channelFromNumber(channelNumber);
948 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
957 #if VDRVERSNUM < 10300
959 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
960 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
962 if (channel->Apid2())
964 else if (channel->Apid1())
971 for (const int *Apid = channel->Apids(); *Apid; Apid++)
975 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
979 for (const int *Spid = channel->Spids(); *Spid; Spid++)
986 // Format of response
1005 resp->addULONG(channel->Vpid());
1006 #if VDRVERSNUM < 10703
1009 resp->addULONG(channel->Vtype());
1011 resp->addULONG(numApids);
1013 #if VDRVERSNUM < 10300
1016 resp->addULONG(channel->Apid1());
1017 resp->addString("");
1021 resp->addULONG(channel->Apid2());
1022 resp->addString("");
1027 for (ULONG i = 0; i < numApids; i++)
1029 resp->addULONG(channel->Apid(i));
1030 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1032 resp->addULONG(numDpids);
1033 for (ULONG i = 0; i < numDpids; i++)
1035 resp->addULONG(channel->Dpid(i));
1036 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1038 resp->addULONG(numSpids);
1039 for (ULONG i = 0; i < numSpids; i++)
1041 resp->addULONG(channel->Spid(i));
1042 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1045 resp->addULONG(channel->Tpid());
1046 // Format of extended response, for compatibility with older client at the end
1058 #if VDRVERSNUM < 10300
1068 for (ULONG i = 0; i < numApids; i++)
1070 #if VDRVERSNUM < 10715
1073 resp->addULONG(channel->Atype(i));
1076 for (ULONG i = 0; i < numDpids; i++)
1078 #if VDRVERSNUM < 10715
1079 resp->addULONG(0x6A /*AC3*/);
1081 resp->addULONG(channel->Dtype(i));
1084 for (ULONG i = 0; i < numSpids; i++)
1086 #if VDRVERSNUM < 10715
1091 resp->addULONG(channel->SubtitlingType(i));
1092 resp->addULONG(channel->CompositionPageId(i));
1093 resp->addULONG(channel->AncillaryPageId(i));
1100 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1102 log->log("RRProc", Log::DEBUG, "Written channels pids");
1107 int VompClientRRProc::processStartStreamingChannel()
1111 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1115 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1116 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1118 cChannel* channel = x.channelFromNumber(channelNumber);
1123 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1127 // get the priority we should use
1129 int priority = x.config.getValueLong("General", "Live priority", &fail);
1132 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1136 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1140 // a bit of sanity..
1141 if (priority < 0) priority = 0;
1142 if (priority > 99) priority = 99;
1144 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1145 x.lp = MVPReceiver::create(channel, priority);
1151 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1155 if (!x.lp->init(&x.tcp, req->requestID))
1161 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1167 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1171 int VompClientRRProc::processStopStreaming()
1173 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1176 x.lp->detachMVPReceiver();
1180 else if (x.recplayer)
1182 x.writeResumeData();
1185 delete x.recordingManager;
1187 x.recordingManager = NULL;
1192 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1196 int VompClientRRProc::processGetBlock()
1200 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1206 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1210 UCHAR* data = req->data;
1212 ULLONG position = x.ntohll(*(ULLONG*)data);
1213 data += sizeof(ULLONG);
1214 ULONG amount = ntohl(*(ULONG*)data);
1216 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1218 UCHAR sendBuffer[amount];
1219 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1221 if (!amountReceived)
1224 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1228 resp->copyin(sendBuffer, amountReceived);
1229 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1233 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1234 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1238 int VompClientRRProc::processStartStreamingRecording()
1240 // data is a pointer to the fileName string
1242 x.recordingManager = new cRecordings;
1243 x.recordingManager->Load();
1245 cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1247 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1251 x.recplayer = new RecPlayer(recording);
1253 resp->addULLONG(x.recplayer->getLengthBytes());
1254 resp->addULONG(x.recplayer->getLengthFrames());
1256 #if VDRVERSNUM < 10703
1257 resp->addUCHAR(true);//added for TS
1259 resp->addUCHAR(recording->IsPesRecording());//added for TS
1263 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1265 log->log("RRProc", Log::DEBUG, "written totalLength");
1269 delete x.recordingManager;
1270 x.recordingManager = NULL;
1275 int VompClientRRProc::processPositionFromFrameNumber()
1279 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1283 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1287 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1290 resp->addULLONG(retval);
1292 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1294 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1298 int VompClientRRProc::processFrameNumberFromPosition()
1302 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1306 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1310 retval = x.recplayer->frameNumberFromPosition(position);
1313 resp->addULONG(retval);
1315 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1317 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1321 int VompClientRRProc::processGetIFrame()
1323 bool success = false;
1325 ULONG* data = (ULONG*)req->data;
1327 ULONG frameNumber = ntohl(*data);
1329 ULONG direction = ntohl(*data);
1331 ULLONG rfilePosition = 0;
1332 ULONG rframeNumber = 0;
1333 ULONG rframeLength = 0;
1337 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1341 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1344 // returns file position, frame number, length
1348 resp->addULLONG(rfilePosition);
1349 resp->addULONG(rframeNumber);
1350 resp->addULONG(rframeLength);
1358 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1360 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1364 int VompClientRRProc::processGetChannelSchedule()
1366 ULONG* data = (ULONG*)req->data;
1368 ULONG channelNumber = ntohl(*data);
1370 ULONG startTime = ntohl(*data);
1372 ULONG duration = ntohl(*data);
1374 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1376 cChannel* channel = x.channelFromNumber(channelNumber);
1381 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1383 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1387 log->log("RRProc", Log::DEBUG, "Got channel");
1389 #if VDRVERSNUM < 10300
1390 cMutexLock MutexLock;
1391 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1393 cSchedulesLock MutexLock;
1394 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1400 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1402 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1406 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1408 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1413 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1415 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1419 log->log("RRProc", Log::DEBUG, "Got schedule object");
1421 const char* empty = "";
1422 bool atLeastOneEvent = false;
1425 ULONG thisEventTime;
1426 ULONG thisEventDuration;
1427 const char* thisEventTitle;
1428 const char* thisEventSubTitle;
1429 const char* thisEventDescription;
1431 #if VDRVERSNUM < 10300
1433 const cEventInfo *event;
1434 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1436 event = Schedule->GetEventNumber(eventNumber);
1438 thisEventID = event->GetEventID();
1439 thisEventTime = event->GetTime();
1440 thisEventDuration = event->GetDuration();
1441 thisEventTitle = event->GetTitle();
1442 thisEventSubTitle = event->GetSubtitle();
1443 thisEventDescription = event->GetExtendedDescription();
1447 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1449 thisEventID = event->EventID();
1450 thisEventTime = event->StartTime();
1451 thisEventDuration = event->Duration();
1452 thisEventTitle = event->Title();
1453 thisEventSubTitle = NULL;
1454 thisEventDescription = event->Description();
1458 //in the past filter
1459 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1462 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1465 if (thisEventTime >= (startTime + duration)) continue;
1467 if (!thisEventTitle) thisEventTitle = empty;
1468 if (!thisEventSubTitle) thisEventSubTitle = empty;
1469 if (!thisEventDescription) thisEventDescription = empty;
1471 resp->addULONG(thisEventID);
1472 resp->addULONG(thisEventTime);
1473 resp->addULONG(thisEventDuration);
1475 resp->addString(x.charconvsys->Convert(thisEventTitle));
1476 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1477 resp->addString(x.charconvsys->Convert(thisEventDescription));
1479 atLeastOneEvent = true;
1482 log->log("RRProc", Log::DEBUG, "Got all event data");
1484 if (!atLeastOneEvent)
1487 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1491 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1493 log->log("RRProc", Log::DEBUG, "written schedules packet");
1498 int VompClientRRProc::processGetTimers()
1501 int numTimers = Timers.Count();
1503 resp->addULONG(numTimers);
1505 for (int i = 0; i < numTimers; i++)
1507 timer = Timers.Get(i);
1509 #if VDRVERSNUM < 10300
1510 resp->addULONG(timer->Active());
1512 resp->addULONG(timer->HasFlags(tfActive));
1514 resp->addULONG(timer->Recording());
1515 resp->addULONG(timer->Pending());
1516 resp->addULONG(timer->Priority());
1517 resp->addULONG(timer->Lifetime());
1518 resp->addULONG(timer->Channel()->Number());
1519 resp->addULONG(timer->StartTime());
1520 resp->addULONG(timer->StopTime());
1521 resp->addULONG(timer->Day());
1522 resp->addULONG(timer->WeekDays());
1523 resp->addString(timer->File()); //Filename is system specific and not visible by user
1527 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1529 log->log("RRProc", Log::DEBUG, "Written timers list");
1534 int VompClientRRProc::processSetTimer()
1536 char* timerString = new char[strlen((char*)req->data) + 1];
1537 strcpy(timerString, (char*)req->data);
1539 #if VDRVERSNUM < 10300
1541 // If this is VDR 1.2 the date part of the timer string must be reduced
1542 // to just DD rather than YYYY-MM-DD
1544 int s = 0; // source
1545 int d = 0; // destination
1547 while(c != 2) // copy up to date section, including the second ':'
1549 timerString[d] = req->data[s];
1550 if (req->data[s] == ':') c++;
1554 // now it has copied up to the date section
1556 while(c != 2) // waste YYYY-MM-
1558 if (req->data[s] == '-') c++;
1561 // now source is at the DD
1562 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1563 d += req->dataLength - s;
1564 timerString[d] = '\0';
1566 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1569 log->log("RRProc", Log::DEBUG, "%s", timerString);
1571 cTimer *timer = new cTimer;
1572 if (timer->Parse((char*)timerString))
1574 cTimer *t = Timers.GetTimer(timer);
1578 #if VDRVERSNUM < 10300
1581 Timers.SetModified();
1585 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1586 return 1; // FIXME - cTimer* timer is leaked here!
1592 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1599 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1605 int VompClientRRProc::processDeleteTimer()
1607 log->log("RRProc", Log::DEBUG, "Delete timer called");
1612 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1613 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1614 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1615 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1616 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1619 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1621 if ( (ti->Channel()->Number() == delChannel)
1622 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1623 && (ti->StartTime() == delStart)
1624 && (ti->StopTime() == delStop) )
1632 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1636 if (!Timers.BeingEdited())
1638 if (!ti->Recording())
1641 Timers.SetModified();
1644 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1649 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1652 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1658 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1661 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1666 int VompClientRRProc::processGetRecInfo()
1668 // data is a pointer to the fileName string
1670 cRecordings Recordings;
1671 Recordings.Load(); // probably have to do this
1673 cRecording *recording = Recordings.GetByName((char*)req->data);
1675 time_t timerStart = 0;
1676 time_t timerStop = 0;
1677 char* summary = NULL;
1678 char* shorttext = NULL;
1679 char* description = NULL;
1680 bool newsummary=false;
1681 ULONG resumePoint = 0;
1685 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1688 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1693 4 bytes: start time for timer
1694 4 bytes: end time for timer
1695 4 bytes: resume point
1697 4 bytes: num components
1704 8 bytes: frames per second
1707 // Get current timer
1709 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1712 timerStart = rc->Timer()->StartTime();
1713 timerStop = rc->Timer()->StopTime();
1714 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1717 resp->addULONG(timerStart);
1718 resp->addULONG(timerStop);
1722 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1725 resumePoint = strtoul(value, NULL, 10);
1729 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1732 ResumeId = atoi(ResumeIdC);
1736 ResumeId = 0; //default if not defined in vomp-MAC.conf
1738 while (ResumeIDLock)
1739 cCondWait::SleepMs(100);
1740 ResumeIDLock = true;
1741 int OldSetupResumeID = Setup.ResumeID;
1742 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1743 #if VDRVERSNUM < 10703
1744 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1746 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1748 Setup.ResumeID = OldSetupResumeID; //and restore it back
1749 ResumeIDLock = false;
1751 int resume = ResumeFile.Read();
1752 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1754 resumePoint = ResumeFile.Read();
1756 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1758 resp->addULONG(resumePoint);
1762 #if VDRVERSNUM < 10300
1763 summary = (char*)recording->Summary();
1765 const cRecordingInfo *Info = recording->Info();
1766 shorttext = (char*)Info->ShortText();
1767 description = (char*) (char*)Info->Description();
1768 if (isempty(shorttext)) summary=description;
1769 else if (isempty(description)) summary=shorttext;
1771 int length=strlen(description)+strlen(shorttext)+4;
1772 summary=new char[length];
1773 snprintf(summary,length,"%s\n\n%s",shorttext,description);
1777 if (isempty(summary)) summary = (char*)Info->Description();
1779 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1782 resp->addString(x.charconvsys->Convert(summary));
1783 if (newsummary) delete [] summary;
1787 resp->addString("");
1792 #if VDRVERSNUM < 10300
1794 // Send 0 for numchannels - this signals the client this info is not available
1798 const cComponents* components = Info->Components();
1800 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1808 resp->addULONG(components->NumComponents());
1810 tComponent* component;
1811 for (int i = 0; i < components->NumComponents(); i++)
1813 component = components->Component(i);
1815 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1817 resp->addUCHAR(component->stream);
1818 resp->addUCHAR(component->type);
1820 if (component->language)
1822 resp->addString(x.charconvsys->Convert(component->language));
1826 resp->addString("");
1828 if (component->description)
1830 resp->addString(x.charconvsys->Convert(component->description));
1834 resp->addString("");
1840 double framespersec;
1841 #if VDRVERSNUM < 10703
1842 framespersec = FRAMESPERSEC;
1844 framespersec = Info->FramesPerSecond();
1846 resp->adddouble(framespersec);
1851 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1853 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1863 int VompClientRRProc::processReScanRecording()
1867 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1871 x.recplayer->scan();
1873 resp->addULLONG(x.recplayer->getLengthBytes());
1874 resp->addULONG(x.recplayer->getLengthFrames());
1876 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1877 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1881 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1883 int VompClientRRProc::processGetMarks()
1885 // data is a pointer to the fileName string
1888 cRecordings Recordings;
1889 Recordings.Load(); // probably have to do this
1891 cRecording *recording = Recordings.GetByName((char*)req->data);
1893 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1897 #if VDRVERSNUM < 10703
1898 Marks.Load(recording->FileName());
1900 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
1904 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1906 #if VDRVERSNUM < 10721
1907 ULLONG mposition = m->position;
1909 ULLONG mposition = m->Position();
1911 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
1913 resp->addULONG(mposition);
1918 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1924 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1926 log->log("RRProc", Log::DEBUG, "Written Marks list");
1931 #endif // !VOMPSTANDALONE