Skip to content
Browse files

Merge pull request #261 from fetzerch/release-v1.9.14

[mythtv-cmyth] Release v1.9.14
  • Loading branch information...
2 parents 868e611 + cd21e85 commit 8ba792d98bbb7d0deed4e3c06038255eb55e86cc @opdenkamp committed Jan 25, 2014
Showing with 6,241 additions and 181 deletions.
  1. +13 −1 addons/pvr.mythtv.cmyth/Makefile.am
  2. +1 −1 addons/pvr.mythtv.cmyth/addon/addon.xml.in
  3. +13 −0 addons/pvr.mythtv.cmyth/addon/changelog.txt
  4. +48 −0 addons/pvr.mythtv.cmyth/addon/resources/language/English/strings.po
  5. +4 −0 addons/pvr.mythtv.cmyth/addon/resources/settings.xml
  6. +27 −0 addons/pvr.mythtv.cmyth/project/VS2010Express/pvr.mythtv.cmyth.vcxproj
  7. +80 −0 addons/pvr.mythtv.cmyth/project/VS2010Express/pvr.mythtv.cmyth.vcxproj.filters
  8. +141 −14 addons/pvr.mythtv.cmyth/src/client.cpp
  9. +12 −0 addons/pvr.mythtv.cmyth/src/client.h
  10. +4 −0 addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp
  11. +9 −1 addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp
  12. +1 −1 addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h
  13. +26 −0 addons/pvr.mythtv.cmyth/src/cppmyth/MythRecordingRule.cpp
  14. +8 −1 addons/pvr.mythtv.cmyth/src/cppmyth/MythRecordingRule.h
  15. +366 −40 addons/pvr.mythtv.cmyth/src/cppmyth/MythScheduleManager.cpp
  16. +28 −12 addons/pvr.mythtv.cmyth/src/cppmyth/MythScheduleManager.h
  17. +584 −0 addons/pvr.mythtv.cmyth/src/demux.cpp
  18. +95 −0 addons/pvr.mythtv.cmyth/src/demux.h
  19. +253 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_AAC.cpp
  20. +56 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_AAC.h
  21. +251 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_AC3.cpp
  22. +47 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_AC3.h
  23. +144 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_MPEGAudio.cpp
  24. +46 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_MPEGAudio.h
  25. +278 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_MPEGVideo.cpp
  26. +58 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_MPEGVideo.h
  27. +62 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_Subtitle.cpp
  28. +35 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_Subtitle.h
  29. +57 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_Teletext.cpp
  30. +35 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_Teletext.h
  31. +583 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_h264.cpp
  32. +111 −0 addons/pvr.mythtv.cmyth/src/demuxer/ES_h264.h
  33. +143 −0 addons/pvr.mythtv.cmyth/src/demuxer/bitstream.cpp
  34. +50 −0 addons/pvr.mythtv.cmyth/src/demuxer/bitstream.h
  35. +43 −0 addons/pvr.mythtv.cmyth/src/demuxer/common.h
  36. +111 −0 addons/pvr.mythtv.cmyth/src/demuxer/debug.cpp
  37. +44 −0 addons/pvr.mythtv.cmyth/src/demuxer/debug.h
  38. +279 −0 addons/pvr.mythtv.cmyth/src/demuxer/elementaryStream.cpp
  39. +113 −0 addons/pvr.mythtv.cmyth/src/demuxer/elementaryStream.h
  40. +1,012 −0 addons/pvr.mythtv.cmyth/src/demuxer/tsDemuxer.cpp
  41. +130 −0 addons/pvr.mythtv.cmyth/src/demuxer/tsDemuxer.h
  42. +77 −0 addons/pvr.mythtv.cmyth/src/demuxer/tsPacket.h
  43. +55 −0 addons/pvr.mythtv.cmyth/src/demuxer/tsTable.h
  44. +1 −1 addons/pvr.mythtv.cmyth/src/fileOps.cpp
  45. +203 −67 addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp
  46. +24 −0 addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.h
  47. +43 −1 lib/cmyth/include/cmyth/cmyth.h
  48. +7 −0 lib/cmyth/libcmyth/cmyth_local.h
  49. +1 −1 lib/cmyth/libcmyth/file.c
  50. +18 −0 lib/cmyth/libcmyth/keyframe.c
  51. +24 −21 lib/cmyth/libcmyth/mythtv_mysql.c
  52. +18 −0 lib/cmyth/libcmyth/posmap.c
  53. +216 −14 lib/cmyth/libcmyth/recorder.c
  54. +42 −0 lib/cmyth/libcmyth/recordingrule.c
  55. +111 −5 lib/cmyth/libcmyth/socket.c
