Index: DirectShowFilters/TsWriter/source/TsChannel.cpp =================================================================== --- DirectShowFilters/TsWriter/source/TsChannel.cpp (revision 27796) +++ DirectShowFilters/TsWriter/source/TsChannel.cpp (working copy) @@ -33,13 +33,16 @@ CTsChannel::CTsChannel(LPUNKNOWN pUnk, HRESULT *phr,int id) { m_id=id; + + b_grabCustomPackets=false; m_pVideoAnalyzer = new CVideoAnalyzer(pUnk,phr); m_pPmtGrabber = new CPmtGrabber(pUnk,phr); m_pRecorder = new CDiskRecorder(RecordingMode::Recording); m_pTimeShifting= new CDiskRecorder(RecordingMode::TimeShift); m_pTeletextGrabber= new CTeletextGrabber(pUnk,phr); - m_pCaGrabber= new CCaGrabber(pUnk,phr); -} + m_pCaGrabber= new CCaGrabber(pUnk,phr); + m_CustomPacketWriter= new FileWriter; +} CTsChannel::~CTsChannel(void) { @@ -79,6 +82,12 @@ delete m_pCaGrabber; m_pCaGrabber=NULL; } + if (m_CustomPacketWriter!=NULL) + { + LogDebug("del m_CustomPacketWriter"); + delete m_CustomPacketWriter; + m_CustomPacketWriter=NULL; + } LogDebug("del done..."); } @@ -91,10 +100,27 @@ m_pRecorder->OnTsPacket(tsPacket); m_pTimeShifting->OnTsPacket(tsPacket); m_pTeletextGrabber->OnTsPacket(tsPacket); - m_pCaGrabber->OnTsPacket(tsPacket); - } - catch(...) - { - LogDebug("exception in AnalyzeTsPacket"); - } -} \ No newline at end of file + m_pCaGrabber->OnTsPacket(tsPacket); + + if(b_grabCustomPackets) + { + if (m_CustomPacketWriter!=NULL) + { + int pid=((tsPacket[1] & 0x1F) <<8)+tsPacket[2]; + for (int i=0; i < (int)m_vecCustomPids.size();++i) + { + if(m_vecCustomPids[i]==pid) + { + m_CustomPacketWriter->Write(tsPacket,188); + } + } + } + } + } + catch(...) + { + LogDebug("exception in AnalyzeTsPacket"); + } + +} + Index: DirectShowFilters/TsWriter/source/TsChannel.h =================================================================== --- DirectShowFilters/TsWriter/source/TsChannel.h (revision 27796) +++ DirectShowFilters/TsWriter/source/TsChannel.h (working copy) @@ -27,6 +27,7 @@ #include "DiskRecorder.h" #include "teletextgrabber.h" #include "cagrabber.h" +#include // {C564CEB9-FC77-4776-8CB8-96DD87624161} @@ -35,7 +36,7 @@ public: CTsChannel(LPUNKNOWN pUnk, HRESULT *phr, int id); virtual ~CTsChannel(void); - void OnTsPacket(byte* tsPacket); + void OnTsPacket(byte* tsPacket); int Handle() { return m_id;} CVideoAnalyzer* m_pVideoAnalyzer; @@ -43,6 +44,10 @@ CDiskRecorder* m_pRecorder; CDiskRecorder* m_pTimeShifting; CTeletextGrabber* m_pTeletextGrabber; - CCaGrabber* m_pCaGrabber; + CCaGrabber* m_pCaGrabber; + FileWriter* m_CustomPacketWriter; + bool b_grabCustomPackets; + vector m_vecCustomPids; + typedef vector::iterator* ivecCustomPids; int m_id; }; Index: DirectShowFilters/TsWriter/source/TsWriter.cpp =================================================================== --- DirectShowFilters/TsWriter/source/TsWriter.cpp (revision 27796) +++ DirectShowFilters/TsWriter/source/TsWriter.cpp (working copy) @@ -322,6 +322,7 @@ void CMpTsFilterPin::OnTsPacket(byte* tsPacket) { + m_pWriterFilter->AnalyzeTsPacket(tsPacket); } @@ -394,7 +395,7 @@ delete m_pChannelScanner; delete m_pEpgScanner; delete m_pChannelLinkageScanner; - delete m_rawPaketWriter; + delete m_rawPaketWriter; CAutoLock lock(&m_Lock); for (int i=0; i < (int)m_vecChannels.size();++i) { @@ -503,13 +504,14 @@ try { CAutoLock lock(&m_Lock); - for (int i=0; i < (int)m_vecChannels.size();++i) + for (int i=0; i < (int)m_vecChannels.size();++i) { m_vecChannels[i]->OnTsPacket(tsPacket); } m_pChannelScanner->OnTsPacket(tsPacket); - m_pEpgScanner->OnTsPacket(tsPacket); + m_pEpgScanner->OnTsPacket(tsPacket); m_pChannelLinkageScanner->OnTsPacket(tsPacket); + // Check if custom PID Grabber is enabled } catch(...) { @@ -695,6 +697,9 @@ if (pChannel==NULL) return S_OK; b_dumpRawPakets=false; + + + if (DumpRawTS()) { b_dumpRawPakets=true; @@ -705,18 +710,29 @@ LogDebug(L"Setting name for raw packet dump file to %s", fileName.c_str()); m_rawPaketWriter->SetFileName(fileName.c_str()); } + pChannel->m_pTimeShifting->SetFileNameW(pwszFileName); return S_OK; } + STDMETHODIMP CMpTs::TimeShiftStart( int handle ) { CTsChannel* pChannel=GetTsChannel(handle); if (pChannel==NULL) return S_OK; + + //Added to open file for custom PID grabber + if(pChannel->b_grabCustomPackets) + { + pChannel->m_CustomPacketWriter->OpenFile(); + LogDebug("Custom Data grabber file created"); + } + if (b_dumpRawPakets) { - m_rawPaketWriter->OpenFile(); + m_rawPaketWriter->OpenFile(); LogDebug("Raw packet dump file created. Now dumping raw packets to dump file"); } + if (pChannel->m_pTimeShifting->Start()) return S_OK; else @@ -727,6 +743,13 @@ { CTsChannel* pChannel=GetTsChannel(handle); if (pChannel==NULL) return S_OK; + if(pChannel->b_grabCustomPackets) + { + pChannel->m_CustomPacketWriter->CloseFile(); + pChannel->m_vecCustomPids.clear(); + LogDebug("Custom file closed"); + } + if (b_dumpRawPakets) { m_rawPaketWriter->CloseFile(); @@ -739,9 +762,15 @@ STDMETHODIMP CMpTs:: TimeShiftReset( int handle ) { CTsChannel* pChannel=GetTsChannel(handle); - if (pChannel==NULL) return S_OK; + if (pChannel==NULL) return S_OK; + if(pChannel->b_grabCustomPackets) + { + pChannel->m_CustomPacketWriter->CloseFile(); + pChannel->m_CustomPacketWriter->OpenFile(); + } if (b_dumpRawPakets) { + m_rawPaketWriter->CloseFile(); m_rawPaketWriter->OpenFile(); LogDebug("Raw packet dump file reset"); @@ -858,8 +887,7 @@ return pChannel->m_pCaGrabber->Reset( ); } -STDMETHODIMP CMpTs::GetStreamQualityCounters(int handle, int* totalTsBytes, int* totalRecordingBytes, - int* TsDiscontinuity, int* recordingDiscontinuity) +STDMETHODIMP CMpTs::GetStreamQualityCounters(int handle, int* totalTsBytes, int* totalRecordingBytes, int* TsDiscontinuity, int* recordingDiscontinuity) { CTsChannel* pChannel=GetTsChannel(handle); if (pChannel==NULL) return S_FALSE; @@ -882,7 +910,6 @@ return S_FALSE; } - STDMETHODIMP CMpTs::TimeShiftSetChannelType(int handle, int channelType) { CTsChannel* pChannel=GetTsChannel(handle); @@ -890,4 +917,22 @@ pChannel->m_pRecorder->SetChannelType(channelType); pChannel->m_pTimeShifting->SetChannelType(channelType); return S_OK; +} + +STDMETHODIMP CMpTs::AddPidtoCustomData(int handle, int pid) +{ + CTsChannel* pChannel=GetTsChannel(handle); + if (pChannel==NULL) return S_OK; + LogDebug("Added CustomPid %x",pid); + pChannel->m_vecCustomPids.push_back(pid); + return S_OK; +} + +STDMETHODIMP CMpTs::SetCustomDataFilename(int handle, wchar_t* pwszFileName) +{ + CTsChannel* pChannel=GetTsChannel(handle); + if (pChannel==NULL) return S_OK; + pChannel->m_CustomPacketWriter->SetFileName(pwszFileName); + pChannel->b_grabCustomPackets = true; + return S_OK; } \ No newline at end of file Index: DirectShowFilters/TsWriter/source/TsWriter.h =================================================================== --- DirectShowFilters/TsWriter/source/TsWriter.h (revision 27796) +++ DirectShowFilters/TsWriter/source/TsWriter.h (working copy) @@ -29,6 +29,7 @@ #include "channellinkagescanner.h" #include "tschannel.h" #include "videoaudioobserver.h" +#include "MultiFileWriter.h" #include #include using namespace std; @@ -45,9 +46,8 @@ DECLARE_INTERFACE_(ITSFilter, IUnknown) { STDMETHOD(AddChannel)(THIS_ int* handle)PURE; - STDMETHOD(DeleteChannel)(THIS_ int handle)PURE; - STDMETHOD(DeleteAllChannels)()PURE; - + STDMETHOD(DeleteChannel)(THIS_ int handle)PURE; + STDMETHOD(DeleteAllChannels)()PURE; STDMETHOD(AnalyzerSetVideoPid)(THIS_ int handle, int videoPid)PURE; STDMETHOD(AnalyzerGetVideoPid)(THIS_ int handle, int* videoPid)PURE; STDMETHOD(AnalyzerSetAudioPid)(THIS_ int handle, int audioPid)PURE; @@ -55,19 +55,14 @@ STDMETHOD(AnalyzerIsVideoEncrypted)(THIS_ int handle, int* yesNo)PURE; STDMETHOD(AnalyzerIsAudioEncrypted)(THIS_ int handle, int* yesNo)PURE; STDMETHOD(AnalyzerReset)(THIS_ int handle )PURE; - - - STDMETHOD(PmtSetPmtPid)(THIS_ int handle, int pmtPid, long serviceId); - STDMETHOD(PmtSetCallBack)(THIS_ int handle, IPMTCallback* callback); - STDMETHOD(PmtGetPMTData) (THIS_ int handle, BYTE *pmtData); - - - STDMETHOD(RecordSetRecordingFileNameW)(THIS_ int handle,wchar_t* pwszFileName)PURE; - STDMETHOD(RecordStartRecord)(THIS_ int handle)PURE; - STDMETHOD(RecordStopRecord)(THIS_ int handle)PURE; + STDMETHOD(PmtSetPmtPid)(THIS_ int handle, int pmtPid, long serviceId)PURE; + STDMETHOD(PmtSetCallBack)(THIS_ int handle, IPMTCallback* callback)PURE; + STDMETHOD(PmtGetPMTData) (THIS_ int handle, BYTE *pmtData)PURE; + STDMETHOD(RecordSetRecordingFileNameW)(THIS_ int handle,wchar_t* pwszFileName)PURE; + STDMETHOD(RecordStartRecord)(THIS_ int handle)PURE; + STDMETHOD(RecordStopRecord)(THIS_ int handle)PURE; STDMETHOD(RecordSetPmtPid)(THIS_ int handle,int mtPid,int serviceId,byte* pmtData,int pmtLength)PURE; STDMETHOD(RecordSetVideoAudioObserver)(THIS_ int handle, IVideoAudioObserver* callback)PURE; - STDMETHOD(TimeShiftSetTimeShiftingFileNameW)(THIS_ int handle, wchar_t* pwszFileName)PURE; STDMETHOD(TimeShiftStart)(THIS_ int handle )PURE; STDMETHOD(TimeShiftStop)(THIS_ int handle )PURE; @@ -78,20 +73,17 @@ STDMETHOD(TimeShiftSetParams) (THIS_ int handle, int minFiles, int maxFiles, ULONG chunkSize) PURE; STDMETHOD(TimeShiftGetCurrentFilePosition) (THIS_ int handle,__int64 * position, long * bufferId) PURE; STDMETHOD(SetVideoAudioObserver)(THIS_ int handle, IVideoAudioObserver* callback)PURE; - STDMETHOD(TTxStart)(THIS_ int handle)PURE; STDMETHOD(TTxStop)(THIS_ int handle )PURE; STDMETHOD(TTxSetTeletextPid)(THIS_ int handle,int teletextPid)PURE; STDMETHOD(TTxSetCallBack)(THIS_ int handle,ITeletextCallBack* callback)PURE; - - STDMETHOD(CaSetCallBack)(THIS_ int handle,ICACallback* callback)PURE; + STDMETHOD(CaSetCallBack)(THIS_ int handle,ICACallback* callback)PURE; STDMETHOD(CaGetCaData) (THIS_ int handle,BYTE *caData)PURE; STDMETHOD(CaReset)(THIS_ int handle)PURE; - - STDMETHOD(GetStreamQualityCounters)(THIS_ int handle, int* totalTsBytes, int* totalRecordingBytes, - int* TsDiscontinuity, int* recordingDiscontinuity)PURE; - + STDMETHOD(GetStreamQualityCounters)(THIS_ int handle, int* totalTsBytes, int* totalRecordingBytes, int* TsDiscontinuity, int* recordingDiscontinuity)PURE; STDMETHOD(TimeShiftSetChannelType)(THIS_ int handle, int channelType)PURE; + STDMETHOD(AddPidtoCustomData)(THIS_ int handle, int pid)PURE; + STDMETHOD(SetCustomDataFilename)(THIS_ int handle, wchar_t* pwszFileName)PURE; }; // Main filter object @@ -149,6 +141,8 @@ private: CCritSec m_section; FileWriter *m_rawPaketWriter; + + }; @@ -165,8 +159,10 @@ CCritSec m_ReceiveLock; // Sublock for received samples public: DECLARE_IUNKNOWN + STDMETHODIMP AddPidtoCustomData(int handle, int pid); + STDMETHODIMP SetCustomDataFilename(int handle, wchar_t* pwszFileName); - STDMETHODIMP AddChannel( int* handle); + STDMETHODIMP AddChannel( int* handle); STDMETHODIMP DeleteChannel( int handle); STDMETHODIMP DeleteAllChannels(); @@ -199,7 +195,6 @@ STDMETHODIMP TimeShiftSetParams(int handle, int minFiles, int maxFiles, ULONG chunkSize) ; STDMETHODIMP TimeShiftGetCurrentFilePosition(int handle,__int64 * position,long * bufferId); STDMETHODIMP SetVideoAudioObserver(int handle, IVideoAudioObserver* callback); - STDMETHODIMP TTxStart( int handle); STDMETHODIMP TTxStop( int handle ); STDMETHODIMP TTxSetTeletextPid( int handle,int teletextPid); @@ -221,13 +216,14 @@ private: // Overriden to say what interfaces we support where CTsChannel* GetTsChannel(int handle); - STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv); + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv); CChannelScan* m_pChannelScanner; - CEpgScanner* m_pEpgScanner; - FileWriter* m_rawPaketWriter; + CEpgScanner* m_pEpgScanner; + FileWriter* m_rawPaketWriter; bool b_dumpRawPakets; CChannelLinkageScanner* m_pChannelLinkageScanner; vector m_vecChannels; - typedef vector::iterator ivecChannels; + typedef vector::iterator ivecChannels; + int m_id; }; Index: TvEngine3/TVLibrary/TvControl/Controller.cs =================================================================== --- TvEngine3/TVLibrary/TvControl/Controller.cs (revision 27796) +++ TvEngine3/TVLibrary/TvControl/Controller.cs (working copy) @@ -726,6 +726,16 @@ TvResult StartTimeShifting(ref IUser user, ref string fileName); /// + /// Start timeshifting. + /// + /// The user. + /// Name of the timeshiftfile. + /// + /// TvResult indicating whether method succeeded + /// + TvResult StartTimeShiftingWithCustom(ref IUser user, ref string fileName, ref string CustomFileName, ref List Pids); + + /// /// Stops the card. /// /// The user. @@ -783,12 +793,53 @@ /// TvResult StartTimeShifting(ref IUser user, int idChannel, out VirtualCard card); + /// + /// Start timeshifting on a specific channel + /// + /// user credentials. + /// The id channel. + /// returns on which card timeshifting is started + /// Indicated, if the card should be forced + /// CustomFilename + /// List of Pids + /// + /// TvResult indicating whether method succeeded + /// + TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, bool forceCardId,string CustomFileName, List Pids); /// + /// Start timeshifting on a specific channel + /// + /// user credentials. + /// The id channel. + /// returns on which card timeshifting is started + /// indicates if card was changed + /// CustomFilename + /// List of Pids + /// + /// TvResult indicating whether method succeeded + /// + TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, out bool cardChanged, string CustomFileName, List Pids); + + /// + /// Start timeshifting on a specific channel + /// + /// user credentials. + /// The id channel. + /// returns on which card timeshifting is started + /// CustomFilename + /// List of Pids + /// + /// TvResult indicating whether method succeeded + /// + TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, string CustomFileName, List Pids); + + /// /// Stops the time shifting. /// /// user credentials. /// reason why timeshifting is stopped. + /// /// true if success otherwise false bool StopTimeShifting(ref IUser user, TvStoppedReason reason); Index: TvEngine3/TVLibrary/TvControl/TvServer.cs =================================================================== --- TvEngine3/TVLibrary/TvControl/TvServer.cs (revision 27796) +++ TvEngine3/TVLibrary/TvControl/TvServer.cs (working copy) @@ -348,8 +348,85 @@ return TvResult.UnknownError; } +//Custom TimeShifter /// + /// Start timeshifting on a specific channel + /// + /// The user. + /// id of the channel + /// returns on which card timeshifting is started + /// + /// TvResult indicating whether method succeeded + /// + public TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, string CustomFileName, List Pids) + { + card = null; + try + { + TvResult result = RemoteControl.Instance.StartTimeShiftingWithCustom(ref user, idChannel, out card, CustomFileName, Pids); + return result; + } + catch (Exception) + { + HandleFailure(); + } + return TvResult.UnknownError; + } + + /// + /// Start timeshifting on a specific channel + /// + /// The user. + /// id of the channel + /// returns on which card timeshifting is started + /// indicates if card was changed + /// + /// TvResult indicating whether method succeeded + /// + public TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, out bool cardChanged, string CustomFileName, List Pids) + { + card = null; + cardChanged = false; + try + { + TvResult result = RemoteControl.Instance.StartTimeShiftingWithCustom(ref user, idChannel, out card, out cardChanged, CustomFileName, Pids); + return result; + } + catch (Exception) + { + HandleFailure(); + } + return TvResult.UnknownError; + } + + /// + /// Start timeshifting on a specific channel + /// + /// The user. + /// id of the channel + /// returns on which card timeshifting is started + /// Indicated, if the card should be forced + /// + /// TvResult indicating whether method succeeded + /// + public TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, bool forceCardId, string CustomFileName, List Pids) + { + card = null; + try + { + TvResult result = RemoteControl.Instance.StartTimeShiftingWithCustom(ref user, idChannel, out card, forceCardId, CustomFileName, Pids); + return result; + } + catch (Exception) + { + HandleFailure(); + } + return TvResult.UnknownError; + } + +// Custom FileShifter + /// /// Checks if the schedule specified is currently being recorded and ifso /// returns on which card /// Index: TvEngine3/TVLibrary/TvLibrary.Interfaces/Interfaces/ITvSubChannel.cs =================================================================== --- TvEngine3/TVLibrary/TvLibrary.Interfaces/Interfaces/ITvSubChannel.cs (revision 27796) +++ TvEngine3/TVLibrary/TvLibrary.Interfaces/Interfaces/ITvSubChannel.cs (working copy) @@ -117,6 +117,14 @@ /// filename used for the timeshiftbuffer /// true if succeeded else false bool StartTimeShifting(string fileName); + /// + /// Starts timeshifting. Note card has to be tuned first + /// + /// filename used for the timeshiftbuffer + /// Custom Filename + /// List of Pids + /// true if succeeded else false + bool StartTimeShiftingWithCustom(string fileName, string CustomFileName, List Pids); /// /// Stops timeshifting Index: TvEngine3/TVLibrary/TvLibrary.sln =================================================================== --- TvEngine3/TVLibrary/TvLibrary.sln (revision 27796) +++ TvEngine3/TVLibrary/TvLibrary.sln (working copy) @@ -5,8 +5,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{E5B37E07-6E84-4F14-859D-69A40E871034}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestApps", "TestApps", "{CB80D13D-DB70-4292-A2C1-C9D0360D214C}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Database", "Database", "{2D534797-AC32-428E-9E36-A6DDFE3E4229}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Setup GUI", "Setup GUI", "{1481FA11-87E9-4850-AEAD-5E47BDF718BF}" @@ -33,8 +31,6 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TVLibrary", "TVLibrary\TVLibrary.csproj", "{59C2EBC8-0FEA-4B2E-B5D1-476347332331}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp\TestApp.csproj", "{E9D3F443-A654-4D94-BBAA-080362D25D7C}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TvService", "TvService\TvService.csproj", "{3BE4F3AA-36EB-4956-B000-E29C74FC6AB0}" ProjectSection(ProjectDependencies) = postProject {AD20D519-D1AF-484E-ADDE-251F7CA912D9} = {AD20D519-D1AF-484E-ADDE-251F7CA912D9} @@ -116,12 +112,6 @@ {59C2EBC8-0FEA-4B2E-B5D1-476347332331}.Release|Any CPU.Build.0 = Release|Any CPU {59C2EBC8-0FEA-4B2E-B5D1-476347332331}.Release|x86.ActiveCfg = Release|x86 {59C2EBC8-0FEA-4B2E-B5D1-476347332331}.Release|x86.Build.0 = Release|x86 - {E9D3F443-A654-4D94-BBAA-080362D25D7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E9D3F443-A654-4D94-BBAA-080362D25D7C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E9D3F443-A654-4D94-BBAA-080362D25D7C}.Debug|x86.ActiveCfg = Debug|x86 - {E9D3F443-A654-4D94-BBAA-080362D25D7C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E9D3F443-A654-4D94-BBAA-080362D25D7C}.Release|Any CPU.Build.0 = Release|Any CPU - {E9D3F443-A654-4D94-BBAA-080362D25D7C}.Release|x86.ActiveCfg = Release|x86 {3BE4F3AA-36EB-4956-B000-E29C74FC6AB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3BE4F3AA-36EB-4956-B000-E29C74FC6AB0}.Debug|Any CPU.Build.0 = Debug|Any CPU {3BE4F3AA-36EB-4956-B000-E29C74FC6AB0}.Debug|x86.ActiveCfg = Debug|x86 @@ -309,7 +299,6 @@ {AE752FB4-7450-4150-9BAD-19EFA9D2AC37} = {E5B37E07-6E84-4F14-859D-69A40E871034} {954F9929-7FDA-4F59-88E3-FD2B9A7B6E6E} = {E5B37E07-6E84-4F14-859D-69A40E871034} {CE009BFA-5EF6-4153-B2A1-2FA79698A9AE} = {E5B37E07-6E84-4F14-859D-69A40E871034} - {E9D3F443-A654-4D94-BBAA-080362D25D7C} = {CB80D13D-DB70-4292-A2C1-C9D0360D214C} {0FD8DEAE-37A4-49CC-A7BE-E86E0E0D85BD} = {2D534797-AC32-428E-9E36-A6DDFE3E4229} {58C35C9D-F18D-40F2-8024-24E50D01E9C5} = {2D534797-AC32-428E-9E36-A6DDFE3E4229} {FF9AAD68-BBE0-41F7-9183-F2B8474B9224} = {1481FA11-87E9-4850-AEAD-5E47BDF718BF} Index: TvEngine3/TVLibrary/TVLibrary/Implementations/Analog/Graphs/Analog/AnalogSubChannel.cs =================================================================== --- TvEngine3/TVLibrary/TVLibrary/Implementations/Analog/Graphs/Analog/AnalogSubChannel.cs (revision 27796) +++ TvEngine3/TVLibrary/TVLibrary/Implementations/Analog/Graphs/Analog/AnalogSubChannel.cs (working copy) @@ -144,7 +144,10 @@ #endregion #region Timeshifting - Recording methods - + protected override bool OnStartTimeShiftingWithCustom(string fileName, string CustomFileName, List Pids) + { + throw new NotImplementedException(); + } /// /// sets the filename used for timeshifting /// Index: TvEngine3/TVLibrary/TVLibrary/Implementations/Analog/Graphs/HDPVR/HDPVRChannel.cs =================================================================== --- TvEngine3/TVLibrary/TVLibrary/Implementations/Analog/Graphs/HDPVR/HDPVRChannel.cs (revision 27796) +++ TvEngine3/TVLibrary/TVLibrary/Implementations/Analog/Graphs/HDPVR/HDPVRChannel.cs (working copy) @@ -189,6 +189,11 @@ #region Timeshifting - Recording methods + protected override bool OnStartTimeShiftingWithCustom(string fileName, string CustomFileName, List Pids) + { + throw new NotImplementedException(); + } + /// /// sets the filename used for timeshifting /// Index: TvEngine3/TVLibrary/TVLibrary/Implementations/BaseSubChannel.cs =================================================================== --- TvEngine3/TVLibrary/TVLibrary/Implementations/BaseSubChannel.cs (revision 27796) +++ TvEngine3/TVLibrary/TVLibrary/Implementations/BaseSubChannel.cs (working copy) @@ -323,8 +323,26 @@ } return false; } - /// + /// Starts timeshifting. Note card has to be tuned first + /// + /// filename used for the timeshiftbuffer + /// + public bool StartTimeShiftingWithCustom(string fileName,string CustomFileName, List Pids) + { + try + { + return OnStartTimeShiftingWithCustom(fileName,CustomFileName,Pids); + } + catch (Exception e) + { + //cleanup + Log.Log.WriteFile("StartTimeShifting with Custom failed, cleaning up {0}", e.Message); + StopTimeShifting(); + } + return false; + } + /// /// Stops timeshifting /// /// @@ -589,6 +607,11 @@ protected abstract bool OnStartTimeShifting(string fileName); /// + /// A derrived class should start here the timeshifting on the tv card. It will be called from StartTimeshifting() + /// + protected abstract bool OnStartTimeShiftingWithCustom(string fileName,string CustomFileName, List Pids); + + /// /// A derrived class should stop here the timeshifting on the tv card. It will be called from StopTimeshifting() /// protected abstract void OnStopTimeShifting(); Index: TvEngine3/TVLibrary/TVLibrary/Implementations/DVB/Graphs/TvDvbChannel.cs =================================================================== --- TvEngine3/TVLibrary/TVLibrary/Implementations/DVB/Graphs/TvDvbChannel.cs (revision 27796) +++ TvEngine3/TVLibrary/TVLibrary/Implementations/DVB/Graphs/TvDvbChannel.cs (working copy) @@ -597,7 +597,56 @@ } return (_channelInfo.pids.Count != 0); } + /// + /// sets the filename used for timeshifting + /// + /// timeshifting filename + protected override bool OnStartTimeShiftingWithCustom(string fileName, string CustomFileName, List Pids) + { + //if (_channelInfo.pids.Count == 0) + if (!PMTreceived) + { + Log.Log.WriteFile("subch:{0} SetTimeShiftFileName no pmt received. Timeshifting failed", _subChannelId); + return false; + } + _timeshiftFileName = fileName; + Log.Log.WriteFile("subch:{0} SetTimeShiftFileName:{1}", _subChannelId, fileName); + //int hr; + if (_tsFilterInterface != null) + { + Log.Log.WriteFile("Set video / audio observer"); + _tsFilterInterface.SetVideoAudioObserver(_subChannelIndex, this); + _tsFilterInterface.TimeShiftSetParams(_subChannelIndex, _parameters.MinimumFiles, _parameters.MaximumFiles, + _parameters.MaximumFileSize); + _tsFilterInterface.TimeShiftSetTimeShiftingFileNameW(_subChannelIndex, fileName); + + + if (CurrentChannel == null) + { + Log.Log.Error("CurrentChannel is null when trying to start timeshifting"); + return false; + } + + // Set the channel type (0=tv, 1=radio) + _tsFilterInterface.TimeShiftSetChannelType(_subChannelId, (CurrentChannel.IsTv ? 0 : 1)); + + Log.Log.WriteFile("subch:{0} SetTimeShiftFileName fill in pids", _subChannelId); + _startTimeShifting = false; + SetTimeShiftPids(); + _tsFilterInterface.SetCustomDataFilename(_subChannelIndex, CustomFileName); + foreach (int i in Pids) + { + _tsFilterInterface.AddPidtoCustomData(_subChannelIndex, i); + } + Log.Log.WriteFile("subch:{0}-{1} tswriter StartTimeshifting...", _subChannelId, _subChannelIndex); + _tsFilterInterface.TimeShiftStart(_subChannelIndex); + + _graphState = GraphState.TimeShifting; + } + return (_channelInfo.pids.Count != 0); + } + /// /// Stop recording /// Index: TvEngine3/TVLibrary/TVLibrary/Interfaces/Analyzer/ITsFilter.cs =================================================================== --- TvEngine3/TVLibrary/TVLibrary/Interfaces/Analyzer/ITsFilter.cs (revision 27796) +++ TvEngine3/TVLibrary/TVLibrary/Interfaces/Analyzer/ITsFilter.cs (working copy) @@ -365,5 +365,24 @@ /// [PreserveSig] int TimeShiftSetChannelType(int handle, int channelType); + + /// + /// Adds a Pid to the Custom Data Watcher + /// + /// Handle of the sub channel + /// Pid + /// + [PreserveSig] + int AddPidtoCustomData(int handle, int pid); + + /// + /// Sets the Custom Pid Watcher filename + /// + /// Handle of the sub channel + /// Filename + /// + [PreserveSig] + int SetCustomDataFilename(int handle, [In, MarshalAs(UnmanagedType.LPWStr)] string fileName); + } } \ No newline at end of file Index: TvEngine3/TVLibrary/TvService/CardManagement/CardHandler/TimeShifter.cs =================================================================== --- TvEngine3/TVLibrary/TvService/CardManagement/CardHandler/TimeShifter.cs (revision 27796) +++ TvEngine3/TVLibrary/TvService/CardManagement/CardHandler/TimeShifter.cs (working copy) @@ -19,6 +19,7 @@ #endregion using System; +using System.Collections.Generic; using System.Threading; using TvLibrary.Implementations; using TvLibrary.Interfaces; @@ -478,7 +479,166 @@ Stop(ref user); return TvResult.UnknownError; } + /// + /// Start timeshifting. + /// + /// User + /// Name of the timeshiftfile. + /// TvResult indicating whether method succeeded + public TvResult StartWithCustom(ref IUser user, ref string fileName, ref string CustomFileName, ref List Pids) + { + try + { + // Is the card enabled ? + if (_cardHandler.DataBaseCard.Enabled == false) + { + return TvResult.CardIsDisabled; + } + lock (this) + { + try + { + RemoteControl.HostName = _cardHandler.DataBaseCard.ReferencedServer().HostName; + if (!RemoteControl.Instance.CardPresent(_cardHandler.DataBaseCard.IdCard)) + return TvResult.CardIsDisabled; + + + // Let's verify if hard disk drive has enough free space before we start time shifting. The function automatically handles both local and UNC paths + if (!IsTimeShifting(ref user)) + { + ulong FreeDiskSpace = Utils.GetFreeDiskSpace(fileName); + + TvBusinessLayer layer = new TvBusinessLayer(); + UInt32 MaximumFileSize = UInt32.Parse(layer.GetSetting("timeshiftMaxFileSize", "256").Value); // in MB + ulong DiskSpaceNeeded = Convert.ToUInt64(MaximumFileSize); + DiskSpaceNeeded *= 1000000 * 2; // Convert to bytes; 2 times of timeshiftMaxFileSize + if (FreeDiskSpace < DiskSpaceNeeded) + // TimeShifter need at least this free disk space otherwise, it will not start. + { + Stop(ref user); + return TvResult.NoFreeDiskSpace; + } + } + + Log.Write("card: StartTimeShifting {0} {1} ", _cardHandler.DataBaseCard.IdCard, fileName); + + if (_cardHandler.IsLocal == false) + { + return RemoteControl.Instance.StartTimeShiftingWithCustom(ref user, ref fileName, ref CustomFileName, ref Pids); + } + } + catch (Exception) + { + Log.Error("card: unable to connect to slave controller at:{0}", + _cardHandler.DataBaseCard.ReferencedServer().HostName); + Stop(ref user); + return TvResult.UnknownError; + } + + ITvCardContext context = _cardHandler.Card.Context as ITvCardContext; + if (context == null) + { + Stop(ref user); + return TvResult.UnknownChannel; + } + + context.GetUser(ref user); + ITvSubChannel subchannel = _cardHandler.Card.GetSubChannel(user.SubChannel); + + if (subchannel == null) + { + Stop(ref user); + return TvResult.UnknownChannel; + } + + _subchannel = subchannel; + + Log.Write("card: CAM enabled : {0}", _cardHandler.HasCA); + + if (subchannel is TvDvbChannel) + { + if (!((TvDvbChannel)subchannel).PMTreceived) + { + Log.Info("start subch:{0} No PMT received. Timeshifting failed", subchannel.SubChannelId); + Stop(ref user); + return TvResult.UnableToStartGraph; + } + } + + if (subchannel is BaseSubChannel) + { + ((BaseSubChannel)subchannel).AudioVideoEvent += AudioVideoEventHandler; + } + + bool isScrambled; + if (subchannel.IsTimeShifting) + { + if (!WaitForTimeShiftFile(ref user, out isScrambled)) + { + Stop(ref user); + if (isScrambled) + { + return TvResult.ChannelIsScrambled; + } + return TvResult.NoVideoAudioDetected; + } + + context.OnZap(user); + if (_linkageScannerEnabled) + _cardHandler.Card.StartLinkageScanner(_linkageGrabber); + if (_timeshiftingEpgGrabberEnabled) + { + Channel channel = Channel.Retrieve(user.IdChannel); + if (channel.GrabEpg) + _cardHandler.Card.GrabEpg(); + else + Log.Info("TimeshiftingEPG: channel {0} is not configured for grabbing epg", + channel.DisplayName); + } + return TvResult.Succeeded; + } + + bool result = subchannel.StartTimeShiftingWithCustom(fileName,CustomFileName,Pids); + if (result == false) + { + Stop(ref user); + return TvResult.UnableToStartGraph; + } + + fileName += ".tsbuffer"; + if (!WaitForTimeShiftFile(ref user, out isScrambled)) + { + Stop(ref user); + if (isScrambled) + { + return TvResult.ChannelIsScrambled; + } + return TvResult.NoVideoAudioDetected; + } + context.OnZap(user); + if (_linkageScannerEnabled) + _cardHandler.Card.StartLinkageScanner(_linkageGrabber); + if (_timeshiftingEpgGrabberEnabled) + { + Channel channel = Channel.Retrieve(user.IdChannel); + if (channel.GrabEpg) + _cardHandler.Card.GrabEpg(); + else + Log.Info("TimeshiftingEPG: channel {0} is not configured for grabbing epg", + channel.DisplayName); + } + return TvResult.Succeeded; + } + } + catch (Exception ex) + { + Log.Write(ex); + } + + Stop(ref user); + return TvResult.UnknownError; + } /// /// Stops the time shifting. /// Index: TvEngine3/TVLibrary/TvService/TVController.cs =================================================================== --- TvEngine3/TVLibrary/TvService/TVController.cs (revision 27796) +++ TvEngine3/TVLibrary/TvService/TVController.cs (working copy) @@ -1887,6 +1887,94 @@ return TvResult.UnknownError; } + /// + /// Start timeshifting. + /// + /// + /// Name of the timeshiftfile. + /// CustomFileName + /// Pids + /// + /// TvResult indicating whether method succeeded + /// + /// + public TvResult StartTimeShiftingWithCustom(ref IUser user, ref string fileName,ref string CustomFileName, ref List Pids) + { + if (ValidateTvControllerParams(user)) + return TvResult.UnknownError; + try + { + int cardId = user.CardId; + if (false == _cards[cardId].IsLocal) + { + try + { + RemoteControl.HostName = _cards[cardId].DataBaseCard.ReferencedServer().HostName; + return RemoteControl.Instance.StartTimeShiftingWithCustom(ref user, ref fileName, ref CustomFileName, ref Pids); + } + catch (Exception) + { + Log.Error("card: unable to connect to slave controller at:{0}", + _cards[cardId].DataBaseCard.ReferencedServer().HostName); + return TvResult.UnknownError; + } + } + + Fire(this, new TvServerEventArgs(TvServerEventType.StartTimeShifting, GetVirtualCard(user), (User)user)); + if (_epgGrabber != null) + { + _epgGrabber.Stop(); + } + + bool isTimeShifting; + try + { + isTimeShifting = _cards[cardId].TimeShifter.IsTimeShifting(ref user); + } + catch (Exception ex) + { + isTimeShifting = false; + Log.Error("Exception in checking " + ex.Message); + } + TvResult result = _cards[cardId].TimeShifter.StartWithCustom(ref user, ref fileName, ref CustomFileName, ref Pids); + if (result == TvResult.Succeeded) + { + if (!isTimeShifting) + { + Log.Info("user:{0} card:{1} sub:{2} add stream:{3}", user.Name, user.CardId, user.SubChannel, fileName); + if (File.Exists(fileName)) + { + _streamer.Start(); + + // Default to tv + bool isTv = true; + + ITvSubChannel subChannel = _cards[cardId].Card.GetSubChannel(user.SubChannel); + + if (subChannel != null && subChannel.CurrentChannel != null) + isTv = subChannel.CurrentChannel.IsTv; + else + Log.Error("SubChannel or CurrentChannel is null when starting streaming"); + + RtspStream stream = new RtspStream(String.Format("stream{0}.{1}", cardId, user.SubChannel), fileName, + _cards[cardId].Card, isTv); + _streamer.AddStream(stream); + } + else + { + Log.Write("Controller: streaming: file not found:{0}", fileName); + } + } + } + return result; + } + catch (Exception ex) + { + Log.Write(ex); + } + return TvResult.UnknownError; + } + public void StopCard(IUser user) { if (ValidateTvControllerParams(user)) @@ -2856,7 +2944,312 @@ return StartTimeShifting(ref user, idChannel, out card, false, out cardChanged); } +//Custom TimeShifting + /// + /// Start timeshifting on a specific channel + /// + /// user credentials. + /// The id channel. + /// returns card for which timeshifting is started + /// Indicated, if the card should be forced + /// indicates if card was changed + /// + /// TvResult indicating whether method succeeded + /// + public TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, bool forceCardId,string CustomFileName, List Pids) + { + bool cardChanged = false; + return StartTimeShiftingWithCustom(ref user, idChannel, out card, forceCardId, out cardChanged,CustomFileName,Pids); + } + + /// + /// Start timeshifting on a specific channel + /// + /// user credentials. + /// The id channel. + /// returns card for which timeshifting is started + /// Indicated, if the card should be forced + /// indicates if card was changed + /// + /// TvResult indicating whether method succeeded + /// + public TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, bool forceCardId, + out bool cardChanged,string CustomFileName, List Pids) + { + cardChanged = false; + if (user == null) + { + card = null; + return TvResult.UnknownError; + } + + string intialTimeshiftingFilename = ""; + + VirtualCard initialCard = null; + + if (user.CardId != -1) + initialCard = GetVirtualCard(user); + + if (initialCard != null && initialCard.TimeShiftFileName != null) + { + intialTimeshiftingFilename = initialCard.TimeShiftFileName; + } + + Channel channel = Channel.Retrieve(idChannel); + Log.Write("Controller: StartTimeShifting {0} {1}", channel.DisplayName, channel.IdChannel); + card = null; + if (_epgGrabber != null) + { + _epgGrabber.Stop(); + } + IUser userCopy = null; + try + { + TvResult result; + List freeCards = _cardAllocation.GetFreeCardsForChannel(_cards, channel, ref user, out result); + if (freeCards.Count == 0) + { + //no free cards available + Log.Write("Controller: StartTimeShifting failed:{0}", result); + + if (_epgGrabber != null && AllCardsIdle) + { + _epgGrabber.Start(); + } + return result; + } + + int maxCards; + if (_maxFreeCardsToTry == 0) + { + maxCards = freeCards.Count; + } + else + { + maxCards = Math.Min(_maxFreeCardsToTry, freeCards.Count); + + if (maxCards > freeCards.Count) + { + maxCards = freeCards.Count; + } + } + + Log.Write("Controller: try max {0} of {1} cards for timeshifting", maxCards, freeCards.Count); + TvBusinessLayer layer = new TvBusinessLayer(); + //keep tuning each card until we are succesful + for (int i = 0; i < maxCards; i++) + { + int nrOfOtherUsersTimeshiftingOnCard = 0; + if (i > 0) + { + Log.Write("Controller: Timeshifting failed, lets try next available card."); + cardChanged = (maxCards > 1); + } + userCopy = new User(user.Name, user.IsAdmin); + + CardDetail cardInfo = freeCards[i]; + userCopy.CardId = cardInfo.Id; + if (forceCardId && user.CardId != cardInfo.Id) + { + continue; + } + IChannel tuneChannel = cardInfo.TuningDetail; + + //setup folders + if (cardInfo.Card.RecordingFolder == String.Empty) + { + cardInfo.Card.RecordingFolder = String.Format(@"{0}\Team MediaPortal\MediaPortal TV Server\recordings", + Environment.GetFolderPath( + Environment.SpecialFolder.CommonApplicationData)); + if (!Directory.Exists(cardInfo.Card.RecordingFolder)) + { + Log.Write("Controller: creating recording folder {0} for card {0}", cardInfo.Card.RecordingFolder, + cardInfo.Card.Name); + Directory.CreateDirectory(cardInfo.Card.RecordingFolder); + } + } + if (cardInfo.Card.TimeShiftFolder == String.Empty) + { + cardInfo.Card.TimeShiftFolder = String.Format( + @"{0}\Team MediaPortal\MediaPortal TV Server\timeshiftbuffer", + Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + if (!Directory.Exists(cardInfo.Card.TimeShiftFolder)) + { + Log.Write("Controller: creating timeshifting folder {0} for card {0}", cardInfo.Card.TimeShiftFolder, + cardInfo.Card.Name); + Directory.CreateDirectory(cardInfo.Card.TimeShiftFolder); + } + } + + //todo : if the owner is changing channel to a new transponder, then kick any leeching users. + ITvCardHandler tvcard = _cards[cardInfo.Id]; + bool isTS = tvcard.TimeShifter.IsAnySubChannelTimeshifting; + bool skipCard = false; + if (isTS) + { + IUser[] users = tvcard.Users.GetUsers(); + for (int j = users.Length - 1; j > -1; j--) + { + IUser u = users[j]; + if (user.Name.Equals(u.Name)) + { + continue; + } + + IChannel tmpChannel = tvcard.CurrentChannel(ref u); + + if (tmpChannel == null) + { + tvcard.Users.RemoveUser(u); + //removing inactive user which shouldnt happen, but atleast its better than having timeshfiting fail. + continue; + } + + bool isDiffTS = tuneChannel.IsDifferentTransponder(tmpChannel); + + if (isDiffTS) + { + Log.Write("Controller: kicking leech user {0} off card {1} since owner {2} changed transponder", u.Name, + cardInfo.Card.Name, user.Name); + StopTimeShifting(ref u, TvStoppedReason.OwnerChangedTS); + } + else + { + DVBBaseChannel dvbBaseChannel = tmpChannel as DVBBaseChannel; + if (dvbBaseChannel != null) + { + TuningDetail userChannel = layer.GetTuningDetail(dvbBaseChannel); + bool isOnSameChannel = (idChannel == userChannel.IdChannel); + + if (isOnSameChannel) + { + if (tvcard.Users.IsOwner(userCopy)) + { + if (i < maxCards) + { + Log.Write("Controller: skipping card:{0} since other users are present on the same channel.", + userCopy.CardId); + skipCard = true; + break; //try next card + } + } + else + { + nrOfOtherUsersTimeshiftingOnCard++; + if (!u.IsAdmin) + { + userCopy.SubChannel = u.SubChannel; + } + } + } + } + } + } + } + + if (skipCard) + { + continue; + } + + //tune to the new channel + result = CardTune(ref userCopy, tuneChannel, channel); + if (result != TvResult.Succeeded) + { + user.FailedCardId = userCopy.FailedCardId; + StopTimeShifting(ref userCopy); + continue; //try next card + } + Log.Info("control2:{0} {1} {2}", userCopy.Name, userCopy.CardId, userCopy.SubChannel); + if (!IsTimeShifting(ref userCopy)) + { + CleanTimeShiftFiles(cardInfo.Card.TimeShiftFolder, + String.Format("live{0}-{1}.ts", userCopy.CardId, userCopy.SubChannel)); + } + string timeshiftFileName = String.Format(@"{0}\live{1}-{2}.ts", cardInfo.Card.TimeShiftFolder, userCopy.CardId, + userCopy.SubChannel); + + //start timeshifting + result = StartTimeShiftingWithCustom(ref userCopy, ref timeshiftFileName, ref CustomFileName, ref Pids); + if (result != TvResult.Succeeded) + { + StopTimeShifting(ref userCopy); + continue; //try next card + } + Log.Write("Controller: StartTimeShifting started on card:{0} to {1}", userCopy.CardId, timeshiftFileName); + card = GetVirtualCard(userCopy); + card.NrOfOtherUsersTimeshiftingOnCard = nrOfOtherUsersTimeshiftingOnCard; + RemoveUserFromOtherCards(card.Id, userCopy); //only remove user from other cards if new tuning was a success + UpdateChannelStatesForUsers(); + + if (card != null && card.TimeShiftFileName != null) + { + string newTimeshiftingFilename = card.TimeShiftFileName; + cardChanged = (intialTimeshiftingFilename != newTimeshiftingFilename); + } + + break; //if we made it to the bottom, then we have a successful timeshifting. + } + + if (result != TvResult.Succeeded) + { + if (_epgGrabber != null && AllCardsIdle) + { + _epgGrabber.Start(); + } + } + + return result; + } + catch (Exception ex) + { + if (userCopy != null) + { + user.FailedCardId = userCopy.FailedCardId; + } + if (_epgGrabber != null && AllCardsIdle) + { + _epgGrabber.Start(); + } + Log.Write(ex); + return TvResult.UnknownError; + } + } + + /// + /// Start timeshifting on a specific channel + /// + /// user credentials. + /// The id channel. + /// returns card for which timeshifting is started + /// + /// TvResult indicating whether method succeeded + /// + public TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, string CustomFileName, List Pids) + { + bool cardChanged = false; + return StartTimeShiftingWithCustom(ref user, idChannel, out card, false, out cardChanged,CustomFileName,Pids); + } + + /// + /// Start timeshifting on a specific channel + /// + /// user credentials. + /// The id channel. + /// returns card for which timeshifting is started + /// indicates if card was changed + /// + /// TvResult indicating whether method succeeded + /// + public TvResult StartTimeShiftingWithCustom(ref IUser user, int idChannel, out VirtualCard card, out bool cardChanged,string CustomFileName, List Pids) + { + return StartTimeShiftingWithCustom(ref user, idChannel, out card, false, out cardChanged, CustomFileName, Pids); + } + +//End Custom Fileshifting + + /// /// Checks if the schedule specified is currently being recorded and ifso /// returns on which card ///