View
14 addons/pvr.mythtv.cmyth/Makefile.am
@@ -33,4 +33,16 @@ libmythtvcmyth_addon_la_SOURCES = src/client.cpp \
src/cppmyth/MythRecordingRule.cpp \
src/cppmyth/MythTimestamp.cpp \
src/cppmyth/MythEPGInfo.cpp \
- src/cppmyth/MythScheduleManager.cpp
+ src/cppmyth/MythScheduleManager.cpp \
+ src/demux.cpp \
+ src/demuxer/debug.cpp \
+ src/demuxer/elementaryStream.cpp \
+ src/demuxer/tsDemuxer.cpp \
+ src/demuxer/bitstream.cpp \
+ src/demuxer/ES_MPEGVideo.cpp \
+ src/demuxer/ES_MPEGAudio.cpp \
+ src/demuxer/ES_h264.cpp \
+ src/demuxer/ES_AAC.cpp \
+ src/demuxer/ES_AC3.cpp \
+ src/demuxer/ES_Subtitle.cpp \
+ src/demuxer/ES_Teletext.cpp
View
2 addons/pvr.mythtv.cmyth/addon/addon.xml.in
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon
id="pvr.mythtv.cmyth"
- version="1.9.13"
+ version="1.9.14"
name="MythTV cmyth PVR Client"
provider-name="Christian Fetzer, Jean-Luc Barrière, Tonny Petersen">
<requires>
View
13 addons/pvr.mythtv.cmyth/addon/changelog.txt
@@ -1,3 +1,16 @@
+v1.9.14
+- Added demuxer (optional)
+ - Improved timeshifting (GetPlayingTime, GetBufferStart/EndTime)
+ - Faster channel switching
+- Added possibility to start backend using Wake-on-LAN
+- Added client actions
+ - Toogle visibility of recordings that are in state 'Not recording'
+ - Create special recording rules (series recording)
+- Fixed compatibility with MythTV 0.27 backend
+ - Fixed channel icon download
+ - Fixed schedule management
+- Fixed recognizing merged channels
+
v1.9.13
- add timeshift buffer functions
View
48 addons/pvr.mythtv.cmyth/addon/resources/language/English/strings.po
@@ -66,6 +66,10 @@ msgctxt "#30011"
msgid "Prefer Live TV and cancel conflicting recording"
msgstr ""
+msgctxt "#30012"
+msgid "MythTV Backend Ethernet address (WOL)"
+msgstr ""
+
msgctxt "#30019"
msgid "General"
msgstr ""
@@ -126,6 +130,14 @@ msgctxt "#30049"
msgid "Recording template"
msgstr ""
+msgctxt "#30050"
+msgid "Advanced"
+msgstr ""
+
+msgctxt "#30052"
+msgid "Enable demuxing MPEG-TS"
+msgstr ""
+
# Systeminformation labels
msgctxt "#30100"
msgid "Protocol version: %i - Database version: %i"
@@ -172,6 +184,18 @@ msgctxt "#30309"
msgid "Not recording"
msgstr ""
+msgctxt "#30310"
+msgid "Enabled"
+msgstr ""
+
+msgctxt "#30311"
+msgid "Disabled"
+msgstr ""
+
+msgctxt "#30312"
+msgid "No broadcast found"
+msgstr ""
+
# Menu Hooks
msgctxt "#30411"
msgid "Delete and re-record"
@@ -180,3 +204,27 @@ msgstr ""
msgctxt "#30412"
msgid "Keep LiveTV recording"
msgstr ""
+
+msgctxt "#30421"
+msgid "Show/hide rules with status 'Not Recording'"
+msgstr ""
+
+msgctxt "#30431"
+msgid "Record all showings (this channel)"
+msgstr ""
+
+msgctxt "#30432"
+msgid "Record this showing every week"
+msgstr ""
+
+msgctxt "#30433"
+msgid "Record this showing every day"
+msgstr ""
+
+msgctxt "#30434"
+msgid "Record one showing (all channels)"
+msgstr ""
+
+msgctxt "#30435"
+msgid "Record all new episodes (this channel)"
+msgstr ""
View
4 addons/pvr.mythtv.cmyth/addon/resources/settings.xml
@@ -8,6 +8,7 @@
<setting id="db_name" type="text" label="30004" default="mythconverg" />
<setting id="db_host" type="text" visible="false" />
<setting id="db_port" type="number" option="int" visible="false" default="3306" />
+ <setting id="host_ether" type="text" label="30012" default="" />
<setting id="extradebug" type="bool" label="30005" default="false" />
<setting id="livetv" type="bool" label="30006" default="true" />
<setting id="livetv_priority" type="bool" label="30007" default="true" />
@@ -26,4 +27,7 @@
<setting id="rec_autorunjob3" type="bool" label="30031" enable="eq(-9,0)" default="false" />
<setting id="rec_autorunjob4" type="bool" label="30032" enable="eq(-10,0)" default="false" />
</category>
+ <category label="30050">
+ <setting id="demuxing" type="bool" label="30052" default="false" />
+ </category>
</settings>
View
27 addons/pvr.mythtv.cmyth/project/VS2010Express/pvr.mythtv.cmyth.vcxproj
@@ -26,6 +26,18 @@
<ClCompile Include="..\..\src\cppmyth\MythStorageGroupFile.cpp" />
<ClCompile Include="..\..\src\cppmyth\MythSignal.cpp" />
<ClCompile Include="..\..\src\cppmyth\MythTimestamp.cpp" />
+ <ClCompile Include="..\..\src\demux.cpp" />
+ <ClCompile Include="..\..\src\demuxer\bitstream.cpp" />
+ <ClCompile Include="..\..\src\demuxer\debug.cpp" />
+ <ClCompile Include="..\..\src\demuxer\elementaryStream.cpp" />
+ <ClCompile Include="..\..\src\demuxer\ES_AAC.cpp" />
+ <ClCompile Include="..\..\src\demuxer\ES_AC3.cpp" />
+ <ClCompile Include="..\..\src\demuxer\ES_h264.cpp" />
+ <ClCompile Include="..\..\src\demuxer\ES_MPEGAudio.cpp" />
+ <ClCompile Include="..\..\src\demuxer\ES_MPEGVideo.cpp" />
+ <ClCompile Include="..\..\src\demuxer\ES_Subtitle.cpp" />
+ <ClCompile Include="..\..\src\demuxer\ES_Teletext.cpp" />
+ <ClCompile Include="..\..\src\demuxer\tsDemuxer.cpp" />
<ClCompile Include="..\..\src\fileOps.cpp" />
<ClCompile Include="..\..\src\pvrclient-mythtv.cpp" />
</ItemGroup>
@@ -47,6 +59,21 @@
<ClInclude Include="..\..\src\cppmyth\MythStorageGroupFile.h" />
<ClInclude Include="..\..\src\cppmyth\MythSignal.h" />
<ClInclude Include="..\..\src\cppmyth\MythTimestamp.h" />
+ <ClInclude Include="..\..\src\demux.h" />
+ <ClInclude Include="..\..\src\demuxer\bitstream.h" />
+ <ClInclude Include="..\..\src\demuxer\common.h" />
+ <ClInclude Include="..\..\src\demuxer\debug.h" />
+ <ClInclude Include="..\..\src\demuxer\elementaryStream.h" />
+ <ClInclude Include="..\..\src\demuxer\ES_AAC.h" />
+ <ClInclude Include="..\..\src\demuxer\ES_AC3.h" />
+ <ClInclude Include="..\..\src\demuxer\ES_h264.h" />
+ <ClInclude Include="..\..\src\demuxer\ES_MPEGAudio.h" />
+ <ClInclude Include="..\..\src\demuxer\ES_MPEGVideo.h" />
+ <ClInclude Include="..\..\src\demuxer\ES_Subtitle.h" />
+ <ClInclude Include="..\..\src\demuxer\ES_Teletext.h" />
+ <ClInclude Include="..\..\src\demuxer\tsDemuxer.h" />
+ <ClInclude Include="..\..\src\demuxer\tsPacket.h" />
+ <ClInclude Include="..\..\src\demuxer\tsTable.h" />
<ClInclude Include="..\..\src\fileOps.h" />
<ClInclude Include="..\..\src\pvrclient-mythtv.h" />
<ClInclude Include="..\..\src\tools.h" />
View
80 addons/pvr.mythtv.cmyth/project/VS2010Express/pvr.mythtv.cmyth.vcxproj.filters
@@ -44,6 +44,40 @@
<ClCompile Include="..\..\src\cppmyth\MythScheduleManager.cpp">
<Filter>cppmyth</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\demuxer\elementaryStream.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\ES_AAC.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\ES_AC3.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\ES_h264.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\ES_MPEGAudio.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\ES_MPEGVideo.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\ES_Subtitle.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\ES_Teletext.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\tsDemuxer.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demuxer\bitstream.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\demux.cpp" />
+ <ClCompile Include="..\..\src\demuxer\debug.cpp">
+ <Filter>demuxer</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\client.h" />
@@ -94,6 +128,49 @@
<ClInclude Include="..\..\src\cppmyth\MythScheduleManager.h">
<Filter>cppmyth</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\demuxer\ES_Teletext.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\elementaryStream.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\ES_AAC.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\ES_AC3.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\ES_h264.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\ES_MPEGAudio.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\ES_MPEGVideo.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\ES_Subtitle.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\tsDemuxer.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\tsPacket.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\tsTable.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\bitstream.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demuxer\common.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\demux.h" />
+ <ClInclude Include="..\..\src\demuxer\debug.h">
+ <Filter>demuxer</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cppmyth">
@@ -111,6 +188,9 @@
<Filter Include="addon\resources\language\English">
<UniqueIdentifier>{3ba85d2f-45bc-4080-8895-55949f7f52a7}</UniqueIdentifier>
</Filter>
+ <Filter Include="demuxer">
+ <UniqueIdentifier>{af26b031-f1b0-4eed-bdb7-1e12feef27d0}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<None Include="..\..\addon\changelog.txt">
View
155 addons/pvr.mythtv.cmyth/src/client.cpp
@@ -32,6 +32,7 @@ using namespace ADDON;
* and exported to the other source files.
*/
CStdString g_szMythHostname = DEFAULT_HOST; ///< The Host name or IP of the mythtv server
+CStdString g_szMythHostEther = ""; ///< The Host MAC address of the mythtv server
int g_iMythPort = DEFAULT_PORT; ///< The mythtv Port (default is 6543)
CStdString g_szDBUser = DEFAULT_DB_USER; ///< The mythtv sql username (default is mythtv)
CStdString g_szDBPassword = DEFAULT_DB_PASSWORD; ///< The mythtv sql password (default is mythtv)
@@ -52,6 +53,7 @@ bool g_bRecAutoRunJob3 = false;
bool g_bRecAutoRunJob4 = false;
bool g_bRecAutoExpire = false;
int g_iRecTranscoder = 0;
+bool g_bDemuxing = DEFAULT_HANDLE_DEMUXING;
///* Client member variables */
ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN;
@@ -65,6 +67,7 @@ PVRClientMythTV *g_client = NULL;
CHelper_libXBMC_addon *XBMC = NULL;
CHelper_libXBMC_pvr *PVR = NULL;
CHelper_libXBMC_gui *GUI = NULL;
+CHelper_libXBMC_codec *CODEC = NULL;
extern "C" {
@@ -117,6 +120,15 @@ ADDON_STATUS ADDON_Create(void *hdl, void *props)
}
XBMC->Log(LOG_DEBUG, "Register handle @ libXBMC_gui...done");
+ CODEC = new CHelper_libXBMC_codec;
+ if (!CODEC->RegisterMe(hdl))
+ {
+ SAFE_DELETE(PVR);
+ SAFE_DELETE(XBMC);
+ SAFE_DELETE(GUI);
+ return ADDON_STATUS_PERMANENT_FAILURE;
+ }
+
m_CurStatus = ADDON_STATUS_UNKNOWN;
g_szUserPath = pvrprops->strUserPath;
g_szClientPath = pvrprops->strClientPath;
@@ -250,6 +262,24 @@ ADDON_STATUS ADDON_Create(void *hdl, void *props)
if (!XBMC->GetSetting("rec_transcoder", &g_iRecTranscoder))
g_iRecTranscoder = 0;
+ /* Read setting "demuxing" from settings.xml */
+ if (!XBMC->GetSetting("demuxing", &g_bDemuxing))
+ {
+ /* If setting is unknown fallback to defaults */
+ XBMC->Log(LOG_ERROR, "Couldn't get 'demuxing' setting, falling back to '%b' as default", DEFAULT_HANDLE_DEMUXING);
+ g_bDemuxing = DEFAULT_HANDLE_DEMUXING;
+ }
+
+ /* Read setting "host_ether" from settings.xml */
+ if (XBMC->GetSetting("host_ether", buffer))
+ g_szMythHostEther = buffer;
+ else
+ {
+ /* If setting is unknown fallback to defaults */
+ g_szMythHostEther = "";
+ }
+ buffer[0] = 0;
+
free (buffer);
// Create our addon
@@ -259,6 +289,7 @@ ADDON_STATUS ADDON_Create(void *hdl, void *props)
{
XBMC->Log(LOG_ERROR, "Failed to connect to backend");
SAFE_DELETE(g_client);
+ SAFE_DELETE(CODEC);
SAFE_DELETE(GUI);
SAFE_DELETE(PVR);
SAFE_DELETE(XBMC);
@@ -290,6 +321,42 @@ ADDON_STATUS ADDON_Create(void *hdl, void *props)
menuHookKeepLiveTVRec.iLocalizedStringId = 30412;
PVR->AddMenuHook(&menuHookKeepLiveTVRec);
+ PVR_MENUHOOK menuhookSettingShowNR;
+ menuhookSettingShowNR.category = PVR_MENUHOOK_SETTING;
+ menuhookSettingShowNR.iHookId = MENUHOOK_SHOW_HIDE_NOT_RECORDING;
+ menuhookSettingShowNR.iLocalizedStringId = 30421;
+ PVR->AddMenuHook(&menuhookSettingShowNR);
+
+ PVR_MENUHOOK menuhookEpgRec1;
+ menuhookEpgRec1.category = PVR_MENUHOOK_EPG;
+ menuhookEpgRec1.iHookId = MENUHOOK_EPG_REC_CHAN_ALL_SHOWINGS;
+ menuhookEpgRec1.iLocalizedStringId = 30431;
+ PVR->AddMenuHook(&menuhookEpgRec1);
+
+ PVR_MENUHOOK menuhookEpgRec2;
+ menuhookEpgRec2.category = PVR_MENUHOOK_EPG;
+ menuhookEpgRec2.iHookId = MENUHOOK_EPG_REC_CHAN_WEEKLY;
+ menuhookEpgRec2.iLocalizedStringId = 30432;
+ PVR->AddMenuHook(&menuhookEpgRec2);
+
+ PVR_MENUHOOK menuhookEpgRec3;
+ menuhookEpgRec3.category = PVR_MENUHOOK_EPG;
+ menuhookEpgRec3.iHookId = MENUHOOK_EPG_REC_CHAN_DAILY;
+ menuhookEpgRec3.iLocalizedStringId = 30433;
+ PVR->AddMenuHook(&menuhookEpgRec3);
+
+ PVR_MENUHOOK menuhookEpgRec4;
+ menuhookEpgRec4.category = PVR_MENUHOOK_EPG;
+ menuhookEpgRec4.iHookId = MENUHOOK_EPG_REC_ONE_SHOWING;
+ menuhookEpgRec4.iLocalizedStringId = 30434;
+ PVR->AddMenuHook(&menuhookEpgRec4);
+
+ PVR_MENUHOOK menuhookEpgRec5;
+ menuhookEpgRec5.category = PVR_MENUHOOK_EPG;
+ menuhookEpgRec5.iHookId = MENUHOOK_EPG_REC_NEW_EPISODES;
+ menuhookEpgRec5.iLocalizedStringId = 30435;
+ PVR->AddMenuHook(&menuhookEpgRec5);
+
XBMC->Log(LOG_DEBUG, "MythTV cmyth PVR-Client successfully created");
m_CurStatus = ADDON_STATUS_OK;
g_bCreated = true;
@@ -305,6 +372,12 @@ void ADDON_Destroy()
g_bCreated = false;
}
+ if (CODEC)
+ {
+ delete(CODEC);
+ CODEC = NULL;
+ }
+
if (PVR)
{
delete(PVR);
@@ -413,6 +486,12 @@ ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
if (tmp_sDBName != g_szDBName)
return ADDON_STATUS_NEED_RESTART;
}
+ else if (str == "demuxing")
+ {
+ XBMC->Log(LOG_INFO, "Changed Setting 'demuxing' from %u to %u", g_bDemuxing, *(bool*)settingValue);
+ if (g_bDemuxing != *(bool*)settingValue)
+ return ADDON_STATUS_NEED_RESTART;
+ }
else if (str == "extradebug")
{
XBMC->Log(LOG_INFO, "Changed Setting 'extra debug' from %u to %u", g_bExtraDebug, *(bool*)settingValue);
@@ -550,7 +629,7 @@ PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities)
pCapabilities->bSupportsTimers = true;
pCapabilities->bHandlesInputStream = true;
- pCapabilities->bHandlesDemuxing = false;
+ pCapabilities->bHandlesDemuxing = g_bDemuxing;
pCapabilities->bSupportsRecordings = true;
pCapabilities->bSupportsRecordingPlayCount = true;
@@ -564,12 +643,6 @@ PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities)
}
}
-PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* props)
-{
- (void)props;
- return PVR_ERROR_NOT_IMPLEMENTED;
-}
-
const char *GetBackendName()
{
return g_client->GetBackendName();
@@ -950,18 +1023,72 @@ long long LengthRecordedStream(void)
return g_client->LengthRecordedStream();
}
+/*******************************************/
+/** PVR Demux Functions **/
+
+PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties)
+{
+ if (g_client == NULL)
+ return PVR_ERROR_SERVER_ERROR;
+
+ return g_client->GetStreamProperties(pProperties);
+}
+
+void DemuxAbort(void)
+{
+ if (g_client != NULL)
+ g_client->DemuxAbort();
+}
+
+DemuxPacket* DemuxRead(void)
+{
+ if (g_client == NULL)
+ return NULL;
+
+ return g_client->DemuxRead();
+}
+
+void DemuxFlush(void)
+{
+ if (g_client != NULL)
+ g_client->DemuxFlush();
+}
+
+bool SeekTime(int time, bool backwards, double *startpts)
+{
+ if (g_client != NULL)
+ return g_client->SeekTime(time, backwards, startpts);
+ return false;
+}
+
+/*******************************************/
+/** PVR Timeshift Functions **/
+
+time_t GetPlayingTime()
+{
+ if (g_client != NULL)
+ return g_client->GetPlayingTime();
+ return 0;
+}
+
+time_t GetBufferTimeStart()
+{
+ if (g_client != NULL)
+ return g_client->GetBufferTimeStart();
+ return 0;
+}
+
+time_t GetBufferTimeEnd()
+{
+ if (g_client != NULL)
+ return g_client->GetBufferTimeEnd();
+ return 0;
+}
/*******************************************/
/** Unused API Functions **/
-DemuxPacket* DemuxRead() { return NULL; }
-void DemuxAbort() {}
void DemuxReset() {}
-void DemuxFlush() {}
const char * GetLiveStreamURL(const PVR_CHANNEL &) { return ""; }
-bool SeekTime(int,bool,double*) { return false; }
void SetSpeed(int) {};
-time_t GetPlayingTime() { return 0; }
-time_t GetBufferTimeStart() { return 0; }
-time_t GetBufferTimeEnd() { return 0; }
} //end extern "C"
View
12 addons/pvr.mythtv.cmyth/src/client.h
@@ -27,6 +27,7 @@
#include <libXBMC_addon.h>
#include <libXBMC_pvr.h>
#include <libXBMC_gui.h>
+#include <libXBMC_codec.h>
extern "C" {
#include <cmyth/cmyth.h>
@@ -74,6 +75,14 @@ static inline struct tm *localtime_r(const time_t * clock, struct tm *result)
#define MENUHOOK_REC_DELETE_AND_RERECORD 1
#define MENUHOOK_KEEP_LIVETV_RECORDING 2
+#define MENUHOOK_SHOW_HIDE_NOT_RECORDING 3
+#define MENUHOOK_EPG_REC_CHAN_ALL_SHOWINGS 4
+#define MENUHOOK_EPG_REC_CHAN_WEEKLY 5
+#define MENUHOOK_EPG_REC_CHAN_DAILY 6
+#define MENUHOOK_EPG_REC_ONE_SHOWING 7
+#define MENUHOOK_EPG_REC_NEW_EPISODES 8
+
+#define DEFAULT_HANDLE_DEMUXING false
/*!
* @brief PVR macros for string exchange
@@ -92,6 +101,7 @@ extern CStdString g_szClientPath; ///< The Path where this driver i
/* Client Settings */
extern CStdString g_szMythHostname; ///< The Host name or IP of the mythtv server
+extern CStdString g_szMythHostEther; ///< The Host MAC address of the mythtv server
extern int g_iMythPort; ///< The mythtv Port (default is 6543)
extern CStdString g_szDBUser; ///< The mythtv sql username (default is mythtv)
extern CStdString g_szDBPassword; ///< The mythtv sql password (default is mythtv)
@@ -113,9 +123,11 @@ extern bool g_bRecAutoRunJob3;
extern bool g_bRecAutoRunJob4;
extern bool g_bRecAutoExpire;
extern int g_iRecTranscoder;
+extern bool g_bDemuxing;
extern ADDON::CHelper_libXBMC_addon *XBMC;
extern CHelper_libXBMC_pvr *PVR;
extern CHelper_libXBMC_gui *GUI;
+extern CHelper_libXBMC_codec *CODEC;
#endif /* CLIENT_H */
View
4 addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp
@@ -134,6 +134,10 @@ bool MythConnection::IsConnected()
bool MythConnection::TryReconnect()
{
int retval;
+
+ if (!g_szMythHostEther.IsEmpty())
+ XBMC->WakeOnLan(g_szMythHostEther);
+
Lock();
if (m_playback)
retval = cmyth_conn_reconnect_playback(*m_conn_t);
View
10 addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp
@@ -102,6 +102,7 @@ class MythEventHandler::MythEventHandlerPrivate : public CThread
bool m_playback;
bool m_hang;
+ bool m_sendWOL;
CStdString m_currentRecordID;
MythFile m_currentFile;
@@ -122,6 +123,7 @@ MythEventHandler::MythEventHandlerPrivate::MythEventHandlerPrivate(const CStdStr
, m_signal()
, m_playback(false)
, m_hang(false)
+ , m_sendWOL(false)
, m_recordingChangeEventList()
, m_recordingChangePinCount(0)
{
@@ -535,6 +537,10 @@ void MythEventHandler::MythEventHandlerPrivate::RetryConnect()
m_hang = true;
while (!IsStopped())
{
+ // wake up the backend sending magic packet
+ if (m_sendWOL && !g_szMythHostEther.IsEmpty())
+ XBMC->WakeOnLan(g_szMythHostEther);
+
usleep(999999);
ref_release(*m_conn_t);
*m_conn_t = NULL;
@@ -547,6 +553,7 @@ void MythEventHandler::MythEventHandlerPrivate::RetryConnect()
XBMC->Log(LOG_NOTICE, "%s - Connected client to event socket", __FUNCTION__);
XBMC->QueueNotification(QUEUE_INFO, XBMC->GetLocalizedString(30303)); // MythTV backend available
m_hang = false;
+ m_sendWOL = false;
HandleRecordingListChange(); // Reload all recordings
break;
}
@@ -592,10 +599,11 @@ void MythEventHandler::Suspend()
}
}
-void MythEventHandler::Resume()
+void MythEventHandler::Resume(bool sendWOL)
{
if (m_imp->IsStopped())
{
+ m_imp->m_sendWOL = sendWOL;
m_imp->m_lock.Clear();
m_imp->CreateThread();
}
View
2 addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h
@@ -54,7 +54,7 @@ class MythEventHandler
void RegisterObserver(MythEventObserver *observer);
void Suspend();
- void Resume();
+ void Resume(bool sendWOL = false);
void PreventLiveChainUpdate();
void AllowLiveChainUpdate();
View
26 addons/pvr.mythtv.cmyth/src/cppmyth/MythRecordingRule.cpp
@@ -387,3 +387,29 @@ void MythRecordingRule::SetFilter(unsigned int filter)
{
cmyth_recordingrule_set_filter(*m_recordingrule_t, filter);
}
+
+CStdString MythRecordingRule::ProgramID() const
+{
+ char *buf = cmyth_recordingrule_programid(*m_recordingrule_t);
+ CStdString retval(buf);
+ ref_release(buf);
+ return retval;
+}
+
+void MythRecordingRule::SetProgramID(const CStdString &programid)
+{
+ cmyth_recordingrule_set_programid(*m_recordingrule_t, const_cast<char*>(programid.c_str()));
+}
+
+CStdString MythRecordingRule::SeriesID() const
+{
+ char *buf = cmyth_recordingrule_seriesid(*m_recordingrule_t);
+ CStdString retval(buf);
+ ref_release(buf);
+ return retval;
+}
+
+void MythRecordingRule::SetSeriesID(const CStdString &seriesid)
+{
+ cmyth_recordingrule_set_seriesid(*m_recordingrule_t, const_cast<char*>(seriesid.c_str()));
+}
View
9 addons/pvr.mythtv.cmyth/src/cppmyth/MythRecordingRule.h
@@ -88,7 +88,8 @@ class MythRecordingRule
FM_ThisEpisode = 0x040,
FM_ThisSeries = 0x080,
FM_ThisTime = 0x100,
- FM_ThisDayAndTime = 0x200
+ FM_ThisDayAndTime = 0x200,
+ FM_ThisChannel = 0x400
};
MythRecordingRule();
@@ -191,6 +192,12 @@ class MythRecordingRule
unsigned int Filter() const;
void SetFilter(unsigned int filter);
+ CStdString ProgramID() const;
+ void SetProgramID(const CStdString &programid);
+
+ CStdString SeriesID() const;
+ void SetSeriesID(const CStdString &seriesid);
+
private:
boost::shared_ptr<MythPointer<cmyth_recordingrule_t> > m_recordingrule_t;
};
View
406 addons/pvr.mythtv.cmyth/src/cppmyth/MythScheduleManager.cpp
@@ -108,13 +108,15 @@ MythScheduleManager::MythScheduleManager()
, m_db()
, m_dbSchemaVersion(0)
, m_versionHelper(new MythScheduleHelperNoHelper())
+ , m_showNotRecording(false)
{
}
MythScheduleManager::MythScheduleManager(MythConnection &con, MythDatabase &db)
: m_con(con)
, m_db(db)
, m_dbSchemaVersion(0)
+ , m_showNotRecording(false)
{
this->Update();
}
@@ -160,6 +162,10 @@ ScheduleList MythScheduleManager::GetUpcomingRecordings()
MythScheduleManager::MSM_ERROR MythScheduleManager::ScheduleRecording(const MythRecordingRule &rule)
{
+ // Don't schedule nil
+ if (rule.Type() == MythRecordingRule::RT_NotRecording)
+ return MSM_ERROR_FAILED;
+
if (!m_db.AddRecordingRule(rule))
return MSM_ERROR_FAILED;
@@ -630,22 +636,25 @@ void MythScheduleManager::Update()
}
// Add missed programs (NOT RECORDING) to upcoming recordings. User could delete them as needed.
- //ProgramInfoMap schedule = m_con.GetScheduledPrograms();
- //for (ProgramInfoMap::iterator it = schedule.begin(); it != schedule.end(); ++it)
- //{
- // if (m_recordingIndexByRuleId.count(it->second.RecordID()) == 0)
- // {
- // NodeById::const_iterator itr = m_rulesById.find(it->second.RecordID());
- // if (itr != m_rulesById.end() && !itr->second->HasOverrideRules())
- // {
- // boost::shared_ptr<MythProgramInfo> rec = boost::shared_ptr<MythProgramInfo>(new MythProgramInfo());
- // *rec = it->second;
- // unsigned int index = MakeIndex(it->second);
- // m_recordings.insert(RecordingList::value_type(index, rec));
- // m_recordingIndexByRuleId.insert(std::make_pair(it->second.RecordID(), index));
- // }
- // }
- //}
+ if (m_showNotRecording)
+ {
+ ProgramInfoMap schedule = m_con.GetScheduledPrograms();
+ for (ProgramInfoMap::iterator it = schedule.begin(); it != schedule.end(); ++it)
+ {
+ if (m_recordingIndexByRuleId.count(it->second.RecordID()) == 0)
+ {
+ NodeById::const_iterator itr = m_rulesById.find(it->second.RecordID());
+ if (itr != m_rulesById.end() && !itr->second->HasOverrideRules())
+ {
+ boost::shared_ptr<MythProgramInfo> rec = boost::shared_ptr<MythProgramInfo>(new MythProgramInfo());
+ *rec = it->second;
+ unsigned int index = MakeIndex(it->second);
+ m_recordings.insert(RecordingList::value_type(index, rec));
+ m_recordingIndexByRuleId.insert(std::make_pair(it->second.RecordID(), index));
+ }
+ }
+ }
+ }
if (g_bExtraDebug)
{
@@ -656,6 +665,11 @@ void MythScheduleManager::Update()
}
}
+RuleMetadata MythScheduleManager::GetMetadata(const MythRecordingRule &rule) const
+{
+ return m_versionHelper->GetMetadata(rule);
+}
+
MythRecordingRule MythScheduleManager::NewFromTemplate(MythEPGInfo &epgInfo)
{
return m_versionHelper->NewFromTemplate(epgInfo);
@@ -676,14 +690,20 @@ MythRecordingRule MythScheduleManager::NewWeeklyRecord(MythEPGInfo &epgInfo)
return m_versionHelper->NewWeeklyRecord(epgInfo);
}
-MythRecordingRule MythScheduleManager::NewChannelRecord(const CStdString &searchTitle)
+MythRecordingRule MythScheduleManager::NewChannelRecord(MythEPGInfo &epgInfo)
{
- return m_versionHelper->NewChannelRecord(searchTitle);
+ return m_versionHelper->NewChannelRecord(epgInfo);
}
-MythRecordingRule MythScheduleManager::NewOneRecord(const CStdString &searchTitle)
+MythRecordingRule MythScheduleManager::NewOneRecord(MythEPGInfo &epgInfo)
{
- return m_versionHelper->NewOneRecord(searchTitle);
+ return m_versionHelper->NewOneRecord(epgInfo);
+}
+
+bool MythScheduleManager::ToggleShowNotRecording()
+{
+ m_showNotRecording ^= true;
+ return m_showNotRecording;
}
///////////////////////////////////////////////////////////////////////////////
@@ -698,7 +718,17 @@ bool MythScheduleHelperNoHelper::SameTimeslot(MythRecordingRule &first, MythReco
return false;
}
-MythRecordingRule MythScheduleHelperNoHelper::NewFromTemplate(MythEPGInfo& epgInfo)
+RuleMetadata MythScheduleHelperNoHelper::GetMetadata(const MythRecordingRule &rule) const
+{
+ RuleMetadata meta;
+ (void)rule;
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "";
+ return meta;
+}
+
+MythRecordingRule MythScheduleHelperNoHelper::NewFromTemplate(MythEPGInfo &epgInfo)
{
(void)epgInfo;
return MythRecordingRule();
@@ -722,15 +752,15 @@ MythRecordingRule MythScheduleHelperNoHelper::NewWeeklyRecord(MythEPGInfo &epgIn
return MythRecordingRule();
}
-MythRecordingRule MythScheduleHelperNoHelper::NewChannelRecord(const CStdString &searchTitle)
+MythRecordingRule MythScheduleHelperNoHelper::NewChannelRecord(MythEPGInfo &epgInfo)
{
- (void)searchTitle;
+ (void)epgInfo;
return MythRecordingRule();
}
-MythRecordingRule MythScheduleHelperNoHelper::NewOneRecord(const CStdString &searchTitle)
+MythRecordingRule MythScheduleHelperNoHelper::NewOneRecord(MythEPGInfo &epgInfo)
{
- (void)searchTitle;
+ (void)epgInfo;
return MythRecordingRule();
}
@@ -799,6 +829,58 @@ bool MythScheduleHelper1226::SameTimeslot(MythRecordingRule &first, MythRecordin
return false;
}
+RuleMetadata MythScheduleHelper1226::GetMetadata(const MythRecordingRule &rule) const
+{
+ RuleMetadata meta;
+ time_t st = rule.StartTime();
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "";
+ switch (rule.Type())
+ {
+ case MythRecordingRule::RT_DailyRecord:
+ case MythRecordingRule::RT_FindDailyRecord:
+ meta.isRepeating = true;
+ meta.weekDays = 0x7F;
+ meta.marker = "d";
+ break;
+ case MythRecordingRule::RT_WeeklyRecord:
+ case MythRecordingRule::RT_FindWeeklyRecord:
+ meta.isRepeating = true;
+ meta.weekDays = 1 << ((weekday(&st) + 6) % 7);
+ meta.marker = "w";
+ break;
+ case MythRecordingRule::RT_ChannelRecord:
+ meta.isRepeating = true;
+ meta.weekDays = 0x7F;
+ meta.marker = "C";
+ break;
+ case MythRecordingRule::RT_AllRecord:
+ meta.isRepeating = true;
+ meta.weekDays = 0x7F;
+ meta.marker = "A";
+ break;
+ case MythRecordingRule::RT_OneRecord:
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "1";
+ break;
+ case MythRecordingRule::RT_DontRecord:
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "x";
+ break;
+ case MythRecordingRule::RT_OverrideRecord:
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "o";
+ break;
+ default:
+ break;
+ }
+ return meta;
+}
+
MythRecordingRule MythScheduleHelper1226::NewFromTemplate(MythEPGInfo &epgInfo)
{
MythRecordingRule rule;
@@ -850,6 +932,8 @@ MythRecordingRule MythScheduleHelper1226::NewSingleRecord(MythEPGInfo &epgInfo)
rule.SetCategory(epgInfo.Category());
rule.SetDescription(epgInfo.Description());
rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
}
else
{
@@ -862,7 +946,7 @@ MythRecordingRule MythScheduleHelper1226::NewSingleRecord(MythEPGInfo &epgInfo)
return rule;
}
-MythRecordingRule MythScheduleHelper1226::NewDailyRecord(MythEPGInfo& epgInfo)
+MythRecordingRule MythScheduleHelper1226::NewDailyRecord(MythEPGInfo &epgInfo)
{
MythRecordingRule rule = this->NewFromTemplate(epgInfo);
@@ -879,6 +963,8 @@ MythRecordingRule MythScheduleHelper1226::NewDailyRecord(MythEPGInfo& epgInfo)
rule.SetCategory(epgInfo.Category());
rule.SetDescription(epgInfo.Description());
rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
}
else
{
@@ -891,7 +977,7 @@ MythRecordingRule MythScheduleHelper1226::NewDailyRecord(MythEPGInfo& epgInfo)
return rule;
}
-MythRecordingRule MythScheduleHelper1226::NewWeeklyRecord(MythEPGInfo& epgInfo)
+MythRecordingRule MythScheduleHelper1226::NewWeeklyRecord(MythEPGInfo &epgInfo)
{
MythRecordingRule rule = this->NewFromTemplate(epgInfo);
@@ -908,6 +994,8 @@ MythRecordingRule MythScheduleHelper1226::NewWeeklyRecord(MythEPGInfo& epgInfo)
rule.SetCategory(epgInfo.Category());
rule.SetDescription(epgInfo.Description());
rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
}
else
{
@@ -920,30 +1008,64 @@ MythRecordingRule MythScheduleHelper1226::NewWeeklyRecord(MythEPGInfo& epgInfo)
return rule;
}
-MythRecordingRule MythScheduleHelper1226::NewChannelRecord(const CStdString &searchTitle)
+MythRecordingRule MythScheduleHelper1226::NewChannelRecord(MythEPGInfo &epgInfo)
{
- // Backend use the description to find program by keywords or title
- MythEPGInfo epgInfo;
MythRecordingRule rule = this->NewFromTemplate(epgInfo);
+
rule.SetType(MythRecordingRule::RT_ChannelRecord);
- rule.SetSearchType(MythRecordingRule::ST_TitleSearch);
- rule.SetSubtitle("");
- rule.SetDescription(searchTitle);
+
+ if (!epgInfo.IsNull())
+ {
+ rule.SetSearchType(MythRecordingRule::ST_TitleSearch);
+ rule.SetChannelID(epgInfo.ChannelID());
+ rule.SetStartTime(epgInfo.StartTime());
+ rule.SetEndTime(epgInfo.EndTime());
+ rule.SetTitle(epgInfo.Title());
+ // Backend use the description to find program by keywords or title
+ rule.SetSubtitle("");
+ rule.SetDescription(epgInfo.Title());
+ rule.SetCategory(epgInfo.Category());
+ rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
+ }
+ else
+ {
+ // Not feasible
+ rule.SetType(MythRecordingRule::RT_NotRecording);
+ }
rule.SetDuplicateControlMethod(MythRecordingRule::DM_CheckSubtitleAndDescription);
rule.SetCheckDuplicatesInType(MythRecordingRule::DI_InAll);
rule.SetInactive(false);
return rule;
}
-MythRecordingRule MythScheduleHelper1226::NewOneRecord(const CStdString &searchTitle)
+MythRecordingRule MythScheduleHelper1226::NewOneRecord(MythEPGInfo &epgInfo)
{
- // Backend use the description to find program by keywords or title
- MythEPGInfo epgInfo;
MythRecordingRule rule = this->NewFromTemplate(epgInfo);
+
rule.SetType(MythRecordingRule::RT_OneRecord);
- rule.SetSearchType(MythRecordingRule::ST_TitleSearch);
- rule.SetSubtitle("");
- rule.SetDescription(searchTitle);
+
+ if (!epgInfo.IsNull())
+ {
+ rule.SetSearchType(MythRecordingRule::ST_TitleSearch);
+ rule.SetChannelID(epgInfo.ChannelID());
+ rule.SetStartTime(epgInfo.StartTime());
+ rule.SetEndTime(epgInfo.EndTime());
+ rule.SetTitle(epgInfo.Title());
+ // Backend use the description to find program by keywords or title
+ rule.SetSubtitle("");
+ rule.SetDescription(epgInfo.Title());
+ rule.SetCategory(epgInfo.Category());
+ rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
+ }
+ else
+ {
+ // Not feasible
+ rule.SetType(MythRecordingRule::RT_NotRecording);
+ }
rule.SetDuplicateControlMethod(MythRecordingRule::DM_CheckSubtitleAndDescription);
rule.SetCheckDuplicatesInType(MythRecordingRule::DI_InAll);
rule.SetInactive(false);
@@ -1128,5 +1250,209 @@ MythRecordingRule MythScheduleHelper1302::NewFromTemplate(MythEPGInfo &epgInfo)
//// types are automatically converted to the suggested alternatives.
////
-// TODO
+RuleMetadata MythScheduleHelper1309::GetMetadata(const MythRecordingRule &rule) const
+{
+ RuleMetadata meta;
+ time_t st = rule.StartTime();
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "";
+ switch (rule.Type())
+ {
+ case MythRecordingRule::RT_DailyRecord:
+ case MythRecordingRule::RT_FindDailyRecord:
+ meta.isRepeating = true;
+ meta.weekDays = 0x7F;
+ meta.marker = "d";
+ break;
+ case MythRecordingRule::RT_WeeklyRecord:
+ case MythRecordingRule::RT_FindWeeklyRecord:
+ meta.isRepeating = true;
+ meta.weekDays = 1 << ((weekday(&st) + 6) % 7);
+ meta.marker = "w";
+ break;
+ case MythRecordingRule::RT_ChannelRecord:
+ meta.isRepeating = true;
+ meta.weekDays = 0x7F;
+ meta.marker = "C";
+ break;
+ case MythRecordingRule::RT_AllRecord:
+ meta.isRepeating = true;
+ if ((rule.Filter() & MythRecordingRule::FM_ThisDayAndTime))
+ {
+ meta.weekDays = 1 << ((weekday(&st) + 6) % 7);
+ meta.marker = "w";
+ }
+ else if ((rule.Filter() & MythRecordingRule::FM_ThisTime))
+ {
+ meta.weekDays = 0x7F;
+ meta.marker = "d";
+ }
+ else
+ {
+ meta.weekDays = 0x7F;
+ meta.marker = "A";
+ }
+ break;
+ case MythRecordingRule::RT_OneRecord:
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "1";
+ break;
+ case MythRecordingRule::RT_DontRecord:
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "x";
+ break;
+ case MythRecordingRule::RT_OverrideRecord:
+ meta.isRepeating = false;
+ meta.weekDays = 0;
+ meta.marker = "o";
+ break;
+ default:
+ break;
+ }
+ return meta;
+}
+
+MythRecordingRule MythScheduleHelper1309::NewDailyRecord(MythEPGInfo &epgInfo)
+{
+ unsigned int filter;
+ MythRecordingRule rule = this->NewFromTemplate(epgInfo);
+ rule.SetType(MythRecordingRule::RT_AllRecord);
+ filter = MythRecordingRule::FM_ThisChannel + MythRecordingRule::FM_ThisTime;
+ rule.SetFilter(filter);
+
+ if (!epgInfo.IsNull())
+ {
+ rule.SetSearchType(MythRecordingRule::ST_NoSearch);
+ rule.SetChannelID(epgInfo.ChannelID());
+ rule.SetStartTime(epgInfo.StartTime());
+ rule.SetEndTime(epgInfo.EndTime());
+ rule.SetTitle(epgInfo.Title());
+ rule.SetSubtitle(epgInfo.Subtitle());
+ rule.SetCategory(epgInfo.Category());
+ rule.SetDescription(epgInfo.Description());
+ rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
+ }
+ else
+ {
+ // No EPG! Create custom daily for this channel
+ rule.SetType(MythRecordingRule::RT_DailyRecord);
+ rule.SetFilter(MythRecordingRule::FM_ThisChannel);
+ // kManualSearch = http://www.gossamer-threads.com/lists/mythtv/dev/155150?search_string=kManualSearch;#155150
+ rule.SetSearchType(MythRecordingRule::ST_ManualSearch);
+ }
+ rule.SetDuplicateControlMethod(MythRecordingRule::DM_CheckSubtitleAndDescription);
+ rule.SetCheckDuplicatesInType(MythRecordingRule::DI_InAll);
+ rule.SetInactive(false);
+ return rule;
+}
+
+MythRecordingRule MythScheduleHelper1309::NewWeeklyRecord(MythEPGInfo &epgInfo)
+{
+ unsigned int filter;
+ MythRecordingRule rule = this->NewFromTemplate(epgInfo);
+
+ rule.SetType(MythRecordingRule::RT_AllRecord);
+ filter = MythRecordingRule::FM_ThisChannel + MythRecordingRule::FM_ThisDayAndTime;
+ rule.SetFilter(filter);
+
+ if (!epgInfo.IsNull())
+ {
+ rule.SetSearchType(MythRecordingRule::ST_NoSearch);
+ rule.SetChannelID(epgInfo.ChannelID());
+ rule.SetStartTime(epgInfo.StartTime());
+ rule.SetEndTime(epgInfo.EndTime());
+ rule.SetTitle(epgInfo.Title());
+ rule.SetSubtitle(epgInfo.Subtitle());
+ rule.SetCategory(epgInfo.Category());
+ rule.SetDescription(epgInfo.Description());
+ rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
+ }
+ else
+ {
+ // No EPG! Create custom weekly for this channel
+ rule.SetType(MythRecordingRule::RT_WeeklyRecord);
+ rule.SetFilter(MythRecordingRule::FM_ThisChannel);
+ // kManualSearch = http://www.gossamer-threads.com/lists/mythtv/dev/155150?search_string=kManualSearch;#155150
+ rule.SetSearchType(MythRecordingRule::ST_ManualSearch);
+ }
+ rule.SetDuplicateControlMethod(MythRecordingRule::DM_CheckSubtitleAndDescription);
+ rule.SetCheckDuplicatesInType(MythRecordingRule::DI_InAll);
+ rule.SetInactive(false);
+ return rule;
+}
+
+MythRecordingRule MythScheduleHelper1309::NewChannelRecord(MythEPGInfo &epgInfo)
+{
+ unsigned int filter;
+ MythRecordingRule rule = this->NewFromTemplate(epgInfo);
+
+ rule.SetType(MythRecordingRule::RT_AllRecord);
+ filter = MythRecordingRule::FM_ThisChannel;
+ rule.SetFilter(filter);
+
+ if (!epgInfo.IsNull())
+ {
+ rule.SetSearchType(MythRecordingRule::ST_NoSearch);
+ rule.SetChannelID(epgInfo.ChannelID());
+ rule.SetStartTime(epgInfo.StartTime());
+ rule.SetEndTime(epgInfo.EndTime());
+ rule.SetTitle(epgInfo.Title());
+ rule.SetSubtitle(epgInfo.Subtitle());
+ rule.SetCategory(epgInfo.Category());
+ rule.SetDescription(epgInfo.Description());
+ rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
+ }
+ else
+ {
+ // Not feasible
+ rule.SetType(MythRecordingRule::RT_NotRecording);
+ }
+ rule.SetDuplicateControlMethod(MythRecordingRule::DM_CheckSubtitleAndDescription);
+ rule.SetCheckDuplicatesInType(MythRecordingRule::DI_InAll);
+ rule.SetInactive(false);
+ return rule;
+}
+
+MythRecordingRule MythScheduleHelper1309::NewOneRecord(MythEPGInfo &epgInfo)
+{
+ unsigned int filter;
+ MythRecordingRule rule = this->NewFromTemplate(epgInfo);
+
+ rule.SetType(MythRecordingRule::RT_OneRecord);
+ filter = MythRecordingRule::FM_ThisEpisode;
+ rule.SetFilter(filter);
+
+ if (!epgInfo.IsNull())
+ {
+ rule.SetSearchType(MythRecordingRule::ST_NoSearch);
+ rule.SetChannelID(epgInfo.ChannelID());
+ rule.SetStartTime(epgInfo.StartTime());
+ rule.SetEndTime(epgInfo.EndTime());
+ rule.SetTitle(epgInfo.Title());
+ rule.SetSubtitle(epgInfo.Subtitle());
+ rule.SetCategory(epgInfo.Category());
+ rule.SetDescription(epgInfo.Description());
+ rule.SetCallsign(epgInfo.Callsign());
+ rule.SetProgramID(epgInfo.ProgramID());
+ rule.SetSeriesID(epgInfo.SeriesID());
+ }
+ else
+ {
+ // Not feasible
+ rule.SetType(MythRecordingRule::RT_NotRecording);
+ }
+ rule.SetDuplicateControlMethod(MythRecordingRule::DM_CheckSubtitleAndDescription);
+ rule.SetCheckDuplicatesInType(MythRecordingRule::DI_InAll);
+ rule.SetInactive(false);
+ return rule;
+}
View
40 addons/pvr.mythtv.cmyth/src/cppmyth/MythScheduleManager.h
@@ -36,6 +36,13 @@ typedef std::vector<MythRecordingRule> OverrideRuleList;
// Schedule element is pair < index of schedule , program info of schedule >
typedef std::vector<std::pair<unsigned int, boost::shared_ptr<MythProgramInfo> > > ScheduleList;
+typedef struct
+{
+ bool isRepeating;
+ int weekDays;
+ const char* marker;
+} RuleMetadata;
+
class MythRecordingRuleNode
{
public:
@@ -97,20 +104,24 @@ class MythScheduleManager
VersionHelper() {}
virtual ~VersionHelper();
virtual bool SameTimeslot(MythRecordingRule &first, MythRecordingRule &second) const = 0;
+ virtual RuleMetadata GetMetadata(const MythRecordingRule &rule) const = 0;
virtual MythRecordingRule NewFromTemplate(MythEPGInfo &epgInfo) = 0;
virtual MythRecordingRule NewSingleRecord(MythEPGInfo &epgInfo) = 0;
virtual MythRecordingRule NewDailyRecord(MythEPGInfo &epgInfo) = 0;
virtual MythRecordingRule NewWeeklyRecord(MythEPGInfo &epgInfo) = 0;
- virtual MythRecordingRule NewChannelRecord(const CStdString &searchTitle) = 0;
- virtual MythRecordingRule NewOneRecord(const CStdString &searchTitle) = 0;
+ virtual MythRecordingRule NewChannelRecord(MythEPGInfo &epgInfo) = 0;
+ virtual MythRecordingRule NewOneRecord(MythEPGInfo &epgInfo) = 0;
};
+ RuleMetadata GetMetadata(const MythRecordingRule &rule) const;
MythRecordingRule NewFromTemplate(MythEPGInfo &epgInfo);
MythRecordingRule NewSingleRecord(MythEPGInfo &epgInfo);
MythRecordingRule NewDailyRecord(MythEPGInfo &epgInfo);
MythRecordingRule NewWeeklyRecord(MythEPGInfo &epgInfo);
- MythRecordingRule NewChannelRecord(const CStdString &searchTitle);
- MythRecordingRule NewOneRecord(const CStdString &searchTitle);
+ MythRecordingRule NewChannelRecord(MythEPGInfo &epgInfo);
+ MythRecordingRule NewOneRecord(MythEPGInfo &epgInfo);
+
+ bool ToggleShowNotRecording();
private:
mutable PLATFORM::CMutex m_lock;
@@ -136,6 +147,8 @@ class MythScheduleManager
NodeById m_rulesById;
RecordingList m_recordings;
RecordingIndexByRuleId m_recordingIndexByRuleId;
+
+ bool m_showNotRecording;
};
@@ -152,12 +165,13 @@ inline MythScheduleManager::VersionHelper::~VersionHelper() {
class MythScheduleHelperNoHelper : public MythScheduleManager::VersionHelper {
public:
virtual bool SameTimeslot(MythRecordingRule &first, MythRecordingRule &second) const;
+ virtual RuleMetadata GetMetadata(const MythRecordingRule &rule) const;
virtual MythRecordingRule NewFromTemplate(MythEPGInfo &epgInfo);
virtual MythRecordingRule NewSingleRecord(MythEPGInfo &epgInfo);
virtual MythRecordingRule NewDailyRecord(MythEPGInfo &epgInfo);
virtual MythRecordingRule NewWeeklyRecord(MythEPGInfo &epgInfo);
- virtual MythRecordingRule NewChannelRecord(const CStdString &searchTitle);
- virtual MythRecordingRule NewOneRecord(const CStdString &searchTitle);
+ virtual MythRecordingRule NewChannelRecord(MythEPGInfo &epgInfo);
+ virtual MythRecordingRule NewOneRecord(MythEPGInfo &epgInfo);
};
// Base 0.24
@@ -168,12 +182,13 @@ class MythScheduleHelper1226 : public MythScheduleHelperNoHelper {
MythScheduleHelper1226(MythDatabase &db) : m_db(db) {
}
virtual bool SameTimeslot(MythRecordingRule &first, MythRecordingRule &second) const;
+ virtual RuleMetadata GetMetadata(const MythRecordingRule &rule) const;
virtual MythRecordingRule NewFromTemplate(MythEPGInfo &epgInfo);
virtual MythRecordingRule NewSingleRecord(MythEPGInfo &epgInfo);
virtual MythRecordingRule NewDailyRecord(MythEPGInfo &epgInfo);
virtual MythRecordingRule NewWeeklyRecord(MythEPGInfo &epgInfo);
- virtual MythRecordingRule NewChannelRecord(const CStdString &searchTitle);
- virtual MythRecordingRule NewOneRecord(const CStdString &searchTitle);
+ virtual MythRecordingRule NewChannelRecord(MythEPGInfo &epgInfo);
+ virtual MythRecordingRule NewOneRecord(MythEPGInfo &epgInfo);
protected:
MythDatabase m_db;
};
@@ -206,8 +221,9 @@ class MythScheduleHelper1309 : public MythScheduleHelper1302 {
MythScheduleHelper1309(MythDatabase &db) : MythScheduleHelper1302(db) {
}
- //virtual bool SameTimeslot(MythRecordingRule &first, MythRecordingRule &second) const;
- //virtual MythRecordingRule NewSingleRecord() const;
- //virtual MythRecordingRule NewDailyRecord() const;
- //virtual MythRecordingRule NewWeeklyRecord() const;
+ virtual RuleMetadata GetMetadata(const MythRecordingRule &rule) const;
+ virtual MythRecordingRule NewDailyRecord(MythEPGInfo &epgInfo);
+ virtual MythRecordingRule NewWeeklyRecord(MythEPGInfo &epgInfo);
+ virtual MythRecordingRule NewChannelRecord(MythEPGInfo &epgInfo);
+ virtual MythRecordingRule NewOneRecord(MythEPGInfo &epgInfo);
};
View
584 addons/pvr.mythtv.cmyth/src/demux.cpp
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "platform/os.h"
+
+#include "libXBMC_pvr.h"
+#include "xbmc_codec_types.h"
+
+#include "demux.h"
+#include "client.h"
+
+extern "C" {
+#include <cmyth/cmyth.h>
+};
+
+#define LOGTAG "[DEMUX] "
+#define POSMAP_PTS_INTERVAL (PTS_TIME_BASE * 2) // 2 secs
+
+using namespace PLATFORM;
+using namespace ADDON;
+
+void DemuxLog(int level, char *msg)
+{
+ if (msg && level != DEMUX_DBG_NONE)
+ {
+ bool doLog = g_bExtraDebug;
+ addon_log_t loglevel = LOG_DEBUG;
+ switch (level)
+ {
+ case DEMUX_DBG_ERROR:
+ loglevel = LOG_ERROR;
+ doLog = true;
+ break;
+ case DEMUX_DBG_WARN:
+ case DEMUX_DBG_INFO:
+ loglevel = LOG_INFO;
+ break;
+ case DEMUX_DBG_DEBUG:
+ case DEMUX_DBG_PARSE:
+ case DEMUX_DBG_ALL:
+ loglevel = LOG_DEBUG;
+ break;
+ }
+ if (XBMC && doLog)
+ XBMC->Log(loglevel, LOGTAG"%s", msg);
+ }
+}
+
+Demux::Demux(MythRecorder &recorder)
+ : CThread()
+ , m_recorder(recorder)
+ , m_channel(1)
+ , m_av_buf_size(AV_BUFFER_SIZE)
+ , m_av_pos(0)
+ , m_av_buf(NULL)
+ , m_av_rbs(NULL)
+ , m_av_rbe(NULL)
+ , m_AVContext(NULL)
+ , m_mainStreamPID(0xffff)
+ , m_DTS(PTS_UNSET)
+ , m_PTS(PTS_UNSET)
+ , m_pinTime(0)
+ , m_curTime(0)
+ , m_endTime(0)
+ , m_isChangePlaced(false)
+{
+ m_av_buf = (unsigned char*)malloc(sizeof(*m_av_buf) * (m_av_buf_size + 1));
+ if (m_av_buf)
+ {
+ m_av_rbs = m_av_buf;
+ m_av_rbe = m_av_buf;
+
+ if (g_bExtraDebug)
+ demux_dbg_level(DEMUX_DBG_DEBUG);
+ else
+ demux_dbg_level(DEMUX_DBG_ERROR);
+ demux_set_dbg_msgcallback(DemuxLog);
+
+ m_AVContext = new AVContext(this, m_av_pos, m_channel);
+
+ CreateThread(true);
+ }
+ else
+ {
+ XBMC->Log(LOG_ERROR, LOGTAG"alloc AV buffer failed");
+ }
+}
+
+Demux::~Demux()
+{
+ StopThread(0);
+ Flush();
+
+ // Free AV context
+ if (m_AVContext)
+ SAFE_DELETE(m_AVContext);
+ // Free AV buffer
+ if (m_av_buf)
+ {
+ if (g_bExtraDebug)
+ XBMC->Log(LOG_DEBUG, LOGTAG"free AV buffer: allocated size was %zu", m_av_buf_size);
+ free(m_av_buf);
+ m_av_buf = NULL;
+ }
+}
+
+/*
+ * Implement our AV reader
+ */
+const unsigned char* Demux::ReadAV(uint64_t pos, size_t n)
+{
+ // out of range
+ if (n > m_av_buf_size)
+ return NULL;
+
+ // Already read ?
+ size_t sz = m_av_rbe - m_av_buf;
+ if (pos < m_av_pos || pos > (m_av_pos + sz))
+ {
+ // seek and reset buffer
+ int64_t newpos = m_recorder.LiveTVSeek((int64_t)pos, WHENCE_SET);
+ if (newpos < 0)
+ return NULL;
+ m_av_pos = (uint64_t)newpos;
+ m_av_rbs = m_av_rbe = m_av_buf;
+ }
+ else
+ {
+ // move to the desired pos in buffer
+ m_av_rbs = m_av_buf + (size_t)(pos - m_av_pos);
+ }
+
+ size_t dataread = m_av_rbe - m_av_rbs;
+ if (dataread >= n)
+ return m_av_rbs;
+
+ memmove(m_av_buf, m_av_rbs, dataread);
+ m_av_rbs = m_av_buf;
+ m_av_rbe = m_av_rbs + dataread;
+ m_av_pos = pos;
+ unsigned int len = (unsigned int)(m_av_buf_size - dataread);
+ int wait = 5000;
+ while (wait > 0 && !IsStopped() )
+ {
+ int ret = m_recorder.ReadLiveTV(m_av_rbe, len);
+ if (ret > 0)
+ {
+ m_av_rbe += ret;
+ dataread += ret;
+ len -= ret;
+ }
+ if (dataread >= n || ret < 0)
+ break;
+ wait -= 1000;
+ usleep(100000);
+ }
+ return dataread >= n ? m_av_rbs : NULL;
+}
+
+void* Demux::Process()
+{
+ if (!m_AVContext)
+ {
+ XBMC->Log(LOG_ERROR, LOGTAG"%s: no AVContext", __FUNCTION__);
+ return NULL;
+ }
+
+ int ret = 0;
+
+ while (!IsStopped())
+ {
+ {
+ CLockObject lock(m_mutex);
+ ret = m_AVContext->TSResync();
+ }
+ if (ret != AVCONTEXT_CONTINUE)
+ break;
+
+ ret = m_AVContext->ProcessTSPacket();
+
+ if (m_AVContext->HasPIDStreamData())
+ {
+ ElementaryStream::STREAM_PKT pkt;
+ while (get_stream_data(&pkt))
+ {
+ if (pkt.streamChange && update_pvr_stream(pkt.pid))
+ push_stream_change();
+ DemuxPacket* dxp = stream_pvr_data(&pkt);
+ if (dxp)
+ push_stream_data(dxp);
+ }
+ }
+ if (m_AVContext->HasPIDPayload())
+ {
+ ret = m_AVContext->ProcessTSPayload();
+ if (ret == AVCONTEXT_PROGRAM_CHANGE)
+ {
+ populate_pvr_streams();
+ push_stream_change();
+ }
+ }
+
+ if (ret < 0)
+ XBMC->Log(LOG_NOTICE, LOGTAG"%s: error %d", __FUNCTION__, ret);
+
+ if (ret == AVCONTEXT_TS_ERROR)
+ m_AVContext->Shift();
+ else
+ m_AVContext->GoNext();
+ }
+
+ XBMC->Log(LOG_DEBUG, LOGTAG"%s: stopped with status %d", __FUNCTION__, ret);
+ return NULL;
+}
+
+bool Demux::GetStreamProperties(PVR_STREAM_PROPERTIES* props)
+{
+ int wait = 0;
+ // Wait until setup is completed for all streams
+ while (IsRunning() && !m_nosetup.empty() && wait < 20)
+ {
+ if (g_bExtraDebug)
+ XBMC->Log(LOG_DEBUG, LOGTAG"%s: waiting until setup will be completed ...", __FUNCTION__);
+ usleep(100000);
+ wait++;
+ }
+ if (!m_nosetup.empty())
+ XBMC->Log(LOG_ERROR, LOGTAG"%s: incomplete setup", __FUNCTION__);
+
+ CLockObject lock(m_mutex);
+ m_isChangePlaced = false;
+ return m_streams.GetProperties(props);
+}
+
+void Demux::Flush(void)
+{
+ CLockObject lock(m_mutex);
+ DemuxPacket* pkt(NULL);
+ while (m_demuxPacketBuffer.Pop(pkt))
+ PVR->FreeDemuxPacket(pkt);
+}
+
+void Demux::Abort()
+{
+ StopThread(0);
+}
+
+DemuxPacket* Demux::Read()
+{
+ DemuxPacket* packet(NULL);
+ if (m_demuxPacketBuffer.Pop(packet, 100))
+ return packet;
+ return PVR->AllocateDemuxPacket(0);
+}
+
+bool Demux::SeekTime(int time, bool backwards, double* startpts)
+{
+ // Current PTS must be valid to estimate offset
+ if (m_PTS == PTS_UNSET)
+ return false;
+ // time is in MSEC not PTS_TIME_BASE. Rescale time to PTS (90Khz)
+ uint64_t pts = (uint64_t)time * PTS_TIME_BASE / 1000;
+ // Compute offset from current PTS
+ int64_t offset = (int64_t)(pts - m_PTS);
+ // Limit offset to deal with invalid request or PTS discontinuity
+ // Backwards : Limiting offset to +6 secs
+ // Forwards : Limiting offset to -6 secs
+ if (backwards)
+ offset = std::min(offset, (int64_t)(PTS_TIME_BASE * 6));
+ else
+ offset = std::max(offset, (int64_t)(PTS_TIME_BASE * (-6)));
+ // Compute desired time position
+ int64_t desired = m_curTime + offset;
+
+ CLockObject lock(m_mutex);
+
+ std::map<int64_t, AV_POSMAP_ITEM>::const_iterator it;
+ if (offset < 0)
+ {
+ it = m_posmap.upper_bound(desired);
+ if (backwards && it != m_posmap.begin())
+ --it;
+ }
+ else
+ {
+ it = m_posmap.upper_bound(desired);
+ // On end shift back if possible
+ if (it == m_posmap.end() && it != m_posmap.begin())
+ --it;
+ }
+
+ if (g_bExtraDebug)
+ XBMC->Log(LOG_DEBUG, LOGTAG"%s: bw:%d tm:%d tm_pts:%"PRIu64" c_pts:%"PRIu64" offset:%+6.3f c_tm:%+6.3f n_tm:%+6.3f", __FUNCTION__,
+ backwards, time, pts, m_PTS, (double)offset / PTS_TIME_BASE, (double)m_curTime / PTS_TIME_BASE, (double)desired / PTS_TIME_BASE);
+
+ if (it != m_posmap.end())
+ {
+ int64_t new_time = it->first;
+ uint64_t new_pos = it->second.av_pos;
+ uint64_t new_pts = it->second.av_pts;
+ XBMC->Log(LOG_DEBUG, LOGTAG"seek to %"PRId64" pts=%"PRIu64, new_time, new_pts);
+
+ Flush();
+ m_AVContext->GoPosition(new_pos);
+ m_AVContext->ResetPackets();
+ m_curTime = m_pinTime = new_time;
+ m_DTS = m_PTS = new_pts;
+ }
+
+ *startpts = (double)m_PTS * DVD_TIME_BASE / PTS_TIME_BASE;
+
+ return true;
+}
+
+int Demux::GetPlayingTime()
+{
+ double time_ms = (double)m_curTime * 1000 / PTS_TIME_BASE;
+ if (time_ms > INT_MAX)
+ return INT_MAX;
+ return (int)time_ms;
+}
+
+bool Demux::get_stream_data(ElementaryStream::STREAM_PKT* pkt)
+{
+ ElementaryStream* es = m_AVContext->GetPIDStream();
+ if (!es)
+ return false;
+
+ if (!es->GetStreamPacket(pkt))
+ return false;
+
+ if (pkt->duration > 180000)
+ {
+ pkt->duration = 0;
+ }
+ else if (pkt->pid == m_mainStreamPID)
+ {
+ // Fill duration map for main stream
+ m_curTime += pkt->duration;
+ if (m_curTime >= m_pinTime)
+ {
+ m_pinTime += POSMAP_PTS_INTERVAL;
+ if (m_curTime > m_endTime)
+ {
+ AV_POSMAP_ITEM item;
+ item.av_pts = pkt->pts;
+ item.av_pos = m_AVContext->GetPosition();
+ m_posmap.insert(std::make_pair(m_curTime, item));
+ m_endTime = m_curTime;
+ }
+ }
+ // Sync main DTS & PTS
+ m_DTS = pkt->dts;
+ m_PTS = pkt->pts;
+ }
+ return true;
+}
+
+void Demux::reset_posmap()
+{
+ if (m_posmap.empty())
+ return;
+
+ {
+ CLockObject lock(m_mutex);
+ m_posmap.clear();
+ m_pinTime = m_curTime = m_endTime = 0;
+ }
+}
+
+static inline int stream_identifier(int composition_id, int ancillary_id)
+{
+ return ((composition_id & 0xff00) >> 8)
+ | ((composition_id & 0xff) << 8)
+ | ((ancillary_id & 0xff00) << 16)
+ | ((ancillary_id & 0xff) << 24);
+}
+
+static void recode_language(const char* muxLanguage, char* strLanguage)
+{
+ /*
+ * While XBMC does'nt support them.
+ * Fix unsupported language codes (EN 300 468 Annex F & J)
+ * 'qaa' : Original audio
+ * 'qad','NAR' : Audio Description
+ */
+ if (strncmp(muxLanguage, "qaa", 3) == 0 ||
+ strncmp(muxLanguage, "qad", 3) == 0 ||
+ strncmp(muxLanguage, "NAR", 3) == 0)
+ {
+ strLanguage[0] = 0;
+ strLanguage[1] = 0;
+ strLanguage[2] = 0;
+ strLanguage[3] = 0;
+ }
+ else
+ {
+ strLanguage[0] = muxLanguage[0];
+ strLanguage[1] = muxLanguage[1];
+ strLanguage[2] = muxLanguage[2];
+ strLanguage[3] = 0;
+ }
+}
+
+void Demux::populate_pvr_streams()
+{
+ CLockObject Lock(m_mutex);
+
+ uint16_t mainPid = 0xffff;
+ int mainType = XBMC_CODEC_TYPE_UNKNOWN;
+ std::vector<XbmcPvrStream> new_streams;
+ const std::vector<ElementaryStream*> es_streams = m_AVContext->GetStreams();
+ for (std::vector<ElementaryStream*>::const_iterator it = es_streams.begin(); it != es_streams.end(); it++)
+ {
+ const char* codec_name = (*it)->GetStreamCodecName();
+ xbmc_codec_t codec = CODEC->GetCodecByName(codec_name);
+ if (codec.codec_type != XBMC_CODEC_TYPE_UNKNOWN)
+ {
+ // Find the main stream:
+ // The best candidate would be the first video. Else the first audio
+ switch (mainType)
+ {
+ case XBMC_CODEC_TYPE_VIDEO:
+ break;
+ case XBMC_CODEC_TYPE_AUDIO:
+ if (codec.codec_type != XBMC_CODEC_TYPE_VIDEO)
+ break;
+ default:
+ mainPid = (*it)->pid;
+ mainType = codec.codec_type;
+ }
+
+ XbmcPvrStream new_stream;
+ m_streams.GetStreamData((*it)->pid, &new_stream);
+
+ new_stream.iCodecId = codec.codec_id;
+ new_stream.iCodecType = codec.codec_type;
+ recode_language((*it)->stream_info.language, new_stream.strLanguage);
+ new_stream.iIdentifier = stream_identifier((*it)->stream_info.composition_id, (*it)->stream_info.ancillary_id);
+ new_stream.iFPSScale = (*it)->stream_info.fps_scale;
+ new_stream.iFPSRate = (*it)->stream_info.fps_rate;
+ new_stream.iHeight = (*it)->stream_info.height;
+ new_stream.iWidth = (*it)->stream_info.width;
+ new_stream.fAspect = (*it)->stream_info.aspect;
+ new_stream.iChannels = (*it)->stream_info.channels;
+ new_stream.iSampleRate = (*it)->stream_info.sample_rate;
+ new_stream.iBlockAlign = (*it)->stream_info.block_align;
+ new_stream.iBitRate = (*it)->stream_info.bit_rate;
+ new_stream.iBitsPerSample = (*it)->stream_info.bits_Per_sample;
+
+ new_streams.push_back(new_stream);
+ m_AVContext->StartStreaming((*it)->pid);
+
+ // Add stream to no setup set
+ if (!(*it)->has_stream_info)
+ m_nosetup.insert((*it)->pid);
+
+ if (g_bExtraDebug)
+ XBMC->Log(LOG_DEBUG, LOGTAG"%s: register PES %.4x %s", __FUNCTION__, (*it)->pid, codec_name);
+ }
+ }
+ m_streams.UpdateStreams(new_streams);
+ // Renew main stream
+ m_mainStreamPID = mainPid;
+}
+
+bool Demux::update_pvr_stream(uint16_t pid)
+{
+ ElementaryStream* es = m_AVContext->GetStream(pid);
+ if (!es)
+ return false;
+
+ if (g_bExtraDebug)
+ XBMC->Log(LOG_DEBUG, LOGTAG"%s: update info PES %.4x %s", __FUNCTION__, es->pid, es->GetStreamCodecName());
+
+ CLockObject Lock(m_mutex);
+
+ XbmcPvrStream* stream = m_streams.GetStreamById(es->pid);
+ if (stream)
+ {
+ recode_language(es->stream_info.language, stream->strLanguage);
+ stream->iIdentifier = stream_identifier(es->stream_info.composition_id, es->stream_info.ancillary_id);
+ stream->iFPSScale = es->stream_info.fps_scale;
+ stream->iFPSRate = es->stream_info.fps_rate;
+ stream->iHeight = es->stream_info.height;
+ stream->iWidth = es->stream_info.width;
+ stream->fAspect = es->stream_info.aspect;
+ stream->iChannels = es->stream_info.channels;
+ stream->iSampleRate = es->stream_info.sample_rate;
+ stream->iBlockAlign = es->stream_info.block_align;
+ stream->iBitRate = es->stream_info.bit_rate;
+ stream->iBitsPerSample = es->stream_info.bits_Per_sample;
+
+ if (es->has_stream_info)
+ {
+ // Now stream is setup. Remove it from no setup set
+ std::set<uint16_t>::iterator it = m_nosetup.find(es->pid);
+ if (it != m_nosetup.end())
+ {
+ m_nosetup.erase(it);
+ if (m_nosetup.empty())
+ XBMC->Log(LOG_DEBUG, LOGTAG"%s: setup is completed", __FUNCTION__);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+void Demux::push_stream_change()
+{
+ if (!m_isChangePlaced)
+ {
+ bool ret = false;
+ DemuxPacket* dxp = PVR->AllocateDemuxPacket(0);
+ dxp->iStreamId = DMX_SPECIALID_STREAMCHANGE;
+
+ while (!IsStopped() && !(ret = m_demuxPacketBuffer.Push(dxp)))
+ usleep(100000);
+ if (!ret)
+ PVR->FreeDemuxPacket(dxp);
+ else
+ {
+ m_isChangePlaced = true;
+ XBMC->Log(LOG_DEBUG, LOGTAG"%s: done", __FUNCTION__);
+ }
+ }
+}
+
+DemuxPacket* Demux::stream_pvr_data(ElementaryStream::STREAM_PKT* pkt)
+{
+ if (!pkt)
+ return NULL;
+
+ DemuxPacket* dxp = PVR->AllocateDemuxPacket(pkt->size);
+ if (dxp)
+ {
+ if (pkt->size > 0 && pkt->data)
+ memcpy(dxp->pData, pkt->data, pkt->size);
+
+ dxp->iSize = pkt->size;
+ dxp->duration = (double)pkt->duration * DVD_TIME_BASE / PTS_TIME_BASE;
+ if (pkt->dts != PTS_UNSET)
+ dxp->dts = (double)pkt->dts * DVD_TIME_BASE / PTS_TIME_BASE;
+ else
+ dxp->dts = DVD_NOPTS_VALUE;
+ if (pkt->pts != PTS_UNSET)
+ dxp->pts = (double)pkt->pts * DVD_TIME_BASE / PTS_TIME_BASE;
+ else
+ dxp->pts = DVD_NOPTS_VALUE;
+
+ dxp->iStreamId = m_streams.GetStreamId((unsigned int)pkt->pid);
+ }
+ return dxp;
+}
+
+void Demux::push_stream_data(DemuxPacket* dxp)
+{
+ if (dxp)
+ {
+ bool ret = false;
+ while (!IsStopped() && !(ret = m_demuxPacketBuffer.Push(dxp)))
+ usleep(100000);
+ if (!ret)
+ PVR->FreeDemuxPacket(dxp);
+ }
+}
View
95 addons/pvr.mythtv.cmyth/src/demux.h
@@ -0,0 +1,95 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "cppmyth/MythRecorder.h"
+#include "demuxer/tsDemuxer.h"
+#include "client.h"
+
+#include <platform/threads/threads.h>
+#include <platform/threads/mutex.h>
+#include <platform/util/buffer.h>
+#include <xbmc_stream_utils.hpp>
+
+#include <map>
+#include <set>
+
+#define AV_BUFFER_SIZE 131072
+
+class Demux : public TSDemuxer, PLATFORM::CThread
+{
+public:
+ Demux(MythRecorder &recorder);
+ ~Demux();
+
+ const unsigned char* ReadAV(uint64_t pos, size_t n);
+
+ void* Process();
+
+ bool GetStreamProperties(PVR_STREAM_PROPERTIES* props);
+ void Flush();
+ void Abort();
+ DemuxPacket* Read();
+ bool SeekTime(int time, bool backwards, double* startpts);
+
+ int GetPlayingTime();
+
+private:
+ MythRecorder m_recorder;
+ uint16_t m_channel;
+ PLATFORM::SyncedBuffer<DemuxPacket*> m_demuxPacketBuffer;
+ PLATFORM::CMutex m_mutex;
+ ADDON::XbmcStreamProperties m_streams;
+
+ bool get_stream_data(ElementaryStream::STREAM_PKT* pkt);
+ void reset_posmap();
+
+ // PVR interfaces
+ void populate_pvr_streams();
+ bool update_pvr_stream(uint16_t pid);
+ void push_stream_change();
+ DemuxPacket* stream_pvr_data(ElementaryStream::STREAM_PKT* pkt);
+ void push_stream_data(DemuxPacket* dxp);
+
+ // AV raw buffer
+ size_t m_av_buf_size; ///< size of av buffer
+ uint64_t m_av_pos; ///< absolute position in av
+ unsigned char* m_av_buf; ///< buffer
+ unsigned char* m_av_rbs; ///< raw data start in buffer
+ unsigned char* m_av_rbe; ///< raw data end in buffer
+
+ // Playback context
+ AVContext* m_AVContext;
+ uint16_t m_mainStreamPID; ///< PID of main stream
+ uint64_t m_DTS; ///< absolute decode time of main stream
+ uint64_t m_PTS; ///< absolute presentation time of main stream
+ int64_t m_pinTime; ///< pinned relative position (90Khz)
+ int64_t m_curTime; ///< current relative position (90Khz)
+ int64_t m_endTime; ///< last relative marked position (90Khz))
+ typedef struct
+ {
+ uint64_t av_pts;
+ uint64_t av_pos;
+ } AV_POSMAP_ITEM;
+ std::map<int64_t, AV_POSMAP_ITEM> m_posmap;
+
+ bool m_isChangePlaced;
+ std::set<uint16_t> m_nosetup;
+};
View
253 addons/pvr.mythtv.cmyth/src/demuxer/ES_AAC.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+
+#include "ES_AAC.h"
+#include "bitstream.h"
+
+static int aac_sample_rates[16] =
+{
+ 96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000, 7350
+};
+
+
+ES_AAC::ES_AAC(uint16_t pes_pid)
+ : ElementaryStream(pes_pid)
+{
+ m_Configured = false;
+ m_FrameLengthType = 0;
+ m_PTS = 0;
+ m_DTS = 0;
+ m_FrameSize = 0;
+ m_SampleRate = 0;
+ m_Channels = 0;
+ m_BitRate = 0;
+ m_AudioMuxVersion_A = 0;
+ es_alloc_init = 1920*2;
+ Reset();
+}
+
+ES_AAC::~ES_AAC()
+{
+}
+
+void ES_AAC::Parse(STREAM_PKT* pkt)
+{
+ int p = es_parsed;
+ int l;
+ while ((l = es_len - p) > 8)
+ {
+ if (FindHeaders(es_buf + p, l) < 0)
+ break;
+ p++;
+ }
+ es_parsed = p;
+
+ if (es_found_frame && l >= m_FrameSize)
+ {
+ bool streamChange = SetAudioInformation(m_Channels, m_SampleRate, m_BitRate, 0, 0);
+ pkt->pid = pid;
+ pkt->data = &es_buf[p];
+ pkt->size = m_FrameSize;
+ pkt->duration = 1024 * 90000 / m_SampleRate;
+ pkt->dts = m_DTS;
+ pkt->pts = m_PTS;
+ pkt->streamChange = streamChange;
+
+ es_consumed = p + m_FrameSize;
+ es_parsed = es_consumed;
+ es_found_frame = false;
+ }
+}
+
+int ES_AAC::FindHeaders(uint8_t *buf, int buf_size)
+{
+ if (es_found_frame)
+ return -1;
+
+ uint8_t *buf_ptr = buf;
+
+ // STREAM_TYPE_AUDIO_AACLATM
+ if ((buf_ptr[0] == 0x56 && (buf_ptr[1] & 0xE0) == 0xE0))
+ {
+ // TODO
+ if (buf_size < 16)
+ return -1;
+
+ cBitstream bs(buf_ptr, 16 * 8);
+ bs.skipBits(11);
+ m_FrameSize = bs.readBits(13) + 3;
+ if (!ParseLATMAudioMuxElement(&bs))
+ return 0;
+
+ es_found_frame = true;
+ m_DTS = c_pts;
+ m_PTS = c_pts;
+ c_pts += 90000 * 1024 / m_SampleRate;
+ return -1;
+ }
+ //STREAM_TYPE_AUDIO_AACADTS
+ else if(buf_ptr[0] == 0xFF && (buf_ptr[1] & 0xF0) == 0xF0)
+ {
+ // need at least 7 bytes for header
+ if (buf_size < 7)
+ return -1;
+
+ cBitstream bs(buf_ptr, 9 * 8);
+ bs.skipBits(15);
+
+ // check if CRC is present, means header is 9 byte long
+ int noCrc = bs.readBits(1);
+ if (!noCrc && (buf_size < 9))
+ return -1;
+
+ bs.skipBits(2); // profile
+ int SampleRateIndex = bs.readBits(4);
+ bs.skipBits(1); // private
+ m_Channels = bs.readBits(3);
+ bs.skipBits(4);
+
+ m_FrameSize = bs.readBits(13);
+ m_SampleRate = aac_sample_rates[SampleRateIndex & 0x0E];
+
+ es_found_frame = true;
+ m_DTS = c_pts;
+ m_PTS = c_pts;
+ c_pts += 90000 * 1024 / m_SampleRate;
+ return -1;
+ }
+ return 0;
+}
+
+bool ES_AAC::ParseLATMAudioMuxElement(cBitstream *bs)
+{