diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/CMakeLists.txt kodi-pvr-mediaportal-tvserver-2.4.16/CMakeLists.txt --- kodi-pvr-mediaportal-tvserver-1.13.7/CMakeLists.txt 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/CMakeLists.txt 2017-01-11 10:28:16.000000000 +0000 @@ -6,9 +6,9 @@ enable_language(CXX) -find_package(kodi REQUIRED) +find_package(Kodi REQUIRED) find_package(kodiplatform REQUIRED) -find_package(platform REQUIRED) +find_package(p8-platform REQUIRED) find_package(TinyXML REQUIRED) # Grab the addon version info from the addon.xml file @@ -19,14 +19,26 @@ # Generate an addon_version.h header with the version information configure_file(src/addon_version.h.in addon_version.h) +set(LIVE555_INCLUDE_DIR + src/lib/live555/liveMedia/include + src/lib/live555/BasicUsageEnvironment/include + src/lib/live555/UsageEnvironment/include + src/lib/live555/groupsock/include + src/lib/live555/ +) + +set(LIVE555_DEFINES -DLIVE555 -DSOCKLEN_T=socklen_t -DBSD=1) + include_directories(${kodiplatform_INCLUDE_DIRS} - ${platform_INCLUDE_DIRS} + ${p8-platform_INCLUDE_DIRS} ${TINYXML_INCLUDE_DIR} ${KODI_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/src - ${PROJECT_BINARY_DIR}) + ${PROJECT_BINARY_DIR} + ${LIVE555_INCLUDE_DIR} +) -add_definitions(-D__STDC_FORMAT_MACROS) +add_definitions(-D__STDC_FORMAT_MACROS ${LIVE555_DEFINES}) if (NOT WIN32) add_options(ALL_LANGUAGES ALL_BUILDS "-fPIC") @@ -92,7 +104,10 @@ src/lib/tsreader/DeMultiplexer.cpp src/lib/tsreader/DvbUtil.cpp src/lib/tsreader/FileReader.cpp + src/lib/tsreader/MemoryBuffer.cpp src/lib/tsreader/MemoryReader.cpp + src/lib/tsreader/MemorySink.cpp + src/lib/tsreader/MepoRTSPClient.cpp src/lib/tsreader/MultiFileReader.cpp src/lib/tsreader/PacketSync.cpp src/lib/tsreader/PatParser.cpp @@ -101,7 +116,8 @@ src/lib/tsreader/Section.cpp src/lib/tsreader/SectionDecoder.cpp src/lib/tsreader/TSHeader.cpp - src/lib/tsreader/TSReader.cpp) + src/lib/tsreader/TSReader.cpp +) source_group("Source Files\\lib\\tsreader" FILES ${TSREADER_SOURCES}) # TSReader sources @@ -109,7 +125,10 @@ src/lib/tsreader/DeMultiplexer.h src/lib/tsreader/DvbUtil.h src/lib/tsreader/FileReader.h + src/lib/tsreader/MemoryBuffer.h src/lib/tsreader/MemoryReader.h + src/lib/tsreader/MemorySink.h + src/lib/tsreader/MepoRTSPClient.h src/lib/tsreader/MultiFileReader.h src/lib/tsreader/PacketSync.h src/lib/tsreader/PatParser.h @@ -122,10 +141,104 @@ src/lib/tsreader/TSReader.h) source_group("Header Files\\lib\\tsreader" FILES ${TSREADER_HEADERS}) +#Live555 subset sources +SET(LIVE555_SOURCES + src/lib/live555/BasicUsageEnvironment/BasicHashTable.cpp + src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler.cpp + src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler0.cpp + src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment.cpp + src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment0.cpp + src/lib/live555/BasicUsageEnvironment/DelayQueue.cpp + src/lib/live555/UsageEnvironment/HashTable.cpp + src/lib/live555/UsageEnvironment/UsageEnvironment.cpp + src/lib/live555/UsageEnvironment/strDup.cpp + src/lib/live555/groupsock/GroupEId.cpp + src/lib/live555/groupsock/Groupsock.cpp + src/lib/live555/groupsock/GroupsockHelper.cpp + src/lib/live555/groupsock/IOHandlers.cpp + src/lib/live555/groupsock/NetAddress.cpp + src/lib/live555/groupsock/NetInterface.cpp + src/lib/live555/groupsock/inet.c + src/lib/live555/liveMedia/Base64.cpp + src/lib/live555/liveMedia/BasicUDPSource.cpp + src/lib/live555/liveMedia/DigestAuthentication.cpp + src/lib/live555/liveMedia/FramedFilter.cpp + src/lib/live555/liveMedia/FramedSource.cpp + src/lib/live555/liveMedia/Locale.cpp + src/lib/live555/liveMedia/MPEG2TransportStreamFramer.cpp + src/lib/live555/liveMedia/Media.cpp + src/lib/live555/liveMedia/MediaSession.cpp + src/lib/live555/liveMedia/MediaSink.cpp + src/lib/live555/liveMedia/MediaSource.cpp + src/lib/live555/liveMedia/MultiFramedRTPSink.cpp + src/lib/live555/liveMedia/MultiFramedRTPSource.cpp + src/lib/live555/liveMedia/RTCP.cpp + src/lib/live555/liveMedia/RTPInterface.cpp + src/lib/live555/liveMedia/RTPSink.cpp + src/lib/live555/liveMedia/RTPSource.cpp + src/lib/live555/liveMedia/RTSPClient.cpp + src/lib/live555/liveMedia/RTSPCommon.cpp + src/lib/live555/liveMedia/SimpleRTPSink.cpp + src/lib/live555/liveMedia/SimpleRTPSource.cpp + src/lib/live555/liveMedia/our_md5.c + src/lib/live555/liveMedia/our_md5hl.c + src/lib/live555/liveMedia/rtcp_from_spec.c +) +source_group("Source Files\\lib\\live555" FILES ${LIVE555_SOURCES}) + +SET(LIVE555_HEADERS + src/lib/live555/groupsock/include/NetCommon.h + src/lib/live555/liveMedia/our_md5.h + src/lib/live555/liveMedia/rtcp_from_spec.h + src/lib/live555/BasicUsageEnvironment/include/BasicHashTable.hh + src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment.hh + src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh + src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh + src/lib/live555/BasicUsageEnvironment/include/DelayQueue.hh + src/lib/live555/BasicUsageEnvironment/include/HandlerSet.hh + src/lib/live555/groupsock/include/GroupEId.hh + src/lib/live555/groupsock/include/Groupsock.hh + src/lib/live555/groupsock/include/GroupsockHelper.hh + src/lib/live555/groupsock/include/groupsock_version.hh + src/lib/live555/groupsock/include/IOHandlers.hh + src/lib/live555/groupsock/include/NetAddress.hh + src/lib/live555/groupsock/include/NetInterface.hh + src/lib/live555/groupsock/include/TunnelEncaps.hh + src/lib/live555/liveMedia/include/Base64.hh + src/lib/live555/liveMedia/include/BasicUDPSource.hh + src/lib/live555/liveMedia/include/DigestAuthentication.hh + src/lib/live555/liveMedia/include/FramedFilter.hh + src/lib/live555/liveMedia/include/FramedSource.hh + src/lib/live555/liveMedia/include/liveMedia.hh + src/lib/live555/liveMedia/include/liveMedia_version.hh + src/lib/live555/liveMedia/include/Locale.hh + src/lib/live555/liveMedia/include/Media.hh + src/lib/live555/liveMedia/include/MediaSession.hh + src/lib/live555/liveMedia/include/MediaSink.hh + src/lib/live555/liveMedia/include/MediaSource.hh + src/lib/live555/liveMedia/include/MPEG2TransportStreamFramer.hh + src/lib/live555/liveMedia/include/MultiFramedRTPSink.hh + src/lib/live555/liveMedia/include/MultiFramedRTPSource.hh + src/lib/live555/liveMedia/include/RTCP.hh + src/lib/live555/liveMedia/include/RTPInterface.hh + src/lib/live555/liveMedia/include/RTPSink.hh + src/lib/live555/liveMedia/include/RTPSource.hh + src/lib/live555/liveMedia/include/RTSPClient.hh + src/lib/live555/liveMedia/include/RTSPCommon.hh + src/lib/live555/liveMedia/include/SimpleRTPSink.hh + src/lib/live555/liveMedia/include/SimpleRTPSource.hh + src/lib/live555/UsageEnvironment/include/Boolean.hh + src/lib/live555/UsageEnvironment/include/HashTable.hh + src/lib/live555/UsageEnvironment/include/strDup.hh + src/lib/live555/UsageEnvironment/include/UsageEnvironment.hh + src/lib/live555/UsageEnvironment/include/UsageEnvironment_version.hh +) +source_group("Header Files\\lib\\live555" FILES ${LIVE555_HEADERS}) + # Make sure that CMake adds all files to the MSVC project -list(APPEND MPTV_SOURCES ${MPTV_HEADERS} ${TSREADER_SOURCES} ${TSREADER_HEADERS} ${RESOURCE_FILES}) +list(APPEND MPTV_SOURCES ${MPTV_HEADERS} ${TSREADER_SOURCES} ${TSREADER_HEADERS} ${RESOURCE_FILES} ${LIVE555_SOURCES} ${LIVE555_HEADERS}) -set(DEPLIBS ${platform_LIBRARIES} +set(DEPLIBS ${p8-platform_LIBRARIES} ${TINYXML_LIBRARIES}) if(WIN32) diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/debian/changelog kodi-pvr-mediaportal-tvserver-2.4.16/debian/changelog --- kodi-pvr-mediaportal-tvserver-1.13.7/debian/changelog 2016-04-27 11:37:11.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/debian/changelog 2017-01-19 16:40:28.000000000 +0000 @@ -1,25 +1,54 @@ -kodi-pvr-mediaportal-tvserver (1.13.7-1~trusty) trusty; urgency=low +kodi-pvr-mediaportal-tvserver (2.4.16-3~trusty) trusty; urgency=low [ kodi ] * autogenerated dummy changelog [ wsnipex ] - * v1.13.7 - Updated language files from Transifex v1.13.6 - Updated - language files from Transifex v1.13.5 - Updated language files from - Transifex v1.13.4 - Updated language files from Transifex v1.13.3 - - Updated language files from Transifex v1.13.2 - Updated language - files from Transifex v1.13.1 - Updated language files from Transifex - v1.13.0 - Timer: fixed: 'unable to resolve timer type' warnings - (Kodi expects a manual, no EPG timer type) - Timer: fixed: timer - disabled state (PVR_TIMER_STATE_CANCELLED should now be + * v2.4.14 - Fixed: #44 Active recordings not playable (multi-line + description fields were not accepted) Requires at least TVServerKodi + v1.15.0.137 (MediaPortal 1) or v1.13.100.137 - Fixed: skin fixes for + the old series recording settings dialog - Fixed: setting the + lifetime of a recording using the old series recording dialog did + not work anymore v2.4.13 - Revert a code cleanup action. This should + fix the addon start behavior when one has no radio channels. v2.4.12 + - Updated language files from Transifex v2.4.11 - Updated language + files from Transifex v2.4.10 - Add support for asynchronous connect. + The addon will now regularly try to connect to the backend if it is + not connected already. - Added support for the recording channelType + property to distinguish between radio and tv recordings (requires + TVServerKodi v1.15.0.136 or above) - Added a fallback for in- + progress recording playback using the TSReader. When the filename is + empty, try the RTSP url and vice versa. - OpenRecordedStream: send + additional error messages to the log in case things fail here. + V2.4.9 - Adapt to API change - SeekTime v2.4.8 - Add Estuary skin + support for the old series timer dialog - Fixed: various Coverity + reported issues for Live555 (part 2) - Fixed: PVR recordings episode + name being set to subplot. v2.4.7 - Fixed: Live555: Android compile + fix v2.4.6 - Fixed: various Coverity reported issues for Live555 + v2.4.5 - Cosmetics: remove the remark about "tsreader + FFmpeg". - + TSReader: add Live555 RTSP support back. Current ffmpeg rtsp streams + are quite unstable. - Add ipv6 support for connecting to the + TVServerKodi plugin. v2.4.4 - Updated language files from Transifex + v2.4.3 - Updated language files from Transifex v2.4.2 - Updated + language files from Transifex v2.4.1 - Updated language files from + Transifex v2.4.0 - Cmake: rename find_package kodi to Kodi v2.3.1 - + Fix includes v2.3.0 - Updated to PVR addon API v5.2.0 v2.2.0 - + Updated to PVR addon API v5.1.0 v2.1.1 - Timers: reduce the amount + of magic numbers (PVR API 5.0.0) - Recordings: implement the new + iChannelUid field (PVR API 5.0.0) v2.1.0 - Updated to PVR addon API + v5.0.0 v2.0.4 - Timer: fixed: 'unable to resolve timer type' + warnings (Kodi expects a manual, no EPG timer type) - Timer: fixed: + timer disabled state (PVR_TIMER_STATE_CANCELLED should now be PVR_TIMER_STATE_DISABLED) - Timer: fixed: don't show the unsupported - 'folder' field - Coverity fixes v1.12.0 - Timer: Add series - recording timer types support - Timer: Add an backward compatibility - option to re-enable the pre-Jarvis series recording dialog - Timer: - show EPG description - Recordings: pass recording series and episode - numbers to Kodi v1.11.12 - Updated language files from Transifex - v1.11.11 - Compile fix for gcc 5 - Remove the "unstable" notice from - the disclaimer - Rename TVServerXBMC to TVServerKodi - + 'folder' field v2.0.3 - Coverity fixes v2.0.2 - Updated to PVR API + v4.2.0 v2.0.1 - Updated language files from Transifex v2.0.0 - + Initial Kodi Krypton version. v1.12.0 - Timer: Add series recording + timer types support - Timer: Add an backward compatibility option to + re-enable the pre-Jarvis series recording dialog - Timer: show EPG + description - Recordings: pass recording series and episode numbers + to Kodi v1.11.12 - Updated language files from Transifex v1.11.11 - + Compile fix for gcc 5 - Remove the "unstable" notice from the + disclaimer - Rename TVServerXBMC to TVServerKodi - pvr.mediaportal.tvserver binary now reports the version number from the addon.xml v1.11.10 - Updated language files from Transifex v1.11.9 - Updated language files from Transifex v1.11.8 - Updated to @@ -170,4 +199,4 @@ client should abort connection when the TVServerXBMC version is too old v1.1.0.60 - Fix PVR client destroy - -- wsnipex Wed, 27 Apr 2016 13:37:11 +0200 + -- wsnipex Thu, 19 Jan 2017 17:40:28 +0100 diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/debian/changelog.tmp kodi-pvr-mediaportal-tvserver-2.4.16/debian/changelog.tmp --- kodi-pvr-mediaportal-tvserver-1.13.7/debian/changelog.tmp 2016-04-27 11:36:07.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/debian/changelog.tmp 2017-01-19 16:39:14.000000000 +0000 @@ -1,4 +1,4 @@ -kodi-pvr-mediaportal-tvserver (1.13.7-1~#DIST#) #DIST#; urgency=low +kodi-pvr-mediaportal-tvserver (2.4.16-3~#DIST#) #DIST#; urgency=low [ kodi ] * autogenerated dummy changelog diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/debian/kodi-pvr-mediaportal-tvserver.install kodi-pvr-mediaportal-tvserver-2.4.16/debian/kodi-pvr-mediaportal-tvserver.install --- kodi-pvr-mediaportal-tvserver-1.13.7/debian/kodi-pvr-mediaportal-tvserver.install 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/debian/kodi-pvr-mediaportal-tvserver.install 2017-01-19 16:39:14.000000000 +0000 @@ -1,2 +1,2 @@ -usr/lib/*/addons/pvr.mediaportal.tvserver/pvr.mediaportal.tvserver.so* usr/lib/kodi/addons/pvr.mediaportal.tvserver -usr/share/kodi/addons/pvr.mediaportal.tvserver +usr/lib +usr/share diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/addon.xml.in kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/addon.xml.in --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/addon.xml.in 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/addon.xml.in 2017-01-11 10:28:16.000000000 +0000 @@ -1,12 +1,12 @@ - + Kodi voorprogram vir die MediaPortal TV Bediener (ffmpeg + tsreader weergawe) Kodi frontend for the MediaPortal TV Server (ffmpeg + tsreader version) Kodi клиент за ТВ сървъра MediaPortal (ffmpeg + tsreader версия) - Frontal Kodi pel servidor de TV MediaPortal (versió ffmpeg + tsreader) + Frontal de Kodi per al servidor de TV MediaPortal Rozhraní Kodi pro televizní server MediaPortal (verze ffmpeg + tsreader) Blaen Kodi ar gyfer Gweinydd Teledu MediaPortal (fersiwn ffmpeg + tsreader) Kodi frontend til MediaPortal TV server (ffmpeg + tsreader version) Kodi Oberfläche für den MediaPortal TV Server (ffmpeg + tsreader Version) Frontend του Kodi για το διακομιστή MediaPortal TV (έκδοση ffmpeg + tsreader) Kodi frontend for the MediaPortal TV Server (ffmpeg + tsreader version) - Kodi frontend for the MediaPortal TV Server (ffmpeg + tsreader version) + Kodi frontend for the MediaPortal TV Server Kodi frontend for the MediaPortal TV Server (ffmpeg + tsreader version) Kodi frontend for the MediaPortal TV Server (FFmpeg + TSReader version) Interfaz Kodi para el MediaPortal TV Server (versión ffmpeg + tsreader) Interfaz Kodi para el MediaPortal TV Server (versión ffmpeg + tsreader) + Kodi frontend para el MediaPortal TV Server Kodi esi MediaPortal TV serverile (ffmpeg + tsreader versioon) - Kodi frontend MediaPortal TV-serverille (ffmpeg + tsreader) - Frontal Kodi pour le serveur MediaPortal TV (version ffmpeg + tsreader) - Interface logicielle pour le serveur TV de MediaPortal (version ffmpeg + tsreader) + Kodin MediaPortal TV-server -asiakasohjelma + Frontal Kodi pour le serveur MediaPortal TV + Interface logicielle Kodi pour le serveur TV de MediaPortal O Interface do Kodi para o Servidor de TV de MediaPortal (versión de ffmpeg + tsreader) לקוח טלוויזיה חיה עבור MediaPortal (גרסת FFmpeg ו־TSReader) Kodi sučelje za MediaPortal TV poslužitelj (ffmpeg + tsreader inačica) @@ -47,41 +48,42 @@ Bahagian hadapan Kodi untuk Pelayan TV MediaPortal (versi ffmpeg + tsreader) MediaPortal TV Server အတွက် Kodi frontend (ffmpeg + tsreader version) Kodi skall for MediaPortal TV Server (ffmpeg + tsreader versjon) - Kodi frontend voor de Mediaportal TV Server (ffmmpeg + tsreader versie) + Kodi frontend voor de Mediaportal TV Server Klient telewizyjny dla MediaPortal TV (wersja ffmpeg + tsreader) Frontend do Kodi para Servidor de TV MediaPortal (ffmpeg + versão tsreader) - Frontend Kodi para o MediaPortal TV Server (versão ffmpeg + tsreader) - Interfața Kodi pentru MediaPortal TV Server (versiunea ffmpeg + tsreader) + Interface Kodi para MediaPortal TV Server (versão ffmpeg + tsreader) + Partea din față a Kodi pentru servitorul tv MediaPortal Интерфейс Kodi для ТВ-сервера MediaPortal (версия ffmpeg + tsreader) Kodi rozhranie pre MediaPortal TV server (verzia ffmpeg + tsreader) Kodijev vmesnik za strežnik MediaPortal TV (ffmpeg + tsreader) - Kodi интерфејс за MediaPortal ТВ Сервер (ffmpeg + tsreader верзија) - Kodi interfejs za MediaPortal TV Server (ffmpeg + tsreader verzija) + Kodi интерфејс за MediaPortal ТВ Сервер + Kodi interfejs za MediaPortal TV Server Kodi's framsida för servern MediaPortal TV (ffmpeg + tsreader-version) MediaPortal TV Sunucusu için Kodi ön ucu (ffmpeg + tsreader sürümü) Накладка Kodi для сервера ТВ MediaPortal'у (версія ffmpeg + tsreader) Giao tiếp Kodi cho MediaPortal TV Server (phiên bản ffmpeg + tsreader) - Kodi 的 MediaPortal TV 服务器前端(ffmpeg + tsreader版) + Kodi 的 MediaPortal TV 服务器前端 MediaPortal電視服務器的Kodi前端 (ffmpeg+tsreader版本) MediaPortal TV Bediener voorprogram. Ondersteun stroom van Lewendige TV & Opnames, luister na Radio kanale, EPG en Tydhouers. Hierdie byvoegsel kombineer die voormalige ffmpeg en tsreader byvoegsels. MediaPortal TV Server frontend. Supports streaming of Live TV & Recordings, listening to Radio channels, EPG and Timers. This addon combines the former ffmpeg and tsreader addons. Kodi клиент за ТВ сървъра MediaPortal. Поддържа поточна телевизия и записване, слушане на радио канали, електронен програмен справочник и броячи. Добавката обединява старите добавки ffmpeg и tsreader. - Frontal Kodi de TV MediaPortal. Suporta el flux de TV en directe i enregistraments, escoltes de canals de ràdio, EPG i temporitzadors. Aquest connector combina els anteriors connectors de ffmpg i tsreader. + Frontal del servidor de TV MediaPortal. És compatible amb les transmissions en línia de TV en directe i enregistraments, escolta de canals de ràdio, guia electrònica de programació (EPG) i temporitzadors. Rozhraní pro televizní server MediaPortal. Podporuje proudové vysílání živého vysílání a nahrávek, poslech kanálů rádia, EPG, a časovače. Toto rozšíření kombinuje rozšíření ffmpeg a tsreader. Blaen Gweinydd Teledu Media Portal. Mae'n cynnal ffrydio Teledu Byw a Recordiadau, gwrando ar sianeli Radio, Amserlenni Rhaglenni Electronig ac Amseryddion. Mae'r ychwanegyn yn cynnwys y cyn ychwanegion ffmpeg a tsreader. MediaPortal TV server frontend. Understøtter streaming af TV og Optagelser, Radiokanaler, EPG og Timere. Denne addon kombinerer de gamle ffmpeg og tsreader addons. Mediaportal TV Server Oberfläche. Unterstützt Live TV & Aufnahmen, Radiokanäle, EPG und Timer. Dieses Addon kombiniert die ehemaligen ffmpeg- und tsreader-Addons. Frontend για το διακομιστή MediaPortal TV. Υποστηρίζει ροές Live TV & Εγγραφές, ακρόαση Ραδιοφώνου, EPG και Χρονοδιακόπτες. Αυτό το πρόσθετο συνδυάζει τα προηγούμενα πρόσθετα ffmpeg και tsreader. MediaPortal TV Server frontend. Supports streaming of Live TV & Recordings, listening to Radio channels, EPG and Timers. This addon combines the former ffmpeg and tsreader addons. - MediaPortal TV Server frontend. Supports streaming of Live TV & Recordings, listening to Radio channels, EPG and Timers. This addon combines the former ffmpeg and tsreader addons. + MediaPortal TV Server frontend. Supports streaming of Live TV & Recordings, listening to Radio channels, EPG and Timers. MediaPortal TV Server frontend. Supports streaming of Live TV & Recordings, listening to Radio channels, EPG and Timers. This addon combines the former ffmpeg and tsreader addons. MediaPortal TV Server frontend. Supports streaming of Live TV & Recordings, listening to Radio channels, EPG and Timers. This addon combines the former FFmpeg and TSReader addons. Interfaz MediaPortal TV Server. Soporta transmisión de TV en vivo y grabaciones, escuchar canales de radio, GEP y temporizadores. Este complemento combina los anteriores ffmpeg y tsreader. Interfaz MediaPortal TV Server. Soporta transmisión de TV en vivo y grabaciones, escuchar canales de radio, EPG y temporizadores. Este complemento combina los anteriores ffmpeg y tsreader. + MediaPortal TV Server frontend. Soporta streaming de TV y grabaciones en vivo, escucha de canales de radio, EPG y temporizadores. MediaPortal TV serveri esi. Toetab telekanalite striimimist ja salvestamist, raadio kuulamist ja elektroonilist saatekava. See lisa kombineerib endas endised ffmpeg ja tsreader lisad. - MediaPortal TV-palvelimen frontend. Tukee livelähetysten sekä tallenteiden virtaustoista, radiokanavia, ohjelmaopasta sekä ajastuksia. Tämä lisäosa yhdistää aikaisemmat ffmpeg- ja tsreader-lisäosat. - Frontal pour le serveur MediaPortal TV, prenant en charge la diffusion en continu des télés en direct & les enregistrements, l'écoute de chaînes radio, le GÉP et les minuteries. Cet addiciel combines les anciens addiciels ffmpeg et tsreader. - Interface logicielle pour le serveur TV de MediaPortal. Il gère la lecture et l'enregistrement en continu de la TV en direct, l'écoute de radios, le guide électronique des programmes TV et les programmations. Cette extension combine les anciennes extensions ffmpeg et tsreader. + MediaPortal TV Serverin asiakasohjelma. Tukee suorien tv-lähetysten ja tallennusten katsomista, radiokanavia, ohjelmaopasta ja ohjelmien ajastamista. + Frontal pour le serveur MediaPortal TV, prenant en charge la diffusion en continu des télés en direct & les enregistrements, l'écoute de chaînes radio, le GÉP et les minuteries. + Interface logicielle pour le serveur TV de MediaPortal. Il gère la lecture et l'enregistrement en continu de la TV en direct, l'écoute de radios, le guide électronique des programmes TV et les programmations. Interface do Servidor de TV de MediaPortal. Soporta transmisión de TV en directo e Gravacións, escoita de canles de radio, Guía e programacións. Este complemento combina o antigo ffmpeg e os complementos tsreader. לקוח טלוויזיה חיה של MediaPortal. תומך בהזרמת שידורים חיים והקלטות, האזנה לרדיו, הצגת לוח שידורים ותזמון הקלטות. הרחבה זו משלבת את ההרחבות הקודמות המבוססות על FFmpeg ו־TSReader. MediaPortal TV poslužitelj sučelje. Podržava stremanje i snimanje TV programa, slušanje radio programa, elektronski programski vodič (EPG) i vremenski zadano snimanje. Ovaj dodatak objedinjuje nekadašnje ffmpeg i tsreader dodatke. @@ -96,26 +98,26 @@ MediaPortal TV Server frontend. Supports streaming of Live TV & Recordings, listening to Radio channels, EPG and Timers. This addon combines the former ffmpeg and tsreader addons. Bahagian hadapan Pelayan TV MediaPortal; menyokong penstriman TV Langsung & Rakaman, mendengar saluran Radio, EPG dan Pemasa. Tambahan ini gabungkan tambahan ffmpef fan tsreader terdahulu. MediaPortal TV Server skall. Støtter streaming av Live TV og opptak, Lytte til Radio kanaler, EPG og Timere. Dette tillegget kombinererer de tidligere ffmpeg og tsreader tilleggene. - MediaPortal TV Server frontend. Ondersteunt het bekijken van Live TV en opnames, het luisteren van radio zenders, het tonen van de EPG en het inplannen/beheren van nieuwe opnames (Timers). Deze addon combineert de voorgaande "ffmpeg" en "tsreader" addons. - Klient telewizyjny dla MediaPortal obsługuje strumieniowanie kanałów radiowych i telewizyjnych, nagrywanie i harmonogram nagrań oraz funkcje przewodnika telewizyjnego. Klient ten łączy w sobie funkcje wcześniejszych klientów ffmpeg i tsreader. + MediaPortal TV Server frontend. Ondersteunt het bekijken van Live TV en opnames, het luisteren van radio zenders, het tonen van de EPG en het inplannen/beheren van nieuwe opnames (Timers). + Klient telewizyjny dla MediaPortal obsługuje transmisje kanałów radiowych i telewizyjnych, nagrywanie i harmonogram nagrań oraz funkcje przewodnika telewizyjnego. Klient ten łączy w sobie funkcje wcześniejszych klientów ffmpeg i tsreader. Frontend do Servidor de TV do MediaPortal. Suporta streaming de TV Ao Vivo e Gravações, escutar canais de rádio, EPG e Agendamentos. Este addon combina os antigos addons ffmpeg e tsreader Interface para o servidor MediaPortal TV. Suporta transmissão de TV em direto e gravações, ouvir estações de Rádio, EPG e Temporizadores. Este add-on combina os antigos add-ons ffmpeg e tsreader. - Interfața MediaPortal TV Server. Suportă streaming TV live & Înregistrări, ascultarea posturilor de radio, EPG și Înregistrare programată. Acest plugin combină ex addons ffmpeg si tsreader. + Partea din față pentru servitorul tv MediaPortal. Capabil de difuzarea televiunii în direct și a înregistrărilor, ascultarea canalelor Radio channels, ghid tv electronic și cronometre. Интерфейс Kodi для ТВ-сервера MediaPortal. Поддерживает просмотр и запись ТВ, прослушивание радио, EPG и таймеры. Это дополнение заменяет старые дополнения ffmpeg и tsreader. Rozhranie pre MediaPortal TV server. Podporuje streamovanie živého televízneho vysielania a nahrávok, počúvanie rozhlasových kanálov, EPG a časovače. Tento doplnok zlučuje dohromady bývalé doplnky ffmpeg a tsreader. Vmesnik za MediaPortal TV strežnik. Podpira pretakanje televizije v živo & posnetkov, poslušanje radia, EPG in časovnike. Ta dodatek združuje prejšnja dodatka ffmpeg in tsreader. - MediaPortal ТВ Сервер интерфејс. Подржава стримовање ТВ Уживо & Снимака, слушање Радио канала, EPG и Тајмере. Овај додатни програм комбинује претходне ffmpeg и tsreader додатне програме. - MediaPortal TV Server interfejs. Podržava strimovanje TV Uživo & Snimaka, slušanje Radio kanala, EPG i Tajmere. Ovaj dodatni program kombinuje prethodne ffmpeg i tsreader dodatne programe. + MediaPortal ТВ Сервер интерфејс. Подржава стримовање ТВ Уживо & Снимака, слушање Радио канала, EPG и Тајмере. + MediaPortal TV Server interfejs. Podržava strimovanje TV Uživo & Snimaka, slušanje Radio kanala, EPG i Tajmere. MediaPortal TV-server frontend. Stödjer strömning av direktsänd TV & inspelningar, lyssna på radiokanaler, EPG och timers. Detta tillägg kombinerar dom tidigare ffmpeg och tsreader tilläggen. MediaPortal TV Sunucusu ön ucu. Canlı TV akışı ve kayıt yapabilme, radyo kanalları dinleme, EPG ve zamanlayıcıları destekler. Bu eklenti eski ffmpeg ve tsreader eklentilerini birleştirir. Накладка для сервера ТВ MediaPortal. Підтримує потокове Live TV і запис, прослуховування радіо каналів, телегід та планування. Цей додаток сполучає колишні додатки ffmpeg і tsreader. Giao tiếp cho MediaPortal TV Server. Hỗ trợ truyền phát và thu chương trình Live TV, nghe Radio, hẹn giờ và hiển thị lịch trình chiếu (EPG). Addon này kết hợp cho các addon cũ của ffmpeg và tsreader. - MediaPortal TV 服务器前端。支持直播电视和录像流媒体、收听电台、电子节目单和定时器。这个插件结合了之前的 ffmpeg 和 tsreader 插件。 + MediaPortal TV 服务器前端。支持流媒体直播电视和录像、收听电台、电子节目单和定时器。 MediaPortal電視服務器的前端。支援的串流檔案包括有:電視直播和錄影,收聽廣播頻道,電子節目表和計時器。這個插件結合了先前ffmpeg和tsreader的插件。 Hierdie is onstabiele sagteware! Die outeurs is op geen manier verantwoordelik vir gefaalde opnames, inkorrekte tydhouers, gemorsde ure, of enige ander ongewensde effekte. This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects.. Тази програма е нестабилна! Авторите не носят отговорност за неуспешно записване, некоректни броячи, пропиляното време и други нежелани ефекти. - Això és programari inestable! Els autors no són de cap manera responsables dels enregistraments que han fallat, temporitzadors incorrectes, hores perdudes, o qualsevols altres efectes indesitjables.. + Això és programari inestable! Els autors no són de cap manera responsables dels enregistraments que han fallat, temporitzadors incorrectes, hores perdudes, o qualsevol altre efecte indesitjat. Tento software není stabilní. Autoři nejsou žádným způsobem zodpovědní za selhání při nahrávání, neplatné časovače, ztracený čas, či jakékoliv jiné nežádoucí události... Mae'r feddalwedd hon yn fregus! Nid yw'r awduron yn gyfrifol mewn unrhyw ffordd am fethu recordio, amseru gwallus, oriau wedi eu gwastraffu nac effeithiau anymunol eraill. Dette er ustabil software! Ophavsmændene er på ingen måde ansvarlige for mislykkede optagelser, ukorrekte timere, spildte timer, eller andre uønskede konsekvenser.. @@ -129,7 +131,7 @@ ¡Este es un software inestable! Los autores no son de ninguna manera responsables de las grabaciones fallidas o incorrectas, las temporizadores perdidas, ni otros efectos no deseables.. ¡Esto es software inestable! Los autores no son de ninguna manera responsables por grabaciones fallidas, temporizadores incorrectos, horas perdidas o cualquier otro efecto no deseado... See on ebastabiilne tarkvara! Autorid ei ole kuidagi moodi vastutavad nurjunud salvestiste, ebaõige aegrelee, raisatud tundide ega muude soovimatute asjade eest. - Tämä on epävakaa ohjelmisto! Tekijät eivät ole millään muotoa vastuussa epäonnistuneista tallennuksista, virheellisistä ajastuksia, haaskatusta ajasta, verenpaineen noususta tai mistään muusta epäsuotuisasta vaikutuksesta. + Tämä on epävakaa ohjelma! Sen tekijät eivät ole millään muotoa vastuussa epäonnistuneista tallennuksista, virheellisistä ajastuksista, haaskatusta ajasta, verenpaineen noususta tai mistään muusta epäsuotuisasta vaikutuksesta. Les auteurs ne sont aucunement responsables des enregistrements défaillants, des minuteries erronées, des heures perdues ou tout autre effet indésirable. Logiciel en cours d'élaboration ! Les auteurs ne sont en aucun cas responsables de l'échec des enregistrements, programmations défectueuses, temps perdu ou autres effets indésirables. Software non estable, os autores non se fan responsábeis dos erros na gravacións, temporizadores incorrectos, e outros efectos non desexados. diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/changelog.txt kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/changelog.txt --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/changelog.txt 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/changelog.txt 2017-01-11 10:28:16.000000000 +0000 @@ -1,30 +1,91 @@ -v1.13.7 -- Updated language files from Transifex +v2.4.14 +- Fixed: #44 Active recordings not playable (multi-line description fields were not accepted) + Requires at least TVServerKodi v1.15.0.137 (MediaPortal 1) or v1.13.100.137 +- Fixed: skin fixes for the old series recording settings dialog +- Fixed: setting the lifetime of a recording using the old series recording dialog did not work anymore + +v2.4.13 +- Revert a code cleanup action. This should fix the addon start behavior when one has no radio channels. -v1.13.6 +v2.4.12 - Updated language files from Transifex -v1.13.5 +v2.4.11 - Updated language files from Transifex -v1.13.4 +v2.4.10 +- Add support for asynchronous connect. The addon will now regularly try to connect to the backend if it is not connected already. +- Added support for the recording channelType property to distinguish between radio and tv recordings (requires TVServerKodi v1.15.0.136 or above) +- Added a fallback for in-progress recording playback using the TSReader. When the filename is empty, try the RTSP url and vice versa. +- OpenRecordedStream: send additional error messages to the log in case things fail here. + +V2.4.9 +- Adapt to API change - SeekTime + +v2.4.8 +- Add Estuary skin support for the old series timer dialog +- Fixed: various Coverity reported issues for Live555 (part 2) +- Fixed: PVR recordings episode name being set to subplot. + +v2.4.7 +- Fixed: Live555: Android compile fix + +v2.4.6 +- Fixed: various Coverity reported issues for Live555 + +v2.4.5 +- Cosmetics: remove the remark about "tsreader + FFmpeg". +- TSReader: add Live555 RTSP support back. Current ffmpeg rtsp streams are quite unstable. +- Add ipv6 support for connecting to the TVServerKodi plugin. + +v2.4.4 - Updated language files from Transifex -v1.13.3 +v2.4.3 - Updated language files from Transifex -v1.13.2 +v2.4.2 - Updated language files from Transifex -v1.13.1 +v2.4.1 - Updated language files from Transifex -v1.13.0 +v2.4.0 +- Cmake: rename find_package kodi to Kodi + +v2.3.1 +- Fix includes + +v2.3.0 +- Updated to PVR addon API v5.2.0 + +v2.2.0 +- Updated to PVR addon API v5.1.0 + +v2.1.1 +- Timers: reduce the amount of magic numbers (PVR API 5.0.0) +- Recordings: implement the new iChannelUid field (PVR API 5.0.0) + +v2.1.0 +- Updated to PVR addon API v5.0.0 + +v2.0.4 - Timer: fixed: 'unable to resolve timer type' warnings (Kodi expects a manual, no EPG timer type) - Timer: fixed: timer disabled state (PVR_TIMER_STATE_CANCELLED should now be PVR_TIMER_STATE_DISABLED) - Timer: fixed: don't show the unsupported 'folder' field + +v2.0.3 - Coverity fixes +v2.0.2 +- Updated to PVR API v4.2.0 + +v2.0.1 +- Updated language files from Transifex + +v2.0.0 +- Initial Kodi Krypton version. + v1.12.0 - Timer: Add series recording timer types support - Timer: Add an backward compatibility option to re-enable the pre-Jarvis series recording dialog diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.az_az/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.az_az/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.az_az/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.az_az/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -16,6 +16,42 @@ "Language: az_AZ\n" "Plural-Forms: nplurals=1; plural=0;\n" +msgctxt "#30040" +msgid "Connection" +msgstr "Qoşulma" + +msgctxt "#30060" +msgid "All cards are busy" +msgstr "Bütün kartlar məşğuldur" + +msgctxt "#30063" +msgid "No signal detected" +msgstr "Siqnal tapılmadı" + +msgctxt "#30066" +msgid "Unknown channel" +msgstr "Bilinməyən kanal" + +msgctxt "#30103" +msgid "Channels" +msgstr "Kanallar" + msgctxt "#30132" msgid "Days" -msgstr "gün" +msgstr "Günlər" + +msgctxt "#30133" +msgid "Always" +msgstr "Həmişə" + +msgctxt "#30134" +msgid "1 week" +msgstr "1 həftə" + +msgctxt "#30138" +msgid "1 month" +msgstr "1 ay" + +msgctxt "#30140" +msgid "1 year" +msgstr "1 il" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.ca_es/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.ca_es/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.ca_es/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.ca_es/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -18,11 +18,11 @@ msgctxt "#30000" msgid "Mediaportal Hostname" -msgstr "Nom de màquina del MediaPortal" +msgstr "Nom d'amfitrió de MediaPortal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" -msgstr "Port del connector per al Kodi del MediaPortal" +msgstr "Port del connector MediaPortal de Kodi" msgctxt "#30002" msgid "Free-to-air only" @@ -30,15 +30,15 @@ msgctxt "#30003" msgid "Include Radio" -msgstr "Inclou ràdio" +msgstr "Inclou la ràdio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" -msgstr "Canvi de canal ràpid (no aturis el canvi d'hora)" +msgstr "Canvi de canal ràpid (no aturis els salts en el temps)" msgctxt "#30005" msgid "Connect timeout (s)" -msgstr "Temps d'espera de connexió (s)" +msgstr "Temps d'expiració de la connexió (s)" msgctxt "#30006" msgid "Import only TV Channels from group" @@ -50,7 +50,7 @@ msgctxt "#30008" msgid "Convert hostname to IP-adress" -msgstr "Converteix el nom de màquina a adreça IP" +msgstr "Converteix el nom d'amfitrió a adreça IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" @@ -60,9 +60,13 @@ msgid "Wait time after tuning a channel (ms)" msgstr "Temps d'espera després de sintonitzar un canal (ms)" +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "Habilita el diàleg d'enregistrament de les sèries antigues" + msgctxt "#30015" msgid "Streaming method" -msgstr "Mètode de flux" +msgstr "Mètode de transmissió en línia" msgctxt "#30016" msgid "Windows user account (SMB)" @@ -74,7 +78,7 @@ msgctxt "#30018" msgid "Use RTSP streaming" -msgstr "Utilitza flux RTSP" +msgstr "Utilitza la transmissió en línia RTSP" msgctxt "#30040" msgid "Connection" @@ -90,11 +94,11 @@ msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" -msgstr "La vostra versió de servidor de TV Kodi '%s' és massa antiga. Per favor, actualitzau a '%s' o superior!" +msgstr "La vostra versió de servidor de TV Kodi '%s' és massa antiga. Si us plau, actualitzeu a '%s' o superior!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" -msgstr "La vostra versió de servidor de TV Kodi és massa antiga. Per favor, actualitzau a '%s' o superior!" +msgstr "La vostra versió de servidor de TV Kodi és massa antiga. Si us plau, actualitzeu a '%s' o superior!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." @@ -134,7 +138,7 @@ msgctxt "#30068" msgid "Channel is not mapped to any card" -msgstr "El canal no està mapejat a cap targeta" +msgstr "El canal no està assignat a cap targeta" msgctxt "#30069" msgid "Card is disabled" @@ -166,7 +170,7 @@ msgctxt "#30100" msgid "Schedule settings" -msgstr "Ajustos de programacions" +msgstr "Ajusts de les programacions" msgctxt "#30101" msgid "Frequency" @@ -186,27 +190,51 @@ msgctxt "#30105" msgid "Record minutes before start" -msgstr "Minuts gravats abans de començar" +msgstr "Minuts enregistrats abans de començar" msgctxt "#30106" msgid "Record minutes after end" -msgstr "Minuts gravats després de finalitzar" +msgstr "Minuts enregistrats després de finalitzar" msgctxt "#30110" msgid "Record Once" -msgstr "Grava un cop" +msgstr "Enregistra un cop" msgctxt "#30111" msgid "Record Daily (This program)" -msgstr "Gravar Diàriament (Aquest programa)" +msgstr "Enregistra diàriament (aquest programa)" msgctxt "#30112" msgid "Record Weekly" -msgstr "Grava setmanalment" +msgstr "Enregistra setmanalment" msgctxt "#30113" msgid "Record Weekends" -msgstr "Gravar Caps de Setmana" +msgstr "Enregistra els caps de setmana" + +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "Enregistra els caps de setmana" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "Enregistra cada cop en aquest canal" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "Enregistra cada cop en cada canal" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "Enregistra cada setmana a aquesta hora" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "Enregistra cada dia a aquesta hora" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "Enregistra setmanalment en aquest canal" msgctxt "#30120" msgid "This time" @@ -232,6 +260,10 @@ msgid "Until space needed" msgstr "Fins que necessiti espai" +msgctxt "#30131" +msgid "Until watched" +msgstr "Fins vist" + msgctxt "#30132" msgid "Days" msgstr "Dies" @@ -240,10 +272,30 @@ msgid "Always" msgstr "Sempre" +msgctxt "#30134" +msgid "1 week" +msgstr "1 setmana" + msgctxt "#30135" msgid "Backend default" -msgstr "Backend per defecte" +msgstr "Predeterminat del dorsal" msgctxt "#30136" msgid "Kodi default" msgstr "Per defecte a Kodi" + +msgctxt "#30137" +msgid "%d weeks" +msgstr "%d setmanes" + +msgctxt "#30138" +msgid "1 month" +msgstr "1 mes" + +msgctxt "#30139" +msgid "%d months" +msgstr "%d mesos" + +msgctxt "#30140" +msgid "1 year" +msgstr "1 any" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.cs_cz/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.cs_cz/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.cs_cz/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.cs_cz/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -262,7 +262,7 @@ msgctxt "#30131" msgid "Until watched" -msgstr "Dokud není shlédnuto" +msgstr "Dokud není zhlédnuto" msgctxt "#30132" msgid "Days" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.el_gr/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.el_gr/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.el_gr/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.el_gr/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -268,6 +268,10 @@ msgid "Kodi default" msgstr "Προεπιλογή του Kodi" +msgctxt "#30137" +msgid "%d weeks" +msgstr "%d εβδομάδες" + msgctxt "#30138" msgid "1 month" msgstr "1 μήνας" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.en_gb/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.en_gb/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.en_gb/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.en_gb/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -16,7 +16,8 @@ "Language: en_GB\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -# Settings labels +#Settings labels + msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "" @@ -84,6 +85,7 @@ msgstr "" #empty strings from id 30019 to 30039 +#Category labels msgctxt "#30040" msgid "Connection" @@ -98,6 +100,7 @@ msgstr "" #empty strings from id 30043 to 30049 +#On-screen error messages msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" @@ -112,6 +115,7 @@ msgstr "" #empty strings from id 30053 to 30059 +#Status message strings corresponding to the MediaPortal TvResult enum msgctxt "#30060" msgid "All cards are busy" @@ -178,8 +182,8 @@ msgstr "" #empty strings from id 30076 to 30099 +#Schedule settings dialog -# Schedule settings dialog msgctxt "#30100" msgid "Schedule settings" msgstr "" @@ -209,8 +213,8 @@ msgstr "" #empty strings from id 30107 to 30109 +#Frequency: -# Frequency: msgctxt "#30110" msgid "Record Once" msgstr "" @@ -251,7 +255,8 @@ msgid "Record weekly on this channel" msgstr "" -# Airtime: +#Airtime: + msgctxt "#30120" msgid "This time" msgstr "" @@ -267,7 +272,6 @@ #empty strings from id 30123 to 30124 #Channel: -# Channel: msgctxt "#30125" msgid "This Channel" msgstr "" @@ -277,8 +281,8 @@ msgstr "" #empty strings from id 30127 to 30129 +#Keep: -# Keep: msgctxt "#30130" msgid "Until space needed" msgstr "" @@ -299,7 +303,8 @@ msgid "1 week" msgstr "" -# Pre/post record time +#Pre/post record time + msgctxt "#30135" msgid "Backend default" msgstr "" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.es_es/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.es_es/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.es_es/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.es_es/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -60,6 +60,10 @@ msgid "Wait time after tuning a channel (ms)" msgstr "Tiempo de espera después de sintonizar un canal (ms)" +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "Habilitar la ventana de grabación de series antiguas" + msgctxt "#30015" msgid "Streaming method" msgstr "Método de transmisión" @@ -208,6 +212,30 @@ msgid "Record Weekends" msgstr "Grabar Fines de Semana" +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "Grabar días entre semana " + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "Grabar siempre en este canal" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "Grabar siempre en cada canal" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "Grabar cada semana a esta hora" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "Grabar cada día a esta hora" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "Grabar semanalmente en este canal" + msgctxt "#30120" msgid "This time" msgstr "A esta hora" @@ -232,6 +260,10 @@ msgid "Until space needed" msgstr "Hasta que necesite espacio" +msgctxt "#30131" +msgid "Until watched" +msgstr "Hasta que lo vea" + msgctxt "#30132" msgid "Days" msgstr "Días" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.es_mx/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.es_mx/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.es_mx/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.es_mx/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -16,14 +16,58 @@ "Language: es_MX\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgctxt "#30000" +msgid "Mediaportal Hostname" +msgstr "Nombre de host de Mediaportal" + +msgctxt "#30001" +msgid "Mediaportal Kodi plugin Port" +msgstr "Puerto del complemento Kodi Mediaportal" + +msgctxt "#30002" +msgid "Free-to-air only" +msgstr "Sólo en el aire" + msgctxt "#30003" msgid "Include Radio" msgstr "Incluir Radio" +msgctxt "#30004" +msgid "Fast channel switching (don't stop timeshift)" +msgstr "Conmutación rápida de canales (no detiene el turno de tiempo)" + msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Tiempo para conectar agotado (s)" +msgctxt "#30006" +msgid "Import only TV Channels from group" +msgstr "Importar sólo los canales de TV del grupo" + +msgctxt "#30007" +msgid "Import only Radio Channels from group" +msgstr "Importar sólo los canales de radio del grupo" + +msgctxt "#30008" +msgid "Convert hostname to IP-adress" +msgstr "Convertir nombre de host en dirección IP" + +msgctxt "#30009" +msgid "EPG: Read genre strings (slow)" +msgstr "EPG: Leer cadenas de género (lento)" + +msgctxt "#30010" +msgid "Wait time after tuning a channel (ms)" +msgstr "Tiempo de espera después de sintonizar un canal (ms)" + +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "Activar diálogo de grabación de series antiguas" + +msgctxt "#30015" +msgid "Streaming method" +msgstr "Método de transmisión" + msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Usuario de cuenta de Windows (SMB)" @@ -40,14 +84,34 @@ msgid "Connection" msgstr "Conexión" +msgctxt "#30041" +msgid "MediaPortal" +msgstr "MediaPortal" + msgctxt "#30042" msgid "Playback" msgstr "Reproducir" +msgctxt "#30050" +msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" +msgstr "Su versión de TVServerKodi '%s' es demasiado antigua. ¡Actualice a '%s' o superior!" + +msgctxt "#30051" +msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" +msgstr "Su versión TVServerKodi es demasiado antigua. ¡Actualice a '%s' o superior!" + +msgctxt "#30052" +msgid "Recording playback failed. Empty URL of filename." +msgstr "Se ha producido un error en la grabación. URL vacía del nombre de archivo." + msgctxt "#30060" msgid "All cards are busy" msgstr "Todas las tarjetas están ocupadas" +msgctxt "#30061" +msgid "Channel is scrambled" +msgstr "Canal es codificado" + msgctxt "#30062" msgid "No video or audio detected" msgstr "Vídeo o audio no detectado" @@ -68,10 +132,30 @@ msgid "Unknown channel" msgstr "Canal desconocido" +msgctxt "#30067" +msgid "No tuning details" +msgstr "Sin detalles de sintonización" + +msgctxt "#30068" +msgid "Channel is not mapped to any card" +msgstr "El canal no está asignado a ninguna tarjeta" + msgctxt "#30069" msgid "Card is disabled" msgstr "La tarjeta es deshabilitada" +msgctxt "#30070" +msgid "Connection to slave failed" +msgstr "Error de conexión con el esclavo" + +msgctxt "#30071" +msgid "Not the owner" +msgstr "No es el dueño" + +msgctxt "#30072" +msgid "Graph building failed" +msgstr "Error construyendo gráfico" + msgctxt "#30073" msgid "SW Encoder missing" msgstr "Falta Encoder de SW" @@ -80,6 +164,14 @@ msgid "No free disk space" msgstr "No hay espacio en el disco" +msgctxt "#30075" +msgid "No PMT found" +msgstr "No se encontró PMT" + +msgctxt "#30100" +msgid "Schedule settings" +msgstr "Configuración de la programación" + msgctxt "#30101" msgid "Frequency" msgstr "Frecuencia" @@ -96,10 +188,82 @@ msgid "Keep" msgstr "Mantener" +msgctxt "#30105" +msgid "Record minutes before start" +msgstr "Minutos de grabación antes del inicio" + +msgctxt "#30106" +msgid "Record minutes after end" +msgstr "Minutos de grabación después del final" + +msgctxt "#30110" +msgid "Record Once" +msgstr "Grabar una vez" + +msgctxt "#30111" +msgid "Record Daily (This program)" +msgstr "Grabar diario (este programa)" + +msgctxt "#30112" +msgid "Record Weekly" +msgstr "Grabar semanal" + +msgctxt "#30113" +msgid "Record Weekends" +msgstr "Grabar fines de semana" + +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "Grabar días de semana" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "Grabar cada vez en este canal" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "Grabar cada vez sobre este canal" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "Grabar cada semana en este momento" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "Grabar todos los días en este momento" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "Grabar semanalmente en este canal" + +msgctxt "#30120" +msgid "This time" +msgstr "This time" + +msgctxt "#30121" +msgid "Anytime" +msgstr "Cualquier hora" + msgctxt "#30122" msgid "Manual" msgstr "Manual" +msgctxt "#30125" +msgid "This Channel" +msgstr "Este Canal" + +msgctxt "#30126" +msgid "Any Channel" +msgstr "Cualquier canal" + +msgctxt "#30130" +msgid "Until space needed" +msgstr "Hasta que se necesite espacio" + +msgctxt "#30131" +msgid "Until watched" +msgstr "Hasta ser visto" + msgctxt "#30132" msgid "Days" msgstr "Días" @@ -107,3 +271,31 @@ msgctxt "#30133" msgid "Always" msgstr "Siempre" + +msgctxt "#30134" +msgid "1 week" +msgstr "1 semana" + +msgctxt "#30135" +msgid "Backend default" +msgstr "Backend predeterminado" + +msgctxt "#30136" +msgid "Kodi default" +msgstr "Kodi predeterminado" + +msgctxt "#30137" +msgid "%d weeks" +msgstr "%d semanas" + +msgctxt "#30138" +msgid "1 month" +msgstr "1 mes" + +msgctxt "#30139" +msgid "%d months" +msgstr "%d meses" + +msgctxt "#30140" +msgid "1 year" +msgstr "1 año" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.eu_es/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.eu_es/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.eu_es/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.eu_es/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -40,6 +40,10 @@ msgid "Channels" msgstr "Kateak" +msgctxt "#30104" +msgid "Keep" +msgstr "Mantendu" + msgctxt "#30122" msgid "Manual" msgstr "Eskuz" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.fi_fi/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.fi_fi/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.fi_fi/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.fi_fi/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -22,11 +22,11 @@ msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" -msgstr "Mediaportal Kodi-lisäosan portti" +msgstr "Mediaportalin Kodi-lisäosan portti" msgctxt "#30002" msgid "Free-to-air only" -msgstr "Vain ilmaiskanavat" +msgstr "Vain salaamattomat kanavat" msgctxt "#30003" msgid "Include Radio" @@ -42,7 +42,7 @@ msgctxt "#30006" msgid "Import only TV Channels from group" -msgstr "Tuo ainoastaan TV-kanavat ryhmästä" +msgstr "Tuo ainoastaan tv-kanavat ryhmästä" msgctxt "#30007" msgid "Import only Radio Channels from group" @@ -60,6 +60,10 @@ msgid "Wait time after tuning a channel (ms)" msgstr "Odotusaika kanavan virittämisen jälkeen (ms)" +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "Käytä vanhaa sarjatallennuksen valikkoa" + msgctxt "#30015" msgid "Streaming method" msgstr "Suoratoistotapa" @@ -90,15 +94,15 @@ msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" -msgstr "TVServerKodi:n versio '%s' on liian vanha. Päivitä vähintään versioon '%s' tai uudempaan!" +msgstr "TVServerKodin versio '%s' on liian vanha. Päivitä vähintään versioon '%s' tai uudempaan!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" -msgstr "TVServerKodi:n versio on liian vanha. Päivitä vähintään versioon '%s' tai uudempaan!" +msgstr "TVServerKodin versio on liian vanha. Päivitä vähintään versioon '%s' tai uudempaan!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." -msgstr "Tallenteen toistaminen epäonnistui. Tyhjä URL tai tiedostonimi." +msgstr "Tallennuksen toistaminen epäonnistui. Tyhjä URL-osoite tai tiedostonimi." msgctxt "#30060" msgid "All cards are busy" @@ -186,11 +190,11 @@ msgctxt "#30105" msgid "Record minutes before start" -msgstr "Tallennusaika minuutteina ennen aloitusta" +msgstr "Tallennuksen alkuun lisättävä aika" msgctxt "#30106" msgid "Record minutes after end" -msgstr "Tallennusaika minuutteina lopetuksen jälkeen" +msgstr "Tallennuksen loppuun lisättävä aika" msgctxt "#30110" msgid "Record Once" @@ -208,6 +212,30 @@ msgid "Record Weekends" msgstr "Tallenna viikonloppuisin" +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "Tallenna arkisin" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "Tallenna aina tältä kanavalta" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "Tallenna aina joka kanavalta" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "Tallenna joka viikko tähän aikaan" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "Tallenna joka päivä tähän aikaan" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "Tallenna viikottain tältä kanavalta" + msgctxt "#30120" msgid "This time" msgstr "Tämän kerran" @@ -232,6 +260,10 @@ msgid "Until space needed" msgstr "Kunnes tarvetta lisätilalle" +msgctxt "#30131" +msgid "Until watched" +msgstr "Kunnes katsottu" + msgctxt "#30132" msgid "Days" msgstr "päivää" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.fr_ca/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.fr_ca/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.fr_ca/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.fr_ca/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -262,7 +262,7 @@ msgctxt "#30131" msgid "Until watched" -msgstr "Jusqu'à ce que ce soit regardé" +msgstr "Jusqu'à ce qu'il soit visionné" msgctxt "#30132" msgid "Days" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.ko_kr/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.ko_kr/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.ko_kr/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.ko_kr/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -60,6 +60,10 @@ msgid "Wait time after tuning a channel (ms)" msgstr "채널 튜닝 후 대기시간 (ms)" +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "오래된 시리즈 대화 녹화 활성화" + msgctxt "#30015" msgid "Streaming method" msgstr "스트리밍 방식" @@ -208,6 +212,30 @@ msgid "Record Weekends" msgstr "주말에 녹화" +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "평일에 녹화" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "이 채널 항상 녹화" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "모든 채널 항상 녹화" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "이 시간 매주 녹화" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "이 시간 매일 녹화" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "이 채널 매주 녹화" + msgctxt "#30120" msgid "This time" msgstr "이번만" @@ -232,6 +260,10 @@ msgid "Until space needed" msgstr "저장공간이 더 필요할 때까지" +msgctxt "#30131" +msgid "Until watched" +msgstr "시청할 때까지" + msgctxt "#30132" msgid "Days" msgstr "일" @@ -240,6 +272,10 @@ msgid "Always" msgstr "항상" +msgctxt "#30134" +msgid "1 week" +msgstr "1주" + msgctxt "#30135" msgid "Backend default" msgstr "백엔드 기본" @@ -247,3 +283,19 @@ msgctxt "#30136" msgid "Kodi default" msgstr "Kodi 기본" + +msgctxt "#30137" +msgid "%d weeks" +msgstr "%d주" + +msgctxt "#30138" +msgid "1 month" +msgstr "1개월" + +msgctxt "#30139" +msgid "%d months" +msgstr "%d개월" + +msgctxt "#30140" +msgid "1 year" +msgstr "1년" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.mn_mn/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.mn_mn/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.mn_mn/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.mn_mn/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -16,6 +16,10 @@ "Language: mn_MN\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +msgctxt "#30005" +msgid "Connect timeout (s)" +msgstr "Холболтын хугцаа хэтэрсэн" + msgctxt "#30103" msgid "Channels" msgstr "Сувгууд" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.nb_no/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.nb_no/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.nb_no/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.nb_no/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -60,6 +60,10 @@ msgid "Wait time after tuning a channel (ms)" msgstr "Tid å vente etter lasting av kanal (ms)" +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "Slå på gamle seriers opptaks dialog" + msgctxt "#30015" msgid "Streaming method" msgstr "Streming metode" @@ -208,6 +212,30 @@ msgid "Record Weekends" msgstr "Opptak i helger" +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "Ta opp Ukedager" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "Ta opp hver gang på denne kanalen" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "Ta opp hver gang på alle kanaler" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "Ta opp hver uke på denne tiden" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "Ta opp hver dag på denne tiden" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "Ta opp ukentlig på denne kanalen" + msgctxt "#30120" msgid "This time" msgstr "Dette tidspunktet" @@ -232,6 +260,10 @@ msgid "Until space needed" msgstr "Frem til plass trengs" +msgctxt "#30131" +msgid "Until watched" +msgstr "Frem til sett" + msgctxt "#30132" msgid "Days" msgstr "Dager" @@ -240,6 +272,10 @@ msgid "Always" msgstr "Alltid" +msgctxt "#30134" +msgid "1 week" +msgstr "1 uke" + msgctxt "#30135" msgid "Backend default" msgstr "Backend standard" @@ -247,3 +283,19 @@ msgctxt "#30136" msgid "Kodi default" msgstr "Kodi standard" + +msgctxt "#30137" +msgid "%d weeks" +msgstr "%d uker" + +msgctxt "#30138" +msgid "1 month" +msgstr "1 måned" + +msgctxt "#30139" +msgid "%d months" +msgstr "%d måneder" + +msgctxt "#30140" +msgid "1 year" +msgstr "1 år" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.pl_pl/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.pl_pl/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.pl_pl/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.pl_pl/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -66,7 +66,7 @@ msgctxt "#30015" msgid "Streaming method" -msgstr "Metoda strumieniowania" +msgstr "Metoda transmisji" msgctxt "#30016" msgid "Windows user account (SMB)" @@ -78,7 +78,7 @@ msgctxt "#30018" msgid "Use RTSP streaming" -msgstr "Używaj strumieniowania RTSP" +msgstr "Używaj transmisji RTSP" msgctxt "#30040" msgid "Connection" @@ -102,7 +102,7 @@ msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." -msgstr "Odtwarzanie nagrania nie powiodło się. Adres URL pliku jest pusty." +msgstr "Nieudane odtwarzanie nagrania. Adres URL lub nazwa pliku jest pusta." msgctxt "#30060" msgid "All cards are busy" @@ -114,7 +114,7 @@ msgctxt "#30062" msgid "No video or audio detected" -msgstr "Brak strumieni wideo i dźwięku" +msgstr "Nie wykryto transmisji obrazu lub dźwięku" msgctxt "#30063" msgid "No signal detected" @@ -154,7 +154,7 @@ msgctxt "#30072" msgid "Graph building failed" -msgstr "Budowanie diagramu nie powiodło się" +msgstr "Nieudane budowanie diagramu" msgctxt "#30073" msgid "SW Encoder missing" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.pt_pt/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.pt_pt/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.pt_pt/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.pt_pt/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -22,7 +22,7 @@ msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" -msgstr "Porta do plugin Kodi Mediaportal" +msgstr "Porto do plugin Kodi Mediaportal" msgctxt "#30002" msgid "Free-to-air only" @@ -30,7 +30,7 @@ msgctxt "#30003" msgid "Include Radio" -msgstr "Incluir rádio" +msgstr "Incluir Rádio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" @@ -38,7 +38,7 @@ msgctxt "#30005" msgid "Connect timeout (s)" -msgstr "Tempo limite para ligação (s)" +msgstr "Tempo-limite da ligação (s)" msgctxt "#30006" msgid "Import only TV Channels from group" @@ -60,6 +60,10 @@ msgid "Wait time after tuning a channel (ms)" msgstr "Tempo de espera após sintonizar um canal (ms)" +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "Ativar a janela antiga de gravação de séries" + msgctxt "#30015" msgid "Streaming method" msgstr "Método de transmissão" @@ -78,7 +82,7 @@ msgctxt "#30040" msgid "Connection" -msgstr "Ligação " +msgstr "Ligação" msgctxt "#30041" msgid "MediaPortal" @@ -90,11 +94,11 @@ msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" -msgstr "A sua versão do TVServerKodi '%s' é demasiado antiga. Por favor actualize para '%s' ou superior!" +msgstr "A sua versão do TVServerKodi '%s' é demasiado antiga. Por favor atualize para '%s' ou superior!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" -msgstr "A sua versão do TVServerKodi é demasiado antiga. Por favor actualize para '%s' ou superior!" +msgstr "A sua versão do TVServerKodi é demasiado antiga. Por favor atualize para '%s' ou superior!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." @@ -114,7 +118,7 @@ msgctxt "#30063" msgid "No signal detected" -msgstr "Não foi detectado sinal" +msgstr "Não foi detetado sinal" msgctxt "#30064" msgid "Unknown error" @@ -122,7 +126,7 @@ msgctxt "#30065" msgid "Unable to start graph" -msgstr "Não foi possível iniciar gráfico" +msgstr "Não foi possível iniciar o gráfico" msgctxt "#30066" msgid "Unknown channel" @@ -130,7 +134,7 @@ msgctxt "#30067" msgid "No tuning details" -msgstr "A sintonização não tem detalhes" +msgstr "Sem detalhes de sintonia" msgctxt "#30068" msgid "Channel is not mapped to any card" @@ -138,15 +142,15 @@ msgctxt "#30069" msgid "Card is disabled" -msgstr "A placa está desactivada" +msgstr "A placa está desativada" msgctxt "#30070" msgid "Connection to slave failed" -msgstr "Ligação ao escravo falhou" +msgstr "A ligação ao escravo falhou" msgctxt "#30071" msgid "Not the owner" -msgstr "Não é o proprietário" +msgstr "Não é o dono" msgctxt "#30072" msgid "Graph building failed" @@ -154,7 +158,7 @@ msgctxt "#30073" msgid "SW Encoder missing" -msgstr "Falta o codificador SW" +msgstr "Falta o codificador por SW" msgctxt "#30074" msgid "No free disk space" @@ -174,7 +178,7 @@ msgctxt "#30102" msgid "Airtime" -msgstr "Tempo de emissão:" +msgstr "Tempo de emissão" msgctxt "#30103" msgid "Channels" @@ -194,19 +198,43 @@ msgctxt "#30110" msgid "Record Once" -msgstr "Gravar uma vez" +msgstr "Gravar uma Vez" msgctxt "#30111" msgid "Record Daily (This program)" -msgstr "Gravar diariamente (Este programa)" +msgstr "Gravar Diariamente (Este programa)" msgctxt "#30112" msgid "Record Weekly" -msgstr "Gravar semanalmente" +msgstr "Gravar Semanalmente" msgctxt "#30113" msgid "Record Weekends" -msgstr "Gravar fins de semana" +msgstr "Gravar os Fins-de-Semana" + +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "Gravar nos Dias Úteis" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "Gravar a toda a hora neste canal" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "Gravar a toda a hora em todos os canais" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "Gravar todas as semanas a esta hora" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "Gravar todos os dias a esta hora" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "Gravar semanalmente neste canal" msgctxt "#30120" msgid "This time" @@ -222,16 +250,20 @@ msgctxt "#30125" msgid "This Channel" -msgstr "Este canal" +msgstr "Este Canal" msgctxt "#30126" msgid "Any Channel" -msgstr "Qualquer canal" +msgstr "Qualquer Canal" msgctxt "#30130" msgid "Until space needed" msgstr "Até ser necessário espaço" +msgctxt "#30131" +msgid "Until watched" +msgstr "Até ser visto" + msgctxt "#30132" msgid "Days" msgstr "Dias" @@ -246,7 +278,7 @@ msgctxt "#30135" msgid "Backend default" -msgstr "Predefinição do backend" +msgstr "Predefinição da infraestrutura" msgctxt "#30136" msgid "Kodi default" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.ro_ro/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.ro_ro/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.ro_ro/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.ro_ro/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -38,7 +38,7 @@ msgctxt "#30005" msgid "Connect timeout (s)" -msgstr "Conexiune timeout (s)" +msgstr "Expirare timp conexiune (s)" msgctxt "#30006" msgid "Import only TV Channels from group" @@ -98,7 +98,7 @@ msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" -msgstr "Versiunea ta de TVServerKodi este prea veche. Va rugam actualizați la '%s' sau mai nouă." +msgstr "Versiunea ta de TVServerKodi este prea veche. Vă rugămm treceți la „%s” sau mai nouă." msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.szl/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.szl/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.szl/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.szl/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -16,10 +16,170 @@ "Language: szl\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +msgctxt "#30000" +msgid "Mediaportal Hostname" +msgstr "Miano lebo adresa ôd serwera" + +msgctxt "#30001" +msgid "Mediaportal Kodi plugin Port" +msgstr "Port przidŏwka Mediaportal dlŏ Kodi" + +msgctxt "#30002" +msgid "Free-to-air only" +msgstr "Ino niykodowane" + +msgctxt "#30003" +msgid "Include Radio" +msgstr "Kanały radyjowe tyż" + +msgctxt "#30004" +msgid "Fast channel switching (don't stop timeshift)" +msgstr "Gibkŏ pōmiana kanału (niy zastŏwiej timeshiftu)" + +msgctxt "#30005" +msgid "Connect timeout (s)" +msgstr "Limit czŏsu skuplowaniŏ (sekundy)" + +msgctxt "#30006" +msgid "Import only TV Channels from group" +msgstr "Importuj ino kanały telewizyjne ze skupiny" + +msgctxt "#30007" +msgid "Import only Radio Channels from group" +msgstr "Importuj ino kanały radyjowe ze skupiny" + +msgctxt "#30008" +msgid "Convert hostname to IP-adress" +msgstr "Przemiyń miano ôd serwera na adresã IP" + +msgctxt "#30009" +msgid "EPG: Read genre strings (slow)" +msgstr "EPG: wczytuj zorty (poleke)" + +msgctxt "#30010" +msgid "Wait time after tuning a channel (ms)" +msgstr "Czŏs czekaniŏ po przisztymowaniu kanału (ms)" + +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "Aktywuj ôkno nagrowaniŏ starych seriolōw" + +msgctxt "#30015" +msgid "Streaming method" +msgstr "Knif szpricowaniŏ" + +msgctxt "#30016" +msgid "Windows user account (SMB)" +msgstr "Miano używŏcza Windows (SMB)" + +msgctxt "#30017" +msgid "Windows password (SMB)" +msgstr "Hasło używŏcza Windows (SMB)" + +msgctxt "#30018" +msgid "Use RTSP streaming" +msgstr "Używej szpricowaniŏ RTSP" + +msgctxt "#30040" +msgid "Connection" +msgstr "Skuplowanie" + +msgctxt "#30041" +msgid "MediaPortal" +msgstr "MediaPortal" + msgctxt "#30042" msgid "Playback" msgstr "Graniy" +msgctxt "#30050" +msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" +msgstr "Przidŏwek TVServerKodi '%s' je we za staryj wersyji. Zaktualizuj go do wersyje '%s' lebo nowszyj!" + +msgctxt "#30051" +msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" +msgstr "Przidŏwek TVServerKodi je we staryj niypodpiyranyj wersyji. Zaktualizuj go do wersyje '%s\" lebo nowszyj!" + +msgctxt "#30052" +msgid "Recording playback failed. Empty URL of filename." +msgstr "Ôdtwŏrzanie nagraniŏ niy podarziło sie. Adresa URL zbioru je prōżnŏ." + +msgctxt "#30060" +msgid "All cards are busy" +msgstr "Wszyjske szkarty sōm zajynte" + +msgctxt "#30061" +msgid "Channel is scrambled" +msgstr "Kanał je zakodowany" + +msgctxt "#30062" +msgid "No video or audio detected" +msgstr "Niy ma szpricōw wideo i klangu" + +msgctxt "#30063" +msgid "No signal detected" +msgstr "Niy ma sygnału" + +msgctxt "#30064" +msgid "Unknown error" +msgstr "Niyznōmy feler" + +msgctxt "#30065" +msgid "Unable to start graph" +msgstr "Niypodarzōne sztartniyńcie diagramu" + +msgctxt "#30066" +msgid "Unknown channel" +msgstr "Niyznōmy kanał" + +msgctxt "#30067" +msgid "No tuning details" +msgstr "Niy ma datōw dostrŏjaniŏ" + +msgctxt "#30068" +msgid "Channel is not mapped to any card" +msgstr "Kanał niyma przifelezowany do żŏdnyj szkarty" + +msgctxt "#30069" +msgid "Card is disabled" +msgstr "Szkarta je niydostympnŏ" + +msgctxt "#30070" +msgid "Connection to slave failed" +msgstr "Niypodarzōne skuplowanie z posugōm" + +msgctxt "#30071" +msgid "Not the owner" +msgstr "To niyma posiedziciel" + +msgctxt "#30072" +msgid "Graph building failed" +msgstr "Stŏwianie diagramu niy powiodło sie" + +msgctxt "#30073" +msgid "SW Encoder missing" +msgstr "Niy ma dekodera programowygo" + +msgctxt "#30074" +msgid "No free disk space" +msgstr "Niy ma placu na dysku" + +msgctxt "#30075" +msgid "No PMT found" +msgstr "Niy znŏdniynto było PMT" + +msgctxt "#30100" +msgid "Schedule settings" +msgstr "Sztelōnki harmōnogramu" + +msgctxt "#30101" +msgid "Frequency" +msgstr "Programatōr" + +msgctxt "#30102" +msgid "Airtime" +msgstr "Czŏs ymisyje" + msgctxt "#30103" msgid "Channels" msgstr "Kanały" @@ -28,10 +188,82 @@ msgid "Keep" msgstr "Ôstŏw" +msgctxt "#30105" +msgid "Record minutes before start" +msgstr "Nagrowej przed ôbcyrklowanym czŏsym zaczōntku" + +msgctxt "#30106" +msgid "Record minutes after end" +msgstr "Nagrowej po ôbcyrklowanym czŏsie kōńca" + +msgctxt "#30110" +msgid "Record Once" +msgstr "Nagrej rŏz" + +msgctxt "#30111" +msgid "Record Daily (This program)" +msgstr "Nagrowej dziynnie (tyn program)" + +msgctxt "#30112" +msgid "Record Weekly" +msgstr "Nagrowej co tydziyń" + +msgctxt "#30113" +msgid "Record Weekends" +msgstr "Nagrowej we wikyndy" + +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "Nagrowej beztydziyń" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "Nagrowej dycki na tym kanale" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "Nagrowej dycki na wszyjskich kanałach" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "Nagrowej kŏżdy tydziyń ô tym czasie" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "Nagrowej dziynnie ô tym czasie" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "Nagrowej kŏżdy tydziyń na tym kanale" + +msgctxt "#30120" +msgid "This time" +msgstr "Tyn rŏz" + +msgctxt "#30121" +msgid "Anytime" +msgstr "Kej ino" + msgctxt "#30122" msgid "Manual" msgstr "Manualnie" +msgctxt "#30125" +msgid "This Channel" +msgstr "Tyn kanał" + +msgctxt "#30126" +msgid "Any Channel" +msgstr "Leda jaki kanał" + +msgctxt "#30130" +msgid "Until space needed" +msgstr "Aż braknie przestrzyństwa" + +msgctxt "#30131" +msgid "Until watched" +msgstr "Do ôbejzdrzyniŏ" + msgctxt "#30132" msgid "Days" msgstr "Dni" @@ -39,3 +271,31 @@ msgctxt "#30133" msgid "Always" msgstr "zawdy" + +msgctxt "#30134" +msgid "1 week" +msgstr "1 tydziyń" + +msgctxt "#30135" +msgid "Backend default" +msgstr "Wychodne serwera" + +msgctxt "#30136" +msgid "Kodi default" +msgstr "Wychodne dlŏ Kodi" + +msgctxt "#30137" +msgid "%d weeks" +msgstr "%d tydni" + +msgctxt "#30138" +msgid "1 month" +msgstr "1 miesiōnc" + +msgctxt "#30139" +msgid "%d months" +msgstr "%d miesiyncy" + +msgctxt "#30140" +msgid "1 year" +msgstr "1 rok" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.tr_tr/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.tr_tr/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.tr_tr/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.tr_tr/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -60,6 +60,10 @@ msgid "Wait time after tuning a channel (ms)" msgstr "Bir kanal ayarlama sonrası için bekleme zamanı (ms)" +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "Eski dizileri kaydetmeyi etkinleştir" + msgctxt "#30015" msgid "Streaming method" msgstr "Akış yöntemi" @@ -208,6 +212,30 @@ msgid "Record Weekends" msgstr "Haftasonları Kaydet" +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "Hafta içleri kaydet" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "Bu kanalda her zaman kaydet" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "Her kanalda her zaman kaydet" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "Her hafta bu zaman kaydet" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "Her gün bu zamanda kaydet" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "Bu kanalı haftalık kaydet" + msgctxt "#30120" msgid "This time" msgstr "Bu kez" @@ -232,6 +260,10 @@ msgid "Until space needed" msgstr "Boşluk gerekene kadar" +msgctxt "#30131" +msgid "Until watched" +msgstr "İzlenene dek" + msgctxt "#30132" msgid "Days" msgstr "Gün" @@ -240,6 +272,10 @@ msgid "Always" msgstr "Her zaman" +msgctxt "#30134" +msgid "1 week" +msgstr "1 hafta" + msgctxt "#30135" msgid "Backend default" msgstr "Arka uç varsayılanı" @@ -247,3 +283,19 @@ msgctxt "#30136" msgid "Kodi default" msgstr "Kodi varsayılanı" + +msgctxt "#30137" +msgid "%d weeks" +msgstr "%d hafta" + +msgctxt "#30138" +msgid "1 month" +msgstr "1 ay" + +msgctxt "#30139" +msgid "%d months" +msgstr "%d ay" + +msgctxt "#30140" +msgid "1 year" +msgstr "1 yıl" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.zh_tw/strings.po kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.zh_tw/strings.po --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/language/resource.language.zh_tw/strings.po 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/language/resource.language.zh_tw/strings.po 2017-01-11 10:28:16.000000000 +0000 @@ -26,11 +26,11 @@ msgctxt "#30002" msgid "Free-to-air only" -msgstr "只有Free-to-air" +msgstr "僅觀看免費頻道" msgctxt "#30003" msgid "Include Radio" -msgstr "包含收音機" +msgstr "包含廣播電台" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" @@ -42,11 +42,11 @@ msgctxt "#30006" msgid "Import only TV Channels from group" -msgstr "只從群組導入的電視頻道" +msgstr "僅從群組導入電視頻道" msgctxt "#30007" msgid "Import only Radio Channels from group" -msgstr "只從群組導入的廣播頻道" +msgstr "僅從群組導入廣播頻道" msgctxt "#30008" msgid "Convert hostname to IP-adress" @@ -60,6 +60,10 @@ msgid "Wait time after tuning a channel (ms)" msgstr "頻道調整後的等待時間 (毫秒)" +msgctxt "#30011" +msgid "Enable old series recording dialog" +msgstr "開啟舊系列影集的錄影對話框" + msgctxt "#30015" msgid "Streaming method" msgstr "串流方式" @@ -106,7 +110,7 @@ msgctxt "#30061" msgid "Channel is scrambled" -msgstr "頻道已加碼" +msgstr "頻道已加密" msgctxt "#30062" msgid "No video or audio detected" @@ -122,7 +126,7 @@ msgctxt "#30065" msgid "Unable to start graph" -msgstr "無法開啟圖形" +msgstr "無法啟動影像" msgctxt "#30066" msgid "Unknown channel" @@ -130,7 +134,7 @@ msgctxt "#30067" msgid "No tuning details" -msgstr "無調整的詳細訊息" +msgstr "無選台的詳細訊息" msgctxt "#30068" msgid "Channel is not mapped to any card" @@ -142,7 +146,7 @@ msgctxt "#30070" msgid "Connection to slave failed" -msgstr "連接到從屬失敗" +msgstr "連接到附屬設備失敗" msgctxt "#30071" msgid "Not the owner" @@ -150,7 +154,7 @@ msgctxt "#30072" msgid "Graph building failed" -msgstr "圖形建立失敗" +msgstr "影像建立失敗" msgctxt "#30073" msgid "SW Encoder missing" @@ -158,12 +162,16 @@ msgctxt "#30074" msgid "No free disk space" -msgstr "沒有多餘的硬碟空間" +msgstr "沒有可用的磁碟空間" msgctxt "#30075" msgid "No PMT found" msgstr "找不到PMT" +msgctxt "#30100" +msgid "Schedule settings" +msgstr "排程設定" + msgctxt "#30101" msgid "Frequency" msgstr "頻率" @@ -180,6 +188,58 @@ msgid "Keep" msgstr "保留" +msgctxt "#30105" +msgid "Record minutes before start" +msgstr "節目開始前錄影的分鐘數" + +msgctxt "#30106" +msgid "Record minutes after end" +msgstr "節目結束後錄影的分鐘數" + +msgctxt "#30110" +msgid "Record Once" +msgstr "只錄一次" + +msgctxt "#30111" +msgid "Record Daily (This program)" +msgstr "每日錄影 (本程式)" + +msgctxt "#30112" +msgid "Record Weekly" +msgstr "每週錄影" + +msgctxt "#30113" +msgid "Record Weekends" +msgstr "每週末錄影" + +msgctxt "#30114" +msgid "Record Weekdays" +msgstr "每週間錄影" + +msgctxt "#30115" +msgid "Record every time on this channel" +msgstr "每次均錄穎本頻道" + +msgctxt "#30116" +msgid "Record every time on every channel" +msgstr "每次均錄影所有頻道" + +msgctxt "#30117" +msgid "Record every week at this time" +msgstr "每週此時都錄影" + +msgctxt "#30118" +msgid "Record every day at this time" +msgstr "每天此時都錄影" + +msgctxt "#30119" +msgid "Record weekly on this channel" +msgstr "每週錄影本頻道" + +msgctxt "#30120" +msgid "This time" +msgstr "這個時間" + msgctxt "#30121" msgid "Anytime" msgstr "任何時間" @@ -196,6 +256,14 @@ msgid "Any Channel" msgstr "任何頻道" +msgctxt "#30130" +msgid "Until space needed" +msgstr "直到空間不足" + +msgctxt "#30131" +msgid "Until watched" +msgstr "直到看完" + msgctxt "#30132" msgid "Days" msgstr "天" @@ -204,6 +272,10 @@ msgid "Always" msgstr "總是" +msgctxt "#30134" +msgid "1 week" +msgstr "1週" + msgctxt "#30135" msgid "Backend default" msgstr "後端預設值" @@ -211,3 +283,19 @@ msgctxt "#30136" msgid "Kodi default" msgstr "Kodi 預設值" + +msgctxt "#30137" +msgid "%d weeks" +msgstr "%d週" + +msgctxt "#30138" +msgid "1 month" +msgstr "1個月" + +msgctxt "#30139" +msgid "%d months" +msgstr "%d月" + +msgctxt "#30140" +msgid "1 year" +msgstr "1年" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/skins/skin.estuary/1080i/DialogRecordSettings.xml kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/skins/skin.estuary/1080i/DialogRecordSettings.xml --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/skins/skin.estuary/1080i/DialogRecordSettings.xml 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/skins/skin.estuary/1080i/DialogRecordSettings.xml 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,148 @@ + + + 10 + Animation_DialogPopupOpenClose + + + 105 + 50% + 50% + 1520 + Animation_DialogPopupVisible + + + + + + + + + 10 + 80 + 1200 + 790 + buttons/dialogbutton-nofo.png + + + + 30 + 50 + + + 30 + + 100 + 720 + 30 + font13_title + + left + center + white + + + 60 + 355 + 30 + font13_title + + left + center + white + + + 400 + 60 + 355 + 30 + font13_title + + left + center + white + + + + + 160 + + 1160 + 750 + 9000 + 9000 + 23 + 23 + + + Frequency + DefaultSettingButton + 1160 + 100 + + + + + Airtime + DefaultSettingButton + 1160 + + + + + Channels + DefaultSettingButton + 1160 + + + + + separator image + 3 + dialogs/separator-grey.png + + + + Keep + DefaultSettingButton + 1160 + + + + + Record minutes before start + DefaultSettingButton + 1160 + + + + + Record minutes after end + DefaultSettingButton + 1160 + + + + + + + + 1210 + 92 + vertical + 300 + 250 + -10 + 23 + 23 + + + + + + + + + + + + diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/skins/skin.transparency/1080p/DialogRecordSettings.xml kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/skins/skin.transparency/1080p/DialogRecordSettings.xml --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/skins/skin.transparency/1080p/DialogRecordSettings.xml 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/skins/skin.transparency/1080p/DialogRecordSettings.xml 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,248 @@ + + 10 + + 352 + 52 + + dialogeffect + + + + background image + 0 + 0 + 1215 + 975 + dialog.png + + + + + 900 + 52 + 30 + 30 + close-window-focus.png + close-window-nofocus.png + PreviousMenu + Skin.HasSetting(TouchScreenMode) + + + heading label + 68 + 60 + 1065 + 45 + center + center + font-30 + + blue + + + 68 + 117 + 1065 + 3 + separator.png + + + + + + 68 + 120 + + 0 + 0 + 52 + font-20 + + white + left + center + + + 0 + 55 + 52 + font-20 + + left + center + white + + + 0 + 110 + 52 + font-20 + + left + center + white + + + + 68 + 278 + 1065 + 3 + separator.png + + + + 68 + 300 + 5 + 1065 + 290 + 9001 + 9001 + + + Frequency + 1065 + 52 + + font-20 + white + white + list-focus.png + list-nofocus.png + center + yes + 10 + 10 + + + + Airtime + 1065 + 52 + font-20 + white + white + list-focus.png + list-nofocus.png + center + yes + + 11 + 11 + + + + Channels + 1065 + 52 + font-20 + white + white + list-focus.png + list-nofocus.png + center + yes + + 12 + 12 + + + + 68 + 1065 + 3 + separator.png + + + + Keep + 1065 + 52 + font-20 + white + white + list-focus.png + list-nofocus.png + center + yes + + 13 + 13 + + + + Record minutes before start + 1065 + 52 + font-20 + white + white + list-focus.png + list-nofocus.png + center + yes + + 14 + 14 + + + + Record minutes after end + 1065 + 52 + font-20 + white + white + list-focus.png + list-nofocus.png + center + yes + + 15 + 15 + + + + + + 158 + 855 + 900 + 52 + center + horizontal + 0 + 15 + 10 + + OK button + 300 + 52 + + font-23 + lightgrey + white + center + button-focus.png + button-nofocus.png + 2 + 2 + + + Cancel button + 300 + 52 + + font-23 + lightgrey + white + center + button-focus.png + button-nofocus.png + 1 + 1 + + + + diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/skins/skin.transparency/720p/DialogRecordSettings.xml kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/skins/skin.transparency/720p/DialogRecordSettings.xml --- kodi-pvr-mediaportal-tvserver-1.13.7/pvr.mediaportal.tvserver/resources/skins/skin.transparency/720p/DialogRecordSettings.xml 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/pvr.mediaportal.tvserver/resources/skins/skin.transparency/720p/DialogRecordSettings.xml 1970-01-01 00:00:00.000000000 +0000 @@ -1,221 +0,0 @@ - - 10 - - 1 - 240 - 60 - - dialogeffect - - - - 0 - 0 - 800 - 540 - dialog.png - - - - - header label - 40 - 30 - 720 - 30 - - center - center - font-32 - blue - - - 40 - 70 - 720 - 2 - separator.png - - - dialogeffect4 - - 730 - 37 - 20 - 20 - close-window-focus.png - close-window-nofocus.png - Dialog.Close(pvrtimersetting) - Skin.HasSetting(TouchScreenMode) - - - - - - 40 - 70 - - 0 - 0 - 720 - 30 - Font_19 - - center - center - white - - - 0 - 30 - 720 - 30 - Font_19 - - center - center - white - - - 0 - 60 - 720 - 30 - Font_19 - - center - center - white - - - - 40 - 160 - 720 - 1 - separator.png - - - - 40 - 170 - 5 - 720 - 290 - 9001 - 9001 - - - Frequency - 720 - 40 - - 10 - 10 - - - - Airtime - 720 - 40 - - 11 - 11 - - - - Channels - 720 - 40 - - 12 - 12 - - - - Separator - 720 - 1 - separator.png - - - - Keep - 720 - 40 - - 13 - 13 - - - - Record minutes before start - 720 - 40 - - 14 - 14 - - - - Record minutes after end - 720 - 40 - - 15 - 15 - - - - - - 190 - 465 - 15 - 10 - horizontal - - Ok Button - 200 - 35 - font-20 - white - white - black - center - center - button-nofocus.png - button-nofocus.png - - 2 - 2 - - - Cancel Button - 200 - 35 - font-20 - white - white - black - center - center - button-nofocus.png - button-nofocus.png - - 1 - 1 - - - - 190 - 465 - 200 - 35 - button-focus.png - Conditional - Conditional - Control.HasFocus(1) | Control.HasFocus(2) - - - diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/README.md kodi-pvr-mediaportal-tvserver-2.4.16/README.md --- kodi-pvr-mediaportal-tvserver-1.13.7/README.md 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/README.md 2017-01-11 10:28:16.000000000 +0000 @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/kodi-pvr/pvr.mediaportal.tvserver.svg?branch=Jarvis)](https://travis-ci.org/kodi-pvr/pvr.mediaportal.tvserver) +[![Build Status](https://travis-ci.org/kodi-pvr/pvr.mediaportal.tvserver.svg?branch=master)](https://travis-ci.org/kodi-pvr/pvr.mediaportal.tvserver) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5120/badge.svg)](https://scan.coverity.com/projects/5120) # MediaPortal TVServer PVR diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/channels.h kodi-pvr-mediaportal-tvserver-2.4.16/src/channels.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/channels.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/channels.h 2017-01-11 10:28:16.000000000 +0000 @@ -21,9 +21,22 @@ * */ -#include "kodi/libXBMC_pvr.h" +#include "libXBMC_pvr.h" #include +namespace TvDatabase +{ + // From MediaPortal: TvDatabase.ChannelType + namespace ChannelType + { + const int Unknown = -1; //Added + const int Tv = 0; + const int Radio = 1; + const int Web = 2; + const int All = 3; + }; +} + class cChannel { private: diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/client.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/client.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/client.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/client.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -18,7 +18,7 @@ */ #include "client.h" -#include "kodi/xbmc_pvr_dll.h" +#include "xbmc_pvr_dll.h" #include "pvrclient-mediaportal.h" #include "utils.h" @@ -49,7 +49,7 @@ bool g_bUseRTSP = false; ///< Use RTSP streaming when using the tsreader /* Client member variables */ -ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN; +ADDON_STATUS m_curStatus = ADDON_STATUS_UNKNOWN; cPVRClientMediaPortal *g_client = NULL; std::string g_szUserPath = ""; std::string g_szClientPath = ""; @@ -72,7 +72,10 @@ ADDON_STATUS ADDON_Create(void* hdl, void* props) { if (!hdl || !props) - return ADDON_STATUS_UNKNOWN; + { + m_curStatus = ADDON_STATUS_UNKNOWN; + return m_curStatus; + } PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props; @@ -80,7 +83,8 @@ if (!XBMC->RegisterMe(hdl)) { SAFE_DELETE(XBMC); - return ADDON_STATUS_PERMANENT_FAILURE; + m_curStatus = ADDON_STATUS_PERMANENT_FAILURE; + return m_curStatus; } PVR = new CHelper_libXBMC_pvr; @@ -88,7 +92,8 @@ { SAFE_DELETE(PVR); SAFE_DELETE(XBMC); - return ADDON_STATUS_PERMANENT_FAILURE; + m_curStatus = ADDON_STATUS_PERMANENT_FAILURE; + return m_curStatus; } GUI = new CHelper_libKODI_guilib; @@ -97,12 +102,13 @@ SAFE_DELETE(GUI); SAFE_DELETE(PVR); SAFE_DELETE(XBMC); - return ADDON_STATUS_PERMANENT_FAILURE; + m_curStatus = ADDON_STATUS_PERMANENT_FAILURE; + return m_curStatus; } XBMC->Log(LOG_INFO, "Creating MediaPortal PVR-Client"); - m_CurStatus = ADDON_STATUS_UNKNOWN; + m_curStatus = ADDON_STATUS_UNKNOWN; g_szUserPath = pvrprops->strUserPath; g_szClientPath = pvrprops->strClientPath; @@ -111,16 +117,21 @@ /* Create connection to MediaPortal XBMC TV client */ g_client = new cPVRClientMediaPortal(); - m_CurStatus = g_client->Connect(); - if (m_CurStatus != ADDON_STATUS_OK) + m_curStatus = g_client->TryConnect(); + if (m_curStatus == ADDON_STATUS_PERMANENT_FAILURE) { SAFE_DELETE(g_client); SAFE_DELETE(GUI); SAFE_DELETE(PVR); SAFE_DELETE(XBMC); } + else if (m_curStatus == ADDON_STATUS_LOST_CONNECTION) + { + // The addon will try to reconnect, so don't show the permanent failure. + return ADDON_STATUS_OK; + } - return m_CurStatus; + return m_curStatus; } //-- Destroy ------------------------------------------------------------------ @@ -134,7 +145,7 @@ SAFE_DELETE(PVR); SAFE_DELETE(XBMC); - m_CurStatus = ADDON_STATUS_UNKNOWN; + m_curStatus = ADDON_STATUS_UNKNOWN; } //-- GetStatus ---------------------------------------------------------------- @@ -143,10 +154,10 @@ ADDON_STATUS ADDON_GetStatus() { /* check whether we're still connected */ - if (m_CurStatus == ADDON_STATUS_OK && g_client && !g_client->IsUp()) - m_CurStatus = ADDON_STATUS_LOST_CONNECTION; + if (m_curStatus == ADDON_STATUS_OK && g_client && !g_client->IsUp()) + m_curStatus = ADDON_STATUS_LOST_CONNECTION; - return m_CurStatus; + return m_curStatus; } //-- HasSettings -------------------------------------------------------------- @@ -444,14 +455,26 @@ } -void ADDON_Announce(const char* UNUSED(flag), const char* UNUSED(sender), const char* UNUSED(message), const void* UNUSED(data)) -{ -} - /*********************************************************** * PVR Client AddOn specific public library functions ***********************************************************/ +void OnSystemSleep() +{ +} + +void OnSystemWake() +{ +} + +void OnPowerSavingActivated() +{ +} + +void OnPowerSavingDeactivated() +{ +} + const char* GetPVRAPIVersion(void) { static const char *strApiVersion = XBMC_PVR_API_VERSION; @@ -813,12 +836,12 @@ return g_client->LengthLiveStream(); } -int GetCurrentClientChannel() +bool IsRealTimeStream(void) { - if (!g_client) - return 0; - else - return g_client->GetCurrentClientChannel(); + if (g_client == NULL) + return false; + + return g_client->IsRealTimeStream(); } bool SwitchChannel(const PVR_CHANNEL &channelinfo) @@ -925,7 +948,7 @@ PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY[], int*) { return PVR_ERROR_NOT_IMPLEMENTED; }; unsigned int GetChannelSwitchDelay(void) { return 0; } -bool SeekTime(int,bool,double*) { return false; } +bool SeekTime(double,bool,double*) { return false; } void SetSpeed(int) {}; time_t GetPlayingTime() { return 0; } time_t GetBufferTimeStart() { return 0; } @@ -933,4 +956,5 @@ bool IsTimeshifting(void) { return false; } PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR DeleteAllRecordingsFromTrash() { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; } } //end extern "C" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/client.h kodi-pvr-mediaportal-tvserver-2.4.16/src/client.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/client.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/client.h 2017-01-11 10:28:16.000000000 +0000 @@ -24,10 +24,10 @@ #ifndef CLIENT_H #define CLIENT_H -#include "platform/util/StdString.h" -#include "kodi/libXBMC_addon.h" -#include "kodi/libXBMC_pvr.h" -#include "kodi/libKODI_guilib.h" +#include "p8-platform/util/StdString.h" +#include "libXBMC_addon.h" +#include "libXBMC_pvr.h" +#include "libKODI_guilib.h" enum eStreamingMethod { diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/epg.h kodi-pvr-mediaportal-tvserver-2.4.16/src/epg.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/epg.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/epg.h 2017-01-11 10:28:16.000000000 +0000 @@ -26,8 +26,8 @@ #include #include -#include "kodi/libXBMC_addon.h" -#include "kodi/libXBMC_pvr.h" +#include "libXBMC_addon.h" +#include "libXBMC_pvr.h" #include "GenreTable.h" #include "DateTime.h" diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/GUIDialogRecordSettings.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/GUIDialogRecordSettings.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/GUIDialogRecordSettings.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/GUIDialogRecordSettings.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -22,10 +22,11 @@ #include "client.h" #include "GUIDialogRecordSettings.h" -#include "kodi/libKODI_guilib.h" +#include "libKODI_guilib.h" #include "timers.h" #include "utils.h" #include "DateTime.h" +#include "timers.h" /* Dialog item identifiers */ #define BUTTON_OK 1 @@ -69,11 +70,11 @@ // needed for every dialog m_retVal = -1; // init to failed load value (due to xml file not being found) - // Default skin should actually be "skin.confluence", but the fallback mechanism will only + // Default skin should actually be "skin.estuary", but the fallback mechanism will only // find the xml file and not the used image files. This will result in a transparent window // which is basically useless. Therefore, it is better to let the dialog fail by using the // incorrect fallback skin name "Confluence" - m_window = GUI->Window_create("DialogRecordSettings.xml", "Confluence", false, true); + m_window = GUI->Window_create("DialogRecordSettings.xml", "skin.estuary", false, true); if (m_window) { m_window->m_cbhdl = this; @@ -135,11 +136,45 @@ m_spinChannels->SetValue(CGUIDialogRecordSettings::ThisChannel); m_spinChannels->SetVisible(false); + // Populate Keep spin control - for (int i = 0; i < 4; i++) - { // show localized recording options - m_spinKeep->AddLabel(XBMC->GetLocalizedString(30130 + i), i); - } + m_spinKeep->AddLabel(XBMC->GetLocalizedString(30130), -MPTV_KEEP_UNTIL_SPACE_NEEDED); + m_spinKeep->AddLabel(XBMC->GetLocalizedString(30131), -MPTV_KEEP_UNTIL_WATCHED); + m_spinKeep->AddLabel(XBMC->GetLocalizedString(30133), -MPTV_KEEP_ALWAYS); + + // MediaPortal Until date replacements: + const char* strWeeks = XBMC->GetLocalizedString(30137); // %d weeks + const char* strMonths = XBMC->GetLocalizedString(30139); // %d months + const size_t cKeepStringLength = 255; + char strKeepString[cKeepStringLength]; + m_spinKeep->AddLabel(XBMC->GetLocalizedString(30134), MPTV_KEEP_ONE_WEEK); + snprintf(strKeepString, cKeepStringLength, strWeeks, 2); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_TWO_WEEKS); + snprintf(strKeepString, cKeepStringLength, strWeeks, 3); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_THREE_WEEKS); + m_spinKeep->AddLabel(XBMC->GetLocalizedString(30138), MPTV_KEEP_ONE_MONTH); + snprintf(strKeepString, cKeepStringLength, strMonths, 2); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_TWO_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 3); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_THREE_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 4); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_FOUR_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 5); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_FIVE_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 6); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_SIX_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 7); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_SEVEN_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 8); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_EIGHT_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 9); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_NINE_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 10); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_TEN_MONTHS); + snprintf(strKeepString, cKeepStringLength, strMonths, 11); + m_spinKeep->AddLabel(strKeepString, MPTV_KEEP_ELEVEN_MONTHS); + m_spinKeep->AddLabel(XBMC->GetLocalizedString(30140), MPTV_KEEP_ONE_YEAR); + // Set the default values m_spinKeep->SetValue(TvDatabase::Always); @@ -356,7 +391,7 @@ } } - m_timer.SetKeepMethod((TvDatabase::KeepMethodType) m_spinKeep->GetValue()); + m_timer.SetKeepMethod(m_spinKeep->GetValue()); m_timer.SetPreRecordInterval(m_spinPreRecord->GetValue()); m_timer.SetPostRecordInterval(m_spinPostRecord->GetValue()); } diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicHashTable.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicHashTable.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicHashTable.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicHashTable.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,277 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Basic Hash Table implementation +// Implementation + +#include "BasicHashTable.hh" +#include "strDup.hh" + +#if defined(__WIN32__) || defined(_WIN32) +#else +#include +#endif +#include +#include + +// When there are this many entries per bucket, on average, rebuild +// the table to increase the number of buckets +#define REBUILD_MULTIPLIER 3 + +BasicHashTable::BasicHashTable(int keyType) + : fBuckets(fStaticBuckets), fNumBuckets(SMALL_HASH_TABLE_SIZE), + fNumEntries(0), fRebuildSize(SMALL_HASH_TABLE_SIZE*REBUILD_MULTIPLIER), + fDownShift(28), fMask(0x3), fKeyType(keyType) { + for (unsigned i = 0; i < SMALL_HASH_TABLE_SIZE; ++i) { + fStaticBuckets[i] = NULL; + } +} + +BasicHashTable::~BasicHashTable() { + // Free all the entries in the table: + for (unsigned i = 0; i < fNumBuckets; ++i) { + TableEntry* entry; + while ((entry = fBuckets[i]) != NULL) { + deleteEntry(i, entry); + } + } + + // Also free the bucket array, if it was dynamically allocated: + if (fBuckets != fStaticBuckets) delete[] fBuckets; +} + +void* BasicHashTable::Add(char const* key, void* value) { + void* oldValue; + unsigned index; + TableEntry* entry = lookupKey(key, index); + if (entry != NULL) { + // There's already an item with this key + oldValue = entry->value; + } else { + // There's no existing entry; create a new one: + entry = insertNewEntry(index, key); + oldValue = NULL; + } + entry->value = value; + + // If the table has become too large, rebuild it with more buckets: + if (fNumEntries >= fRebuildSize) rebuild(); + + return oldValue; +} + +Boolean BasicHashTable::Remove(char const* key) { + unsigned index; + TableEntry* entry = lookupKey(key, index); + if (entry == NULL) return False; // no such entry + + deleteEntry(index, entry); + + return True; +} + +void* BasicHashTable::Lookup(char const* key) const { + unsigned index; + TableEntry* entry = lookupKey(key, index); + if (entry == NULL) return NULL; // no such entry + + return entry->value; +} + +unsigned BasicHashTable::numEntries() const { + return fNumEntries; +} + +BasicHashTable::Iterator::Iterator(BasicHashTable const& table) + : fTable(table), fNextIndex(0), fNextEntry(NULL) { +} + +void* BasicHashTable::Iterator::next(char const*& key) { + while (fNextEntry == NULL) { + if (fNextIndex >= fTable.fNumBuckets) return NULL; + + fNextEntry = fTable.fBuckets[fNextIndex++]; + } + + BasicHashTable::TableEntry* entry = fNextEntry; + fNextEntry = entry->fNext; + + key = entry->key; + return entry->value; +} + +////////// Implementation of HashTable creation functions ////////// + +HashTable* HashTable::create(int keyType) { + return new BasicHashTable(keyType); +} + +HashTable::Iterator* HashTable::Iterator::create(HashTable const& hashTable) { + // "hashTable" is assumed to be a BasicHashTable + return new BasicHashTable::Iterator((BasicHashTable const&)hashTable); +} + +////////// Implementation of internal member functions ////////// + +BasicHashTable::TableEntry* BasicHashTable +::lookupKey(char const* key, unsigned& index) const { + TableEntry* entry; + index = hashIndexFromKey(key); + + for (entry = fBuckets[index]; entry != NULL; entry = entry->fNext) { + if (keyMatches(key, entry->key)) break; + } + + return entry; +} + +Boolean BasicHashTable +::keyMatches(char const* key1, char const* key2) const { + // The way we check the keys for a match depends upon their type: + if (fKeyType == STRING_HASH_KEYS) { + return (strcmp(key1, key2) == 0); + } else if (fKeyType == ONE_WORD_HASH_KEYS) { + return (key1 == key2); + } else { + unsigned* k1 = (unsigned*)key1; + unsigned* k2 = (unsigned*)key2; + + for (int i = 0; i < fKeyType; ++i) { + if (k1[i] != k2[i]) return False; // keys differ + } + return True; + } +} + +BasicHashTable::TableEntry* BasicHashTable +::insertNewEntry(unsigned index, char const* key) { + TableEntry* entry = new TableEntry(); + entry->fNext = fBuckets[index]; + fBuckets[index] = entry; + + ++fNumEntries; + assignKey(entry, key); + + return entry; +} + +void BasicHashTable::assignKey(TableEntry* entry, char const* key) { + // The way we assign the key depends upon its type: + if (fKeyType == STRING_HASH_KEYS) { + entry->key = strDup(key); + } else if (fKeyType == ONE_WORD_HASH_KEYS) { + entry->key = key; + } else if (fKeyType > 0) { + unsigned* keyFrom = (unsigned*)key; + unsigned* keyTo = new unsigned[fKeyType]; + for (int i = 0; i < fKeyType; ++i) keyTo[i] = keyFrom[i]; + + entry->key = (char const*)keyTo; + } +} + +void BasicHashTable::deleteEntry(unsigned index, TableEntry* entry) { + TableEntry** ep = &fBuckets[index]; + + Boolean foundIt = False; + while (*ep != NULL) { + if (*ep == entry) { + foundIt = True; + *ep = entry->fNext; + break; + } + ep = &((*ep)->fNext); + } + + if (!foundIt) { // shouldn't happen +#ifdef DEBUG + fprintf(stderr, "BasicHashTable[%p]::deleteEntry(%d,%p): internal error - not found (first entry %p", this, index, entry, fBuckets[index]); + if (fBuckets[index] != NULL) fprintf(stderr, ", next entry %p", fBuckets[index]->fNext); + fprintf(stderr, ")\n"); +#endif + } + + --fNumEntries; + deleteKey(entry); + delete entry; +} + +void BasicHashTable::deleteKey(TableEntry* entry) { + // The way we delete the key depends upon its type: + if (fKeyType == ONE_WORD_HASH_KEYS) { + entry->key = NULL; + } else { + delete[] (char*)entry->key; + entry->key = NULL; + } +} + +void BasicHashTable::rebuild() { + // Remember the existing table size: + unsigned oldSize = fNumBuckets; + TableEntry** oldBuckets = fBuckets; + + // Create the new sized table: + fNumBuckets *= 4; + fBuckets = new TableEntry*[fNumBuckets]; + for (unsigned i = 0; i < fNumBuckets; ++i) { + fBuckets[i] = NULL; + } + fRebuildSize *= 4; + fDownShift -= 2; + fMask = (fMask<<2)|0x3; + + // Rehash the existing entries into the new table: + for (TableEntry** oldChainPtr = oldBuckets; oldSize > 0; + --oldSize, ++oldChainPtr) { + for (TableEntry* hPtr = *oldChainPtr; hPtr != NULL; + hPtr = *oldChainPtr) { + *oldChainPtr = hPtr->fNext; + + unsigned index = hashIndexFromKey(hPtr->key); + + hPtr->fNext = fBuckets[index]; + fBuckets[index] = hPtr; + } + } + + // Free the old bucket array, if it was dynamically allocated: + if (oldBuckets != fStaticBuckets) delete[] oldBuckets; +} + +unsigned BasicHashTable::hashIndexFromKey(char const* key) const { + unsigned result = 0; + + if (fKeyType == STRING_HASH_KEYS) { + while (1) { + char c = *key++; + if (c == 0) break; + result += (result<<3) + (unsigned)c; + } + result &= fMask; + } else if (fKeyType == ONE_WORD_HASH_KEYS) { + result = randomIndex((uintptr_t)key); + } else { + unsigned* k = (unsigned*)key; + uintptr_t sum = 0; + for (int i = 0; i < fKeyType; ++i) { + sum += k[i]; + } + result = randomIndex(sum); + } + + return result; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler0.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler0.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler0.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler0.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,175 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Basic Usage Environment: for a simple, non-scripted, console application +// Implementation + +#include "BasicUsageEnvironment0.hh" +#include "HandlerSet.hh" + +////////// A subclass of DelayQueueEntry, +////////// used to implement BasicTaskScheduler0::scheduleDelayedTask() + +class AlarmHandler: public DelayQueueEntry { +public: + AlarmHandler(TaskFunc* proc, void* clientData, DelayInterval timeToDelay) + : DelayQueueEntry(timeToDelay), fProc(proc), fClientData(clientData) { + } + +private: // redefined virtual functions + virtual void handleTimeout() { + (*fProc)(fClientData); + DelayQueueEntry::handleTimeout(); + } + +private: + TaskFunc* fProc; + void* fClientData; +}; + + +////////// BasicTaskScheduler0 ////////// + +BasicTaskScheduler0::BasicTaskScheduler0() + : fLastHandledSocketNum(-1) { + fReadHandlers = new HandlerSet; +} + +BasicTaskScheduler0::~BasicTaskScheduler0() { + delete fReadHandlers; +} + +TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds, + TaskFunc* proc, + void* clientData) { + if (microseconds < 0) microseconds = 0; + DelayInterval timeToDelay((long)(microseconds/1000000), (long)(microseconds%1000000)); + AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay); + fDelayQueue.addEntry(alarmHandler); + + return (void*)(alarmHandler->token()); +} + +void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask) { + DelayQueueEntry* alarmHandler = fDelayQueue.removeEntry((long)prevTask); + prevTask = NULL; + delete alarmHandler; +} + +void BasicTaskScheduler0::doEventLoop(char* watchVariable) { + // Repeatedly loop, handling readble sockets and timed events: + for (int i=0; i < 100;++i) + { + //if (watchVariable != NULL && *watchVariable != 0) break; + SingleStep(); + } +} + + +////////// HandlerSet (etc.) implementation ////////// + +HandlerDescriptor::HandlerDescriptor(HandlerDescriptor* nextHandler) + : socketNum(-1), + handlerProc(NULL), + clientData(NULL) +{ + // Link this descriptor into a doubly-linked list: + if (nextHandler == this) { // initialization + fNextHandler = fPrevHandler = this; + } else { + fNextHandler = nextHandler; + fPrevHandler = nextHandler->fPrevHandler; + nextHandler->fPrevHandler = this; + fPrevHandler->fNextHandler = this; + } +} + +HandlerDescriptor::~HandlerDescriptor() { + // Unlink this descriptor from a doubly-linked list: + fNextHandler->fPrevHandler = fPrevHandler; + fPrevHandler->fNextHandler = fNextHandler; +} + +HandlerSet::HandlerSet() + : fHandlers(&fHandlers) { + fHandlers.socketNum = -1; // shouldn't ever get looked at, but in case... +} + +HandlerSet::~HandlerSet() { + // Delete each handler descriptor: + while (fHandlers.fNextHandler != &fHandlers) { + delete fHandlers.fNextHandler; // changes fHandlers->fNextHandler + } +} + +void HandlerSet +::assignHandler(int socketNum, + TaskScheduler::BackgroundHandlerProc* handlerProc, + void* clientData) { + // First, see if there's already a handler for this socket: + HandlerDescriptor* handler = lookupHandler(socketNum); + if (handler == NULL) { // No existing handler, so create a new descr: + handler = new HandlerDescriptor(fHandlers.fNextHandler); + handler->socketNum = socketNum; + } + + handler->handlerProc = handlerProc; + handler->clientData = clientData; +} + +void HandlerSet::removeHandler(int socketNum) { + HandlerDescriptor* handler = lookupHandler(socketNum); + delete handler; +} + +void HandlerSet::moveHandler(int oldSocketNum, int newSocketNum) { + HandlerDescriptor* handler = lookupHandler(oldSocketNum); + if (handler != NULL) { + handler->socketNum = newSocketNum; + } +} + +HandlerDescriptor* HandlerSet::lookupHandler(int socketNum) { + HandlerDescriptor* handler; + HandlerIterator iter(*this); + while ((handler = iter.next()) != NULL) { + if (handler->socketNum == socketNum) break; + } + return handler; +} + +HandlerIterator::HandlerIterator(HandlerSet& handlerSet) + : fOurSet(handlerSet) { + reset(); +} + +HandlerIterator::~HandlerIterator() { +} + +void HandlerIterator::reset() { + fNextPtr = fOurSet.fHandlers.fNextHandler; +} + +HandlerDescriptor* HandlerIterator::next() { + HandlerDescriptor* result = fNextPtr; + if (result == &fOurSet.fHandlers) { // no more + result = NULL; + } else { + fNextPtr = fNextPtr->fNextHandler; + } + + return result; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,176 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Basic Usage Environment: for a simple, non-scripted, console application +// Implementation + + +#include "BasicUsageEnvironment.hh" +#include "HandlerSet.hh" +#include +#if defined(_QNX4) +#include +#include +#endif + +////////// BasicTaskScheduler ////////// + +BasicTaskScheduler* BasicTaskScheduler::createNew() { + return new BasicTaskScheduler(); +} + +BasicTaskScheduler::BasicTaskScheduler() + : fMaxNumSockets(0) { + FD_ZERO(&fReadSet); +} + +BasicTaskScheduler::~BasicTaskScheduler() { +} + +#ifndef MILLION +#define MILLION 1000000 +#endif + +void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) { + fd_set readSet = fReadSet; // make a copy for this select() call + + DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm(); + struct timeval tv_timeToDelay; + tv_timeToDelay.tv_sec = timeToDelay.seconds(); + tv_timeToDelay.tv_usec = timeToDelay.useconds(); + // Very large "tv_sec" values cause select() to fail. + // Don't make it any larger than 1 million seconds (11.5 days) + const long MAX_TV_SEC = MILLION; + if (tv_timeToDelay.tv_sec > MAX_TV_SEC) { + tv_timeToDelay.tv_sec = MAX_TV_SEC; + } + // Also check our "maxDelayTime" parameter (if it's > 0): + if (maxDelayTime > 0 && + (tv_timeToDelay.tv_sec > (long)maxDelayTime/MILLION || + (tv_timeToDelay.tv_sec == (long)maxDelayTime/MILLION && + tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) { + tv_timeToDelay.tv_sec = maxDelayTime/MILLION; + tv_timeToDelay.tv_usec = maxDelayTime%MILLION; + } + + int selectResult = select(fMaxNumSockets, &readSet, NULL, NULL, + &tv_timeToDelay); + if (selectResult < 0) { +#if defined(__WIN32__) || defined(_WIN32) + int err = WSAGetLastError(); + // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if + // it was called with no entries set in "readSet". If this happens, ignore it: + if (err == WSAEINVAL && readSet.fd_count == 0) { + err = EINTR; + // To stop this from happening again, create a dummy socket: + int dummySocketNum = socket(AF_INET, SOCK_DGRAM, 0); + FD_SET((unsigned)dummySocketNum, &fReadSet); + } + if (err != EINTR) { +#else + if (errno != EINTR && errno != EAGAIN) { +#endif + // Unexpected error - treat this as fatal: +#if !defined(_WIN32_WCE) + perror("BasicTaskScheduler::SingleStep(): select() fails"); +#endif + internalError(); + } + } + + // Call the handler function for one readable socket: + HandlerIterator iter(*fReadHandlers); + HandlerDescriptor* handler; + // To ensure forward progress through the handlers, begin past the last + // socket number that we handled: + if (fLastHandledSocketNum >= 0) { + while ((handler = iter.next()) != NULL) { + if (handler->socketNum == fLastHandledSocketNum) break; + } + if (handler == NULL) { + fLastHandledSocketNum = -1; + iter.reset(); // start from the beginning instead + } + } + while ((handler = iter.next()) != NULL) { + if (FD_ISSET(handler->socketNum, &readSet) && + FD_ISSET(handler->socketNum, &fReadSet) /* sanity check */ && + handler->handlerProc != NULL) { + fLastHandledSocketNum = handler->socketNum; + // Note: we set "fLastHandledSocketNum" before calling the handler, + // in case the handler calls "doEventLoop()" reentrantly. + (*handler->handlerProc)(handler->clientData, SOCKET_READABLE); + break; + } + } + if (handler == NULL && fLastHandledSocketNum >= 0) { + // We didn't call a handler, but we didn't get to check all of them, + // so try again from the beginning: + iter.reset(); + while ((handler = iter.next()) != NULL) { + if (FD_ISSET(handler->socketNum, &readSet) && + FD_ISSET(handler->socketNum, &fReadSet) /* sanity check */ && + handler->handlerProc != NULL) { + fLastHandledSocketNum = handler->socketNum; + // Note: we set "fLastHandledSocketNum" before calling the handler, + // in case the handler calls "doEventLoop()" reentrantly. + (*handler->handlerProc)(handler->clientData, SOCKET_READABLE); + break; + } + } + if (handler == NULL) fLastHandledSocketNum = -1;//because we didn't call a handler + } + + // Also handle any delayed event that may have come due. (Note that we do this *after* calling a socket + // handler, in case the delayed event handler modifies the set of readable socket.) + fDelayQueue.handleAlarm(); +} + +void BasicTaskScheduler::turnOnBackgroundReadHandling(int socketNum, + BackgroundHandlerProc* handlerProc, + void* clientData) { + if (socketNum < 0) return; + fReadHandlers->assignHandler(socketNum, handlerProc, clientData); + FD_SET((unsigned)socketNum, &fReadSet); + + if (socketNum+1 > fMaxNumSockets) { + fMaxNumSockets = socketNum+1; + } +} + +void BasicTaskScheduler::turnOffBackgroundReadHandling(int socketNum) { + if (socketNum < 0) return; + FD_CLR((unsigned)socketNum, &fReadSet); + fReadHandlers->removeHandler(socketNum); + + if (socketNum+1 == fMaxNumSockets) { + --fMaxNumSockets; + } +} + +void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) { + if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check + FD_CLR((unsigned)oldSocketNum, &fReadSet); + fReadHandlers->moveHandler(oldSocketNum, newSocketNum); + FD_SET((unsigned)newSocketNum, &fReadSet); + + if (oldSocketNum+1 == fMaxNumSockets) { + --fMaxNumSockets; + } + if (newSocketNum+1 > fMaxNumSockets) { + fMaxNumSockets = newSocketNum+1; + } +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment0.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment0.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment0.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment0.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,88 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Basic Usage Environment: for a simple, non-scripted, console application +// Implementation + +#include "BasicUsageEnvironment0.hh" +#include + +////////// BasicUsageEnvironment ////////// + +BasicUsageEnvironment0::BasicUsageEnvironment0(TaskScheduler& taskScheduler) + : UsageEnvironment(taskScheduler), + fBufferMaxSize(RESULT_MSG_BUFFER_MAX) { + reset(); +} + +BasicUsageEnvironment0::~BasicUsageEnvironment0() { +} + +void BasicUsageEnvironment0::reset() { + fCurBufferSize = 0; + fResultMsgBuffer[fCurBufferSize] = '\0'; +} + + +// Implementation of virtual functions: + +char const* BasicUsageEnvironment0::getResultMsg() const { + return fResultMsgBuffer; +} + +void BasicUsageEnvironment0::setResultMsg(MsgString msg) { + reset(); + appendToResultMsg(msg); +} + +void BasicUsageEnvironment0::setResultMsg(MsgString msg1, MsgString msg2) { + setResultMsg(msg1); + appendToResultMsg(msg2); +} + +void BasicUsageEnvironment0::setResultMsg(MsgString msg1, MsgString msg2, + MsgString msg3) { + setResultMsg(msg1, msg2); + appendToResultMsg(msg3); +} + +void BasicUsageEnvironment0::setResultErrMsg(MsgString msg, int err) { + setResultMsg(msg); + +#ifndef _WIN32_WCE + appendToResultMsg(strerror(err == 0 ? getErrno() : err)); +#endif +} + +void BasicUsageEnvironment0::appendToResultMsg(MsgString msg) { + char* curPtr = &fResultMsgBuffer[fCurBufferSize]; + unsigned spaceAvailable = fBufferMaxSize - fCurBufferSize; + unsigned msgLength = strlen(msg); + + // Copy only enough of "msg" as will fit: + if (msgLength > spaceAvailable-1) { + msgLength = spaceAvailable-1; + } + + memmove(curPtr, (char*)msg, msgLength); + fCurBufferSize += msgLength; + fResultMsgBuffer[fCurBufferSize] = '\0'; +} + +void BasicUsageEnvironment0::reportBackgroundError() { + fputs(getResultMsg(), stderr); +} + diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,80 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Basic Usage Environment: for a simple, non-scripted, console application +// Implementation + +#include "BasicUsageEnvironment.hh" +#include + +////////// BasicUsageEnvironment ////////// + +#if defined(__WIN32__) || defined(_WIN32) +extern "C" int initializeWinsockIfNecessary(); +#endif + +BasicUsageEnvironment::BasicUsageEnvironment(TaskScheduler& taskScheduler) +: BasicUsageEnvironment0(taskScheduler) { +#if defined(__WIN32__) || defined(_WIN32) + if (!initializeWinsockIfNecessary()) { + setResultErrMsg("Failed to initialize 'winsock': "); + reportBackgroundError(); + abort(); + } +#endif +} + +BasicUsageEnvironment::~BasicUsageEnvironment() { +} + +BasicUsageEnvironment* +BasicUsageEnvironment::createNew(TaskScheduler& taskScheduler) { + return new BasicUsageEnvironment(taskScheduler); +} + +int BasicUsageEnvironment::getErrno() const { +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE) + return WSAGetLastError(); +#else + return errno; +#endif +} + +UsageEnvironment& BasicUsageEnvironment::operator<<(char const* str) { + if (str == NULL) str = "(NULL)"; // sanity check + fprintf(stderr, "%s", str); + return *this; +} + +UsageEnvironment& BasicUsageEnvironment::operator<<(int i) { + fprintf(stderr, "%d", i); + return *this; +} + +UsageEnvironment& BasicUsageEnvironment::operator<<(unsigned u) { + fprintf(stderr, "%u", u); + return *this; +} + +UsageEnvironment& BasicUsageEnvironment::operator<<(double d) { + fprintf(stderr, "%f", d); + return *this; +} + +UsageEnvironment& BasicUsageEnvironment::operator<<(void* p) { + fprintf(stderr, "%p", p); + return *this; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/COPYING kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/COPYING --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/COPYING 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/COPYING 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/DelayQueue.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/DelayQueue.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/DelayQueue.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/DelayQueue.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,233 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved +// Help by Carlo Bonamico to get working for Windows +// Delay queue +// Implementation + +#include "DelayQueue.hh" +#include "GroupsockHelper.hh" + +static const int MILLION = 1000000; + +///// Timeval ///// + +int Timeval::operator>=(const Timeval& arg2) const { + return seconds() > arg2.seconds() + || (seconds() == arg2.seconds() + && useconds() >= arg2.useconds()); +} + +void Timeval::operator+=(const DelayInterval& arg2) { + secs() += arg2.seconds(); usecs() += arg2.useconds(); + if (useconds() >= MILLION) { + usecs() -= MILLION; + ++secs(); + } +} + +void Timeval::operator-=(const DelayInterval& arg2) { + secs() -= arg2.seconds(); usecs() -= arg2.useconds(); + if ((int)useconds() < 0) { + usecs() += MILLION; + --secs(); + } + if ((int)seconds() < 0) + secs() = usecs() = 0; + +} + +DelayInterval operator-(const Timeval& arg1, const Timeval& arg2) { + time_base_seconds secs = arg1.seconds() - arg2.seconds(); + time_base_seconds usecs = arg1.useconds() - arg2.useconds(); + + if ((int)usecs < 0) { + usecs += MILLION; + --secs; + } + if ((int)secs < 0) + return DELAY_ZERO; + else + return DelayInterval(secs, usecs); +} + + +///// DelayInterval ///// + +DelayInterval operator*(short arg1, const DelayInterval& arg2) { + time_base_seconds result_seconds = arg1*arg2.seconds(); + time_base_seconds result_useconds = arg1*arg2.useconds(); + + time_base_seconds carry = result_useconds/MILLION; + result_useconds -= carry*MILLION; + result_seconds += carry; + + return DelayInterval(result_seconds, result_useconds); +} + +#ifndef INT_MAX +#define INT_MAX 0x7FFFFFFF +#endif +const DelayInterval DELAY_ZERO(0, 0); +const DelayInterval DELAY_SECOND(1, 0); +const DelayInterval DELAY_MINUTE = 60*DELAY_SECOND; +const DelayInterval DELAY_HOUR = 60*DELAY_MINUTE; +const DelayInterval DELAY_DAY = 24*DELAY_HOUR; +const DelayInterval ETERNITY(INT_MAX, MILLION-1); +// used internally to make the implementation work + + +///// DelayQueueEntry ///// + +intptr_t DelayQueueEntry::tokenCounter = 0; + +DelayQueueEntry::DelayQueueEntry(DelayInterval delay) + : fDeltaTimeRemaining(delay) { + fNext = fPrev = this; + fToken = ++tokenCounter; +} + +DelayQueueEntry::~DelayQueueEntry() { +} + +void DelayQueueEntry::handleTimeout() { + delete this; +} + + +///// DelayQueue ///// + +DelayQueue::DelayQueue() + : DelayQueueEntry(ETERNITY) { + fLastSyncTime = TimeNow(); +} + +DelayQueue::~DelayQueue() { + while (fNext != this) { + DelayQueueEntry* entryToRemove = fNext; + removeEntry(entryToRemove); + delete entryToRemove; + } +} + +void DelayQueue::addEntry(DelayQueueEntry* newEntry) { + synchronize(); + + DelayQueueEntry* cur = head(); + while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining) { + newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining; + cur = cur->fNext; + } + + cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining; + + // Add "newEntry" to the queue, just before "cur": + newEntry->fNext = cur; + newEntry->fPrev = cur->fPrev; + cur->fPrev = newEntry->fPrev->fNext = newEntry; +} + +void DelayQueue::updateEntry(DelayQueueEntry* entry, DelayInterval newDelay) { + if (entry == NULL) return; + + removeEntry(entry); + entry->fDeltaTimeRemaining = newDelay; + addEntry(entry); +} + +void DelayQueue::updateEntry(intptr_t tokenToFind, DelayInterval newDelay) { + DelayQueueEntry* entry = findEntryByToken(tokenToFind); + updateEntry(entry, newDelay); +} + +void DelayQueue::removeEntry(DelayQueueEntry* entry) { + if (entry == NULL || entry->fNext == NULL) return; + + entry->fNext->fDeltaTimeRemaining += entry->fDeltaTimeRemaining; + entry->fPrev->fNext = entry->fNext; + entry->fNext->fPrev = entry->fPrev; + entry->fNext = entry->fPrev = NULL; + // in case we should try to remove it again +} + +DelayQueueEntry* DelayQueue::removeEntry(intptr_t tokenToFind) { + DelayQueueEntry* entry = findEntryByToken(tokenToFind); + removeEntry(entry); + return entry; +} + +DelayInterval const& DelayQueue::timeToNextAlarm() { + if (head()->fDeltaTimeRemaining == DELAY_ZERO) return DELAY_ZERO; // a common case + + synchronize(); + return head()->fDeltaTimeRemaining; +} + +void DelayQueue::handleAlarm() { + if (head()->fDeltaTimeRemaining != DELAY_ZERO) synchronize(); + + if (head()->fDeltaTimeRemaining == DELAY_ZERO) { + // This event is due to be handled: + DelayQueueEntry* toRemove = head(); + removeEntry(toRemove); // do this first, in case handler accesses queue + + toRemove->handleTimeout(); + } +} + +DelayQueueEntry* DelayQueue::findEntryByToken(intptr_t tokenToFind) { + DelayQueueEntry* cur = head(); + while (cur != this) { + if (cur->token() == tokenToFind) return cur; + cur = cur->fNext; + } + + return NULL; +} + +void DelayQueue::synchronize() { + // First, figure out how much time has elapsed since the last sync: + _EventTime timeNow = TimeNow(); + if (timeNow < fLastSyncTime) { + // The system clock has apparently gone back in time; reset our sync time and return: + fLastSyncTime = timeNow; + return; + } + DelayInterval timeSinceLastSync = timeNow - fLastSyncTime; + fLastSyncTime = timeNow; + + // Then, adjust the delay queue for any entries whose time is up: + DelayQueueEntry* curEntry = head(); + while (timeSinceLastSync >= curEntry->fDeltaTimeRemaining) { + timeSinceLastSync -= curEntry->fDeltaTimeRemaining; + curEntry->fDeltaTimeRemaining = DELAY_ZERO; + curEntry = curEntry->fNext; + } + curEntry->fDeltaTimeRemaining -= timeSinceLastSync; +} + + +///// _EventTime ///// + +_EventTime TimeNow() { + struct timeval tvNow; + + gettimeofday(&tvNow, NULL); + + return _EventTime(tvNow.tv_sec, tvNow.tv_usec); +} + +const _EventTime THE_END_OF_TIME(INT_MAX); diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/BasicHashTable.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/BasicHashTable.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/BasicHashTable.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/BasicHashTable.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,104 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Basic Hash Table implementation +// C++ header + +#ifndef _BASIC_HASH_TABLE_HH +#define _BASIC_HASH_TABLE_HH + +#ifndef _HASH_TABLE_HH +#include "HashTable.hh" +#endif +#ifndef _NET_COMMON_H +#include // to ensure that "uintptr_t" is defined +#endif + +// A simple hash table implementation, inspired by the hash table +// implementation used in Tcl 7.6: + +#define SMALL_HASH_TABLE_SIZE 4 + +class BasicHashTable: public HashTable { +private: + class TableEntry; // forward + +public: + BasicHashTable(int keyType); + virtual ~BasicHashTable(); + + // Used to iterate through the members of the table: + class Iterator; friend class Iterator; // to make Sun's C++ compiler happy + class Iterator: public HashTable::Iterator { + public: + Iterator(BasicHashTable const& table); + + private: // implementation of inherited pure virtual functions + void* next(char const*& key); // returns 0 if none + + private: + BasicHashTable const& fTable; + unsigned fNextIndex; // index of next bucket to be enumerated after this + TableEntry* fNextEntry; // next entry in the current bucket + }; + +private: // implementation of inherited pure virtual functions + virtual void* Add(char const* key, void* value); + // Returns the old value if different, otherwise 0 + virtual Boolean Remove(char const* key); + virtual void* Lookup(char const* key) const; + // Returns 0 if not found + virtual unsigned numEntries() const; + +private: + class TableEntry { + public: + TableEntry* fNext; + char const* key; + void* value; + }; + + TableEntry* lookupKey(char const* key, unsigned& index) const; + // returns entry matching "key", or NULL if none + Boolean keyMatches(char const* key1, char const* key2) const; + // used to implement "lookupKey()" + + TableEntry* insertNewEntry(unsigned index, char const* key); + // creates a new entry, and inserts it in the table + void assignKey(TableEntry* entry, char const* key); + // used to implement "insertNewEntry()" + + void deleteEntry(unsigned index, TableEntry* entry); + void deleteKey(TableEntry* entry); + // used to implement "deleteEntry()" + + void rebuild(); // rebuilds the table as its size increases + + unsigned hashIndexFromKey(char const* key) const; + // used to implement many of the routines above + + unsigned randomIndex(uintptr_t i) const { + return (unsigned)(((i*1103515245) >> fDownShift) & fMask); + } + +private: + TableEntry** fBuckets; // pointer to bucket array + TableEntry* fStaticBuckets[SMALL_HASH_TABLE_SIZE];// used for small tables + unsigned fNumBuckets, fNumEntries, fRebuildSize, fDownShift, fMask; + int fKeyType; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,101 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Basic Usage Environment: for a simple, non-scripted, console application +// C++ header + +#ifndef _BASIC_USAGE_ENVIRONMENT0_HH +#define _BASIC_USAGE_ENVIRONMENT0_HH + +#ifndef _BASICUSAGEENVIRONMENT_VERSION_HH +#include "BasicUsageEnvironment_version.hh" +#endif + +#ifndef _USAGE_ENVIRONMENT_HH +#include "UsageEnvironment.hh" +#endif + +#ifndef _DELAY_QUEUE_HH +#include "DelayQueue.hh" +#endif + +#define RESULT_MSG_BUFFER_MAX 1000 + +// An abstract base class, useful for subclassing +// (e.g., to redefine the implementation of "operator<<") +class BasicUsageEnvironment0: public UsageEnvironment { +public: + // redefined virtual functions: + virtual MsgString getResultMsg() const; + + virtual void setResultMsg(MsgString msg); + virtual void setResultMsg(MsgString msg1, + MsgString msg2); + virtual void setResultMsg(MsgString msg1, + MsgString msg2, + MsgString msg3); + virtual void setResultErrMsg(MsgString msg, int err = 0); + + virtual void appendToResultMsg(MsgString msg); + + virtual void reportBackgroundError(); + +protected: + BasicUsageEnvironment0(TaskScheduler& taskScheduler); + virtual ~BasicUsageEnvironment0(); + +private: + void reset(); + + char fResultMsgBuffer[RESULT_MSG_BUFFER_MAX]; + unsigned fCurBufferSize; + unsigned fBufferMaxSize; +}; + +class HandlerSet; // forward + +// An abstract base class, useful for subclassing +// (e.g., to redefine the implementation of socket event handling) +class BasicTaskScheduler0: public TaskScheduler { +public: + virtual ~BasicTaskScheduler0(); + + virtual void SingleStep(unsigned maxDelayTime = 0) = 0; + // "maxDelayTime" is in microseconds. It allows a subclass to impose a limit + // on how long "select()" can delay, in case it wants to also do polling. + // 0 (the default value) means: There's no maximum; just look at the delay queue + +public: + // Redefined virtual functions: + virtual TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc, + void* clientData); + virtual void unscheduleDelayedTask(TaskToken& prevTask); + + virtual void doEventLoop(char* watchVariable); + +protected: + BasicTaskScheduler0(); + +protected: + // To implement delayed operations: + DelayQueue fDelayQueue; + + // To implement background reads: + HandlerSet* fReadHandlers; + int fLastHandledSocketNum; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,72 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Basic Usage Environment: for a simple, non-scripted, console application +// C++ header + +#ifndef _BASIC_USAGE_ENVIRONMENT_HH +#define _BASIC_USAGE_ENVIRONMENT_HH + +#ifndef _BASIC_USAGE_ENVIRONMENT0_HH +#include "BasicUsageEnvironment0.hh" +#endif + +class BasicUsageEnvironment: public BasicUsageEnvironment0 { +public: + static BasicUsageEnvironment* createNew(TaskScheduler& taskScheduler); + + // redefined virtual functions: + virtual int getErrno() const; + + virtual UsageEnvironment& operator<<(char const* str); + virtual UsageEnvironment& operator<<(int i); + virtual UsageEnvironment& operator<<(unsigned u); + virtual UsageEnvironment& operator<<(double d); + virtual UsageEnvironment& operator<<(void* p); + +protected: + BasicUsageEnvironment(TaskScheduler& taskScheduler); + // called only by "createNew()" (or subclass constructors) + virtual ~BasicUsageEnvironment(); +}; + + +class BasicTaskScheduler: public BasicTaskScheduler0 { +public: + static BasicTaskScheduler* createNew(); + virtual ~BasicTaskScheduler(); + +protected: + BasicTaskScheduler(); + // called only by "createNew()" + +protected: + // Redefined virtual functions: + virtual void SingleStep(unsigned maxDelayTime); + + virtual void turnOnBackgroundReadHandling(int socketNum, + BackgroundHandlerProc* handlerProc, + void* clientData); + virtual void turnOffBackgroundReadHandling(int socketNum); + virtual void moveSocketHandling(int oldSocketNum, int newSocketNum); + +protected: + // To implement background reads: + int fMaxNumSockets; + fd_set fReadSet; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,10 @@ +// Version information for the "BasicUsageEnvironment" library +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. + +#ifndef _BASICUSAGEENVIRONMENT_VERSION_HH +#define _BASICUSAGEENVIRONMENT_VERSION_HH + +#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_STRING "2010.03.16" +#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT 1268697600 + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/DelayQueue.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/DelayQueue.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/DelayQueue.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/DelayQueue.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,182 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ + // Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved +// Delay queue +// C++ header + +#ifndef _DELAY_QUEUE_HH +#define _DELAY_QUEUE_HH + +#ifndef _NET_COMMON_H +#include "NetCommon.h" +#endif + +#ifdef TIME_BASE +typedef TIME_BASE time_base_seconds; +#else +typedef long time_base_seconds; +#endif + +///// A "Timeval" can be either an absolute time, or a time interval ///// + +class Timeval { +public: + time_base_seconds seconds() const { + return fTv.tv_sec; + } + time_base_seconds seconds() { + return fTv.tv_sec; + } + time_base_seconds useconds() const { + return fTv.tv_usec; + } + time_base_seconds useconds() { + return fTv.tv_usec; + } + + int operator>=(Timeval const& arg2) const; + int operator<=(Timeval const& arg2) const { + return arg2 >= *this; + } + int operator<(Timeval const& arg2) const { + return !(*this >= arg2); + } + int operator>(Timeval const& arg2) const { + return arg2 < *this; + } + int operator==(Timeval const& arg2) const { + return *this >= arg2 && arg2 >= *this; + } + int operator!=(Timeval const& arg2) const { + return !(*this == arg2); + } + + void operator+=(class DelayInterval const& arg2); + void operator-=(class DelayInterval const& arg2); + // returns ZERO iff arg2 >= arg1 + +protected: + Timeval(time_base_seconds seconds, time_base_seconds useconds) { + fTv.tv_sec = seconds; fTv.tv_usec = useconds; + } + +private: + time_base_seconds& secs() { + return (time_base_seconds&)fTv.tv_sec; + } + time_base_seconds& usecs() { + return (time_base_seconds&)fTv.tv_usec; + } + + struct timeval fTv; +}; + +#ifndef max +inline Timeval max(Timeval const& arg1, Timeval const& arg2) { + return arg1 >= arg2 ? arg1 : arg2; +} +#endif +#ifndef min +inline Timeval min(Timeval const& arg1, Timeval const& arg2) { + return arg1 <= arg2 ? arg1 : arg2; +} +#endif + +class DelayInterval operator-(Timeval const& arg1, Timeval const& arg2); +// returns ZERO iff arg2 >= arg1 + + +///// DelayInterval ///// + +class DelayInterval: public Timeval { +public: + DelayInterval(time_base_seconds seconds, time_base_seconds useconds) + : Timeval(seconds, useconds) {} +}; + +DelayInterval operator*(short arg1, DelayInterval const& arg2); + +extern DelayInterval const DELAY_ZERO; +extern DelayInterval const DELAY_SECOND; +extern DelayInterval const DELAY_MINUTE; +extern DelayInterval const DELAY_HOUR; +extern DelayInterval const DELAY_DAY; + +///// _EventTime ///// + +class _EventTime: public Timeval { +public: + _EventTime(unsigned secondsSinceEpoch = 0, + unsigned usecondsSinceEpoch = 0) + // We use the Unix standard epoch: January 1, 1970 + : Timeval(secondsSinceEpoch, usecondsSinceEpoch) {} +}; + +_EventTime TimeNow(); + +extern _EventTime const THE_END_OF_TIME; + + +///// DelayQueueEntry ///// + +class DelayQueueEntry { +public: + virtual ~DelayQueueEntry(); + + intptr_t token() { + return fToken; + } + +protected: // abstract base class + DelayQueueEntry(DelayInterval delay); + + virtual void handleTimeout(); + +private: + friend class DelayQueue; + DelayQueueEntry* fNext; + DelayQueueEntry* fPrev; + DelayInterval fDeltaTimeRemaining; + + intptr_t fToken; + static intptr_t tokenCounter; +}; + +///// DelayQueue ///// + +class DelayQueue: public DelayQueueEntry { +public: + DelayQueue(); + virtual ~DelayQueue(); + + void addEntry(DelayQueueEntry* newEntry); // returns a token for the entry + void updateEntry(DelayQueueEntry* entry, DelayInterval newDelay); + void updateEntry(intptr_t tokenToFind, DelayInterval newDelay); + void removeEntry(DelayQueueEntry* entry); // but doesn't delete it + DelayQueueEntry* removeEntry(intptr_t tokenToFind); // but doesn't delete it + + DelayInterval const& timeToNextAlarm(); + void handleAlarm(); + +private: + DelayQueueEntry* head() { return fNext; } + DelayQueueEntry* findEntryByToken(intptr_t token); + void synchronize(); // bring the 'time remaining' fields up-to-date + + _EventTime fLastSyncTime; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/HandlerSet.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/HandlerSet.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/BasicUsageEnvironment/include/HandlerSet.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/BasicUsageEnvironment/include/HandlerSet.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,74 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Basic Usage Environment: for a simple, non-scripted, console application +// C++ header + +#ifndef _HANDLER_SET_HH +#define _HANDLER_SET_HH + +////////// HandlerSet (etc.) definition ////////// + +class HandlerDescriptor { + HandlerDescriptor(HandlerDescriptor* nextHandler); + virtual ~HandlerDescriptor(); + +public: + int socketNum; + TaskScheduler::BackgroundHandlerProc* handlerProc; + void* clientData; + +private: + // Descriptors are linked together in a doubly-linked list: + friend class HandlerSet; + friend class HandlerIterator; + HandlerDescriptor* fNextHandler; + HandlerDescriptor* fPrevHandler; +}; + +class HandlerSet { +public: + HandlerSet(); + virtual ~HandlerSet(); + + void assignHandler(int socketNum, + TaskScheduler::BackgroundHandlerProc* handlerProc, + void* clientData); + void removeHandler(int socketNum); + void moveHandler(int oldSocketNum, int newSocketNum); + +private: + HandlerDescriptor* lookupHandler(int socketNum); + +private: + friend class HandlerIterator; + HandlerDescriptor fHandlers; +}; + +class HandlerIterator { +public: + HandlerIterator(HandlerSet& handlerSet); + virtual ~HandlerIterator(); + + HandlerDescriptor* next(); // returns NULL if none + void reset(); + +private: + HandlerSet& fOurSet; + HandlerDescriptor* fNextPtr; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/COPYING kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/COPYING --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/COPYING 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/COPYING 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/GroupEId.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/GroupEId.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/GroupEId.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/GroupEId.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,105 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2012, Live Networks, Inc. All rights reserved +// "Group Endpoint Id" +// Implementation + +#include "GroupEId.hh" +#include "strDup.hh" +#include + +////////// Scope ////////// + +void Scope::assign(u_int8_t ttl, const char* publicKey) { + fTTL = ttl; + + fPublicKey = strDup(publicKey == NULL ? "nokey" : publicKey); +} + +void Scope::clean() { + delete[] fPublicKey; + fPublicKey = NULL; +} + + +Scope::Scope(u_int8_t ttl, const char* publicKey) { + assign(ttl, publicKey); +} + +Scope::Scope(const Scope& orig) { + assign(orig.ttl(), orig.publicKey()); +} + +Scope& Scope::operator=(const Scope& rightSide) { + if (&rightSide != this) { + if (publicKey() == NULL + || strcmp(publicKey(), rightSide.publicKey()) != 0) { + clean(); + assign(rightSide.ttl(), rightSide.publicKey()); + } else { // need to assign TTL only + fTTL = rightSide.ttl(); + } + } + + return *this; +} + +Scope::~Scope() { + clean(); +} + +unsigned Scope::publicKeySize() const { + return fPublicKey == NULL ? 0 : strlen(fPublicKey); +} + +////////// GroupEId ////////// + +GroupEId::GroupEId(struct in_addr const& groupAddr, + portNumBits portNum, Scope const& scope, + unsigned numSuccessiveGroupAddrs) { + struct in_addr sourceFilterAddr; + sourceFilterAddr.s_addr = ~0; // indicates no source filter + + init(groupAddr, sourceFilterAddr, portNum, scope, numSuccessiveGroupAddrs); +} + +GroupEId::GroupEId(struct in_addr const& groupAddr, + struct in_addr const& sourceFilterAddr, + portNumBits portNum, + unsigned numSuccessiveGroupAddrs) { + init(groupAddr, sourceFilterAddr, portNum, 255, numSuccessiveGroupAddrs); +} + +// margro: removed to fix Coverity CID 135519 +//GroupEId::GroupEId() { +//} + +Boolean GroupEId::isSSM() const { + return fSourceFilterAddress.s_addr != netAddressBits(~0); +} + + +void GroupEId::init(struct in_addr const& groupAddr, + struct in_addr const& sourceFilterAddr, + portNumBits portNum, + Scope const& scope, + unsigned numSuccessiveGroupAddrs) { + fGroupAddress = groupAddr; + fSourceFilterAddress = sourceFilterAddr; + fNumSuccessiveGroupAddrs = numSuccessiveGroupAddrs; + fPortNum = portNum; + fScope = scope; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/Groupsock.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/Groupsock.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/Groupsock.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/Groupsock.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,638 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// 'Group sockets' +// Implementation + +#include "Groupsock.hh" +#include "GroupsockHelper.hh" +//##### Eventually fix the following #include; we shouldn't know about tunnels +#include "TunnelEncaps.hh" + +#ifndef NO_SSTREAM +#include +#endif +#include + +///////// OutputSocket ////////// + +OutputSocket::OutputSocket(UsageEnvironment& env) + : Socket(env, 0 /* let kernel choose port */), + fSourcePort(0), fLastSentTTL(0) { +} + +OutputSocket::OutputSocket(UsageEnvironment& env, Port port) + : Socket(env, port), + fSourcePort(0), fLastSentTTL(0) { +} + +OutputSocket::~OutputSocket() { +} + +Boolean OutputSocket::write(netAddressBits address, Port port, u_int8_t ttl, + unsigned char* buffer, unsigned bufferSize) { + if (ttl == fLastSentTTL) { + // Optimization: So we don't do a 'set TTL' system call again + ttl = 0; + } else { + fLastSentTTL = ttl; + } + struct in_addr destAddr; destAddr.s_addr = address; + if (!writeSocket(env(), socketNum(), destAddr, port, ttl, + buffer, bufferSize)) + return False; + + if (sourcePortNum() == 0) { + // Now that we've sent a packet, we can find out what the + // kernel chose as our ephemeral source port number: + if (!getSourcePort(env(), socketNum(), fSourcePort)) { + if (DebugLevel >= 1) + env() << *this + << ": failed to get source port: " + << env().getResultMsg() << "\n"; + return False; + } + } + + return True; +} + +// By default, we don't do reads: +Boolean OutputSocket +::handleRead(unsigned char* /*buffer*/, unsigned /*bufferMaxSize*/, + unsigned& /*bytesRead*/, struct sockaddr_in& /*fromAddress*/) { + return True; +} + + +///////// destRecord ////////// + +destRecord +::destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, + destRecord* next) + : fNext(next), fGroupEId(addr, port.num(), ttl), fPort(port) { +} + +destRecord::~destRecord() { + delete fNext; +} + + +///////// Groupsock ////////// + +NetInterfaceTrafficStats Groupsock::statsIncoming; +NetInterfaceTrafficStats Groupsock::statsOutgoing; +NetInterfaceTrafficStats Groupsock::statsRelayedIncoming; +NetInterfaceTrafficStats Groupsock::statsRelayedOutgoing; + +// Constructor for a source-independent multicast group +Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr, + Port port, u_int8_t ttl) + : OutputSocket(env, port), + deleteIfNoMembers(False), isSlave(False), + fIncomingGroupEId(groupAddr, port.num(), ttl), fDests(NULL), fTTL(ttl) { + addDestination(groupAddr, port); + + if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr)) { + if (DebugLevel >= 1) { + env << *this << ": failed to join group: " + << env.getResultMsg() << "\n"; + } + } + + // Make sure we can get our source address: + if (ourIPAddress(env) == 0) { + if (DebugLevel >= 0) { // this is a fatal error + env << "Unable to determine our source address: " + << env.getResultMsg() << "\n"; + } + } + + if (DebugLevel >= 2) env << *this << ": created\n"; +} + +// Constructor for a source-specific multicast group +Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr, + struct in_addr const& sourceFilterAddr, + Port port) + : OutputSocket(env, port), + deleteIfNoMembers(False), isSlave(False), + fIncomingGroupEId(groupAddr, sourceFilterAddr, port.num()), + fDests(NULL), fTTL(255) { + addDestination(groupAddr, port); + + // First try a SSM join. If that fails, try a regular join: + if (!socketJoinGroupSSM(env, socketNum(), groupAddr.s_addr, + sourceFilterAddr.s_addr)) { + if (DebugLevel >= 3) { + env << *this << ": SSM join failed: " + << env.getResultMsg(); + env << " - trying regular join instead\n"; + } + if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr)) { + if (DebugLevel >= 1) { + env << *this << ": failed to join group: " + << env.getResultMsg() << "\n"; + } + } + } + + if (DebugLevel >= 2) env << *this << ": created\n"; +} + +Groupsock::~Groupsock() { + if (isSSM()) { + if (!socketLeaveGroupSSM(env(), socketNum(), groupAddress().s_addr, + sourceFilterAddress().s_addr)) { + socketLeaveGroup(env(), socketNum(), groupAddress().s_addr); + } + } else { + socketLeaveGroup(env(), socketNum(), groupAddress().s_addr); + } + + delete fDests; + + if (DebugLevel >= 2) env() << *this << ": deleting\n"; +} + +void +Groupsock::changeDestinationParameters(struct in_addr const& newDestAddr, + Port newDestPort, int newDestTTL) { + if (fDests == NULL) return; + + struct in_addr destAddr = fDests->fGroupEId.groupAddress(); + if (newDestAddr.s_addr != 0) { + if (newDestAddr.s_addr != destAddr.s_addr + && IsMulticastAddress(newDestAddr.s_addr)) { + // If the new destination is a multicast address, then we assume that + // we want to join it also. (If this is not in fact the case, then + // call "multicastSendOnly()" afterwards.) + socketLeaveGroup(env(), socketNum(), destAddr.s_addr); + socketJoinGroup(env(), socketNum(), newDestAddr.s_addr); + } + destAddr.s_addr = newDestAddr.s_addr; + } + + portNumBits destPortNum = fDests->fGroupEId.portNum(); + if (newDestPort.num() != 0) { + if (newDestPort.num() != destPortNum + && IsMulticastAddress(destAddr.s_addr)) { + // Also bind to the new port number: + changePort(newDestPort); + // And rejoin the multicast group: + socketJoinGroup(env(), socketNum(), destAddr.s_addr); + } + destPortNum = newDestPort.num(); + fDests->fPort = newDestPort; + } + + u_int8_t destTTL = ttl(); + if (newDestTTL != ~0) destTTL = (u_int8_t)newDestTTL; + + fDests->fGroupEId = GroupEId(destAddr, destPortNum, destTTL); +} + +void Groupsock::addDestination(struct in_addr const& addr, Port const& port) { + // Check whether this destination is already known: + for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) { + if (addr.s_addr == dests->fGroupEId.groupAddress().s_addr + && port.num() == dests->fPort.num()) { + return; + } + } + + fDests = new destRecord(addr, port, ttl(), fDests); +} + +void Groupsock::removeDestination(struct in_addr const& addr, Port const& port) { + for (destRecord** destsPtr = &fDests; *destsPtr != NULL; + destsPtr = &((*destsPtr)->fNext)) { + if (addr.s_addr == (*destsPtr)->fGroupEId.groupAddress().s_addr + && port.num() == (*destsPtr)->fPort.num()) { + // Remove the record pointed to by *destsPtr : + destRecord* next = (*destsPtr)->fNext; + (*destsPtr)->fNext = NULL; + delete (*destsPtr); + *destsPtr = next; + return; + } + } +} + +void Groupsock::removeAllDestinations() { + delete fDests; fDests = NULL; +} + +void Groupsock::multicastSendOnly() { + // We disable this code for now, because - on some systems - leaving the multicast group seems to cause sent packets + // to not be received by other applications (at least, on the same host). +#if 0 + socketLeaveGroup(env(), socketNum(), fIncomingGroupEId.groupAddress().s_addr); + for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) { + socketLeaveGroup(env(), socketNum(), dests->fGroupEId.groupAddress().s_addr); + } +#endif +} + +Boolean Groupsock::output(UsageEnvironment& env, u_int8_t ttlToSend, + unsigned char* buffer, unsigned bufferSize, + DirectedNetInterface* interfaceNotToFwdBackTo) { + do { + // First, do the datagram send, to each destination: + Boolean writeSuccess = True; + for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) { + if (!write(dests->fGroupEId.groupAddress().s_addr, dests->fPort, ttlToSend, + buffer, bufferSize)) { + writeSuccess = False; + break; + } + } + if (!writeSuccess) break; + statsOutgoing.countPacket(bufferSize); + statsGroupOutgoing.countPacket(bufferSize); + + // Then, forward to our members: + int numMembers = 0; + if (!members().IsEmpty()) { + numMembers = + outputToAllMembersExcept(interfaceNotToFwdBackTo, + ttlToSend, buffer, bufferSize, + ourIPAddress(env)); + if (numMembers < 0) break; + } + + if (DebugLevel >= 3) { + env << *this << ": wrote " << bufferSize << " bytes, ttl " + << (unsigned)ttlToSend; + if (numMembers > 0) { + env << "; relayed to " << numMembers << " members"; + } + env << "\n"; + } + return True; + } while (0); + + if (DebugLevel >= 0) { // this is a fatal error + env.setResultMsg("Groupsock write failed: ", env.getResultMsg()); + } + return False; +} + +Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize, + unsigned& bytesRead, + struct sockaddr_in& fromAddress) { + // Read data from the socket, and relay it across any attached tunnels + //##### later make this code more general - independent of tunnels + + bytesRead = 0; + + int maxBytesToRead = bufferMaxSize - TunnelEncapsulationTrailerMaxSize; + int numBytes = readSocket(env(), socketNum(), + buffer, maxBytesToRead, fromAddress); + if (numBytes < 0) { + if (DebugLevel >= 0) { // this is a fatal error + env().setResultMsg("Groupsock read failed: ", + env().getResultMsg()); + } + return False; + } + + // If we're a SSM group, make sure the source address matches: + if (isSSM() + && fromAddress.sin_addr.s_addr != sourceFilterAddress().s_addr) { + return True; + } + + // We'll handle this data. + // Also write it (with the encapsulation trailer) to each member, + // unless the packet was originally sent by us to begin with. + bytesRead = numBytes; + + int numMembers = 0; + if (!wasLoopedBackFromUs(env(), fromAddress)) { + statsIncoming.countPacket(numBytes); + statsGroupIncoming.countPacket(numBytes); + numMembers = + outputToAllMembersExcept(NULL, ttl(), + buffer, bytesRead, + fromAddress.sin_addr.s_addr); + if (numMembers > 0) { + statsRelayedIncoming.countPacket(numBytes); + statsGroupRelayedIncoming.countPacket(numBytes); + } + } + if (DebugLevel >= 3) { + env() << *this << ": read " << bytesRead << " bytes from " << AddressString(fromAddress).val(); + if (numMembers > 0) { + env() << "; relayed to " << numMembers << " members"; + } + env() << "\n"; + } + + return True; +} + +Boolean Groupsock::wasLoopedBackFromUs(UsageEnvironment& env, + struct sockaddr_in& fromAddress) { + if (fromAddress.sin_addr.s_addr + == ourIPAddress(env)) { + if (fromAddress.sin_port == sourcePortNum()) { +#ifdef DEBUG_LOOPBACK_CHECKING + if (DebugLevel >= 3) { + env() << *this << ": got looped-back packet\n"; + } +#endif + return True; + } + } + + return False; +} + +int Groupsock::outputToAllMembersExcept(DirectedNetInterface* exceptInterface, + u_int8_t ttlToFwd, + unsigned char* data, unsigned size, + netAddressBits sourceAddr) { + // Don't forward TTL-0 packets + if (ttlToFwd == 0) return 0; + + DirectedNetInterfaceSet::Iterator iter(members()); + unsigned numMembers = 0; + DirectedNetInterface* interf; + while ((interf = iter.next()) != NULL) { + // Check whether we've asked to exclude this interface: + if (interf == exceptInterface) + continue; + + // Check that the packet's source address makes it OK to + // be relayed across this interface: + UsageEnvironment& saveEnv = env(); + // because the following call may delete "this" + if (!interf->SourceAddrOKForRelaying(saveEnv, sourceAddr)) { + if (strcmp(saveEnv.getResultMsg(), "") != 0) { + // Treat this as a fatal error + return -1; + } else { + continue; + } + } + + if (numMembers == 0) { + // We know that we're going to forward to at least one + // member, so fill in the tunnel encapsulation trailer. + // (Note: Allow for it not being 4-byte-aligned.) + TunnelEncapsulationTrailer* trailerInPacket + = (TunnelEncapsulationTrailer*)&data[size]; + TunnelEncapsulationTrailer* trailer; + + Boolean misaligned = ((uintptr_t)trailerInPacket & 3) != 0; + unsigned trailerOffset; + u_int8_t tunnelCmd; + if (isSSM()) { + // add an 'auxilliary address' before the trailer + trailerOffset = TunnelEncapsulationTrailerAuxSize; + tunnelCmd = TunnelDataAuxCmd; + } else { + trailerOffset = 0; + tunnelCmd = TunnelDataCmd; + } + unsigned trailerSize = TunnelEncapsulationTrailerSize + trailerOffset; + unsigned tmpTr[TunnelEncapsulationTrailerMaxSize]; + if (misaligned) { + trailer = (TunnelEncapsulationTrailer*)&tmpTr; + } else { + trailer = trailerInPacket; + } + trailer += trailerOffset; + + if (fDests != NULL) { + trailer->address() = fDests->fGroupEId.groupAddress().s_addr; + trailer->port() = fDests->fPort; // structure copy, outputs in network order + } + trailer->ttl() = ttlToFwd; + trailer->command() = tunnelCmd; + + if (isSSM()) { + trailer->auxAddress() = sourceFilterAddress().s_addr; + } + + if (misaligned) { + memmove(trailerInPacket, trailer-trailerOffset, trailerSize); + } + + size += trailerSize; + } + + interf->write(data, size); + ++numMembers; + } + + return numMembers; +} + +UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g) { + UsageEnvironment& s1 = s << timestampString() << " Groupsock(" + << g.socketNum() << ": " + << AddressString(g.groupAddress()).val() + << ", " << g.port() << ", "; + if (g.isSSM()) { + return s1 << "SSM source: " + << AddressString(g.sourceFilterAddress()).val() << ")"; + } else { + return s1 << (unsigned)(g.ttl()) << ")"; + } +} + + +////////// GroupsockLookupTable ////////// + + +// A hash table used to index Groupsocks by socket number. + +static HashTable* getSocketTable(UsageEnvironment& env) { + if (env.groupsockPriv == NULL) { // We need to create it + env.groupsockPriv = HashTable::create(ONE_WORD_HASH_KEYS); + } + return (HashTable*)(env.groupsockPriv); +} + +static Boolean unsetGroupsockBySocket(Groupsock const* groupsock) { + do { + if (groupsock == NULL) break; + + int sock = groupsock->socketNum(); + // Make sure "sock" is in bounds: + if (sock < 0) break; + + HashTable* sockets = getSocketTable(groupsock->env()); + if (sockets == NULL) break; + + Groupsock* gs = (Groupsock*)sockets->Lookup((char*)(long)sock); + if (gs == NULL || gs != groupsock) break; + sockets->Remove((char*)(long)sock); + + if (sockets->IsEmpty()) { + // We can also delete the table (to reclaim space): + delete sockets; + (gs->env()).groupsockPriv = NULL; + } + + return True; + } while (0); + + return False; +} + +static Boolean setGroupsockBySocket(UsageEnvironment& env, int sock, + Groupsock* groupsock) { + do { + // Make sure the "sock" parameter is in bounds: + if (sock < 0) { + char buf[100]; + sprintf(buf, "trying to use bad socket (%d)", sock); + env.setResultMsg(buf); + break; + } + + HashTable* sockets = getSocketTable(env); + if (sockets == NULL) break; + + // Make sure we're not replacing an existing Groupsock (although that shouldn't happen) + Boolean alreadyExists + = (sockets->Lookup((char*)(long)sock) != 0); + if (alreadyExists) { + char buf[100]; + sprintf(buf, "Attempting to replace an existing socket (%d)", sock); + env.setResultMsg(buf); + break; + } + + sockets->Add((char*)(long)sock, groupsock); + return True; + } while (0); + + return False; +} + +static Groupsock* getGroupsockBySocket(UsageEnvironment& env, int sock) { + do { + // Make sure the "sock" parameter is in bounds: + if (sock < 0) break; + + HashTable* sockets = getSocketTable(env); + if (sockets == NULL) break; + + return (Groupsock*)sockets->Lookup((char*)(long)sock); + } while (0); + + return NULL; +} + +Groupsock* +GroupsockLookupTable::Fetch(UsageEnvironment& env, + netAddressBits groupAddress, + Port port, u_int8_t ttl, + Boolean& isNew) { + isNew = False; + Groupsock* groupsock; + do { + groupsock = (Groupsock*) fTable.Lookup(groupAddress, (~0), port); + if (groupsock == NULL) { // we need to create one: + groupsock = AddNew(env, groupAddress, (~0), port, ttl); + if (groupsock == NULL) break; + isNew = True; + } + } while (0); + + return groupsock; +} + +Groupsock* +GroupsockLookupTable::Fetch(UsageEnvironment& env, + netAddressBits groupAddress, + netAddressBits sourceFilterAddr, Port port, + Boolean& isNew) { + isNew = False; + Groupsock* groupsock; + do { + groupsock + = (Groupsock*) fTable.Lookup(groupAddress, sourceFilterAddr, port); + if (groupsock == NULL) { // we need to create one: + groupsock = AddNew(env, groupAddress, sourceFilterAddr, port, 0); + if (groupsock == NULL) break; + isNew = True; + } + } while (0); + + return groupsock; +} + +Groupsock* +GroupsockLookupTable::Lookup(netAddressBits groupAddress, Port port) { + return (Groupsock*) fTable.Lookup(groupAddress, (~0), port); +} + +Groupsock* +GroupsockLookupTable::Lookup(netAddressBits groupAddress, + netAddressBits sourceFilterAddr, Port port) { + return (Groupsock*) fTable.Lookup(groupAddress, sourceFilterAddr, port); +} + +Groupsock* GroupsockLookupTable::Lookup(UsageEnvironment& env, int sock) { + return getGroupsockBySocket(env, sock); +} + +Boolean GroupsockLookupTable::Remove(Groupsock const* groupsock) { + unsetGroupsockBySocket(groupsock); + return fTable.Remove(groupsock->groupAddress().s_addr, + groupsock->sourceFilterAddress().s_addr, + groupsock->port()); +} + +Groupsock* GroupsockLookupTable::AddNew(UsageEnvironment& env, + netAddressBits groupAddress, + netAddressBits sourceFilterAddress, + Port port, u_int8_t ttl) { + Groupsock* groupsock; + do { + struct in_addr groupAddr; groupAddr.s_addr = groupAddress; + if (sourceFilterAddress == netAddressBits(~0)) { + // regular, ISM groupsock + groupsock = new Groupsock(env, groupAddr, port, ttl); + } else { + // SSM groupsock + struct in_addr sourceFilterAddr; + sourceFilterAddr.s_addr = sourceFilterAddress; + groupsock = new Groupsock(env, groupAddr, sourceFilterAddr, port); + } + + if (groupsock == NULL || groupsock->socketNum() < 0) break; + + if (!setGroupsockBySocket(env, groupsock->socketNum(), groupsock)) break; + + fTable.Add(groupAddress, sourceFilterAddress, port, (void*)groupsock); + } while (0); + + return groupsock; +} + +GroupsockLookupTable::Iterator::Iterator(GroupsockLookupTable& groupsocks) + : fIter(AddressPortLookupTable::Iterator(groupsocks.fTable)) { +} + +Groupsock* GroupsockLookupTable::Iterator::next() { + return (Groupsock*) fIter.next(); +}; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/GroupsockHelper.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/GroupsockHelper.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/GroupsockHelper.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/GroupsockHelper.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,812 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Helper routines to implement 'group sockets' +// Implementation + +#include "GroupsockHelper.hh" + +#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__) +#include +extern "C" int initializeWinsockIfNecessary(); +#else +#include +#include +#include +#include +#define initializeWinsockIfNecessary() 1 +#endif +#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4) +#else +#include +#define USE_SIGNALS 1 +#endif +#include + +// By default, use INADDR_ANY for the sending and receiving interfaces: +netAddressBits SendingInterfaceAddr = INADDR_ANY; +netAddressBits ReceivingInterfaceAddr = INADDR_ANY; + +static void socketErr(UsageEnvironment& env, char const* errorMsg) { + env.setResultErrMsg(errorMsg); +} + +static int reuseFlag = 1; + +NoReuse::NoReuse() { + reuseFlag = 0; +} + +NoReuse::~NoReuse() { + reuseFlag = 1; +} + +int setupDatagramSocket(UsageEnvironment& env, Port port) { + if (!initializeWinsockIfNecessary()) { + socketErr(env, "Failed to initialize 'winsock': "); + return -1; + } + + int newSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (newSocket < 0) { + socketErr(env, "unable to create datagram socket: "); + return newSocket; + } + + if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, + (const char*)&reuseFlag, sizeof reuseFlag) < 0) { + socketErr(env, "setsockopt(SO_REUSEADDR) error: "); + closeSocket(newSocket); + return -1; + } + +#if defined(__WIN32__) || defined(_WIN32) + // Windoze doesn't properly handle SO_REUSEPORT or IP_MULTICAST_LOOP +#else +#ifdef SO_REUSEPORT + if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT, + (const char*)&reuseFlag, sizeof reuseFlag) < 0) { + socketErr(env, "setsockopt(SO_REUSEPORT) error: "); + closeSocket(newSocket); + return -1; + } +#endif + +#ifdef IP_MULTICAST_LOOP + const u_int8_t loop = 1; + if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_LOOP, + (const char*)&loop, sizeof loop) < 0) { + socketErr(env, "setsockopt(IP_MULTICAST_LOOP) error: "); + closeSocket(newSocket); + return -1; + } +#endif +#endif + + // Note: Windoze requires binding, even if the port number is 0 + netAddressBits addr = INADDR_ANY; +#if defined(__WIN32__) || defined(_WIN32) +#else + if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) { +#endif + if (port.num() == 0) addr = ReceivingInterfaceAddr; + MAKE_SOCKADDR_IN(name, addr, port.num()); + if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) { + char tmpBuffer[100]; + sprintf(tmpBuffer, "bind() error (port number: %d): ", + ntohs(port.num())); + socketErr(env, tmpBuffer); + closeSocket(newSocket); + return -1; + } +#if defined(__WIN32__) || defined(_WIN32) +#else + } +#endif + + // Set the sending interface for multicasts, if it's not the default: + if (SendingInterfaceAddr != INADDR_ANY) { + struct in_addr addr; + addr.s_addr = SendingInterfaceAddr; + + if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_IF, + (const char*)&addr, sizeof addr) < 0) { + socketErr(env, "error setting outgoing multicast interface: "); + closeSocket(newSocket); + return -1; + } + } + + return newSocket; +} + +Boolean makeSocketNonBlocking(int sock) { +#if defined(__WIN32__) || defined(_WIN32) + unsigned long arg = 1; + return ioctlsocket(sock, FIONBIO, &arg) == 0; +#elif defined(VXWORKS) + int arg = 1; + return ioctl(sock, FIONBIO, (int)&arg) == 0; +#else + int curFlags = fcntl(sock, F_GETFL, 0); + return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0; +#endif +} + +Boolean makeSocketBlocking(int sock) { +#if defined(__WIN32__) || defined(_WIN32) || defined(IMN_PIM) + unsigned long arg = 0; + return ioctlsocket(sock, FIONBIO, &arg) == 0; +#elif defined(VXWORKS) + int arg = 0; + return ioctl(sock, FIONBIO, (int)&arg) == 0; +#else + int curFlags = fcntl(sock, F_GETFL, 0); + return fcntl(sock, F_SETFL, curFlags&(~O_NONBLOCK)) >= 0; +#endif +} + +int setupStreamSocket(UsageEnvironment& env, + Port port, Boolean makeNonBlocking) { + if (!initializeWinsockIfNecessary()) { + socketErr(env, "Failed to initialize 'winsock': "); + return -1; + } + + int newSocket = socket(AF_INET, SOCK_STREAM, 0); + if (newSocket < 0) { + socketErr(env, "unable to create stream socket: "); + return newSocket; + } + + if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, + (const char*)&reuseFlag, sizeof reuseFlag) < 0) { + socketErr(env, "setsockopt(SO_REUSEADDR) error: "); + closeSocket(newSocket); + return -1; + } + + // SO_REUSEPORT doesn't really make sense for TCP sockets, so we + // normally don't set them. However, if you really want to do this + // #define REUSE_FOR_TCP +#ifdef REUSE_FOR_TCP +#if defined(__WIN32__) || defined(_WIN32) + // Windoze doesn't properly handle SO_REUSEPORT +#else +#ifdef SO_REUSEPORT + if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT, + (const char*)&reuseFlag, sizeof reuseFlag) < 0) { + socketErr(env, "setsockopt(SO_REUSEPORT) error: "); + closeSocket(newSocket); + return -1; + } +#endif +#endif +#endif + + // Note: Windoze requires binding, even if the port number is 0 +#if defined(__WIN32__) || defined(_WIN32) +#else + if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) { +#endif + MAKE_SOCKADDR_IN(name, ReceivingInterfaceAddr, port.num()); + if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) { + char tmpBuffer[100]; + sprintf(tmpBuffer, "bind() error (port number: %d): ", + ntohs(port.num())); + socketErr(env, tmpBuffer); + closeSocket(newSocket); + return -1; + } +#if defined(__WIN32__) || defined(_WIN32) +#else + } +#endif + + if (makeNonBlocking) { + if (!makeSocketNonBlocking(newSocket)) { + socketErr(env, "failed to make non-blocking: "); + closeSocket(newSocket); + return -1; + } + } + + return newSocket; +} + +#ifndef IMN_PIM +static int blockUntilReadable(UsageEnvironment& env, + int socket, struct timeval* timeout) { + int result = -1; + bool keepTrying = true; + do { + fd_set rd_set; + FD_ZERO(&rd_set); + if (socket < 0) break; + FD_SET((unsigned) socket, &rd_set); + const unsigned numFds = socket+1; + + result = select(numFds, &rd_set, NULL, NULL, timeout); + if (timeout != NULL && result == 0) { + keepTrying = false; + break; // this is OK - timeout occurred + } else if (result <= 0) { + int err = env.getErrno(); + if (err == EINTR || err == EAGAIN || err == EWOULDBLOCK) + { + continue; + } + else + { + socketErr(env, "select() error: "); + keepTrying = false; + break; + } + } + + if (!FD_ISSET(socket, &rd_set)) { + socketErr(env, "select() error - !FD_ISSET"); + keepTrying = false; + break; + } + } while (keepTrying); + + return result; +} +#else +extern int blockUntilReadable(UsageEnvironment& env, + int socket, struct timeval* timeout); +#endif + +int readSocket(UsageEnvironment& env, + int socket, unsigned char* buffer, unsigned bufferSize, + struct sockaddr_in& fromAddress, + struct timeval* timeout) { + int bytesRead = -1; + do { + int result = blockUntilReadable(env, socket, timeout); + if (timeout != NULL && result == 0) { + bytesRead = 0; + break; + } else if (result <= 0) { + break; + } + + SOCKLEN_T addressSize = sizeof fromAddress; + bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0, + (struct sockaddr*)&fromAddress, + &addressSize); + if (bytesRead < 0) { + //##### HACK to work around bugs in Linux and Windows: + int err = env.getErrno(); + if (err == 111 /*ECONNREFUSED (Linux)*/ +#if defined(__WIN32__) || defined(_WIN32) + // What a piece of crap Windows is. Sometimes + // recvfrom() returns -1, but with an 'errno' of 0. + // This appears not to be a real error; just treat + // it as if it were a read of zero bytes, and hope + // we don't have to do anything else to 'reset' + // this alleged error: + || err == 0 +#else + || err == EAGAIN +#endif + || err == 113 /*EHOSTUNREACH (Linux)*/) { + //Why does Linux return this for datagram sock? + fromAddress.sin_addr.s_addr = 0; + return 0; + } + //##### END HACK + socketErr(env, "recvfrom() error: "); + break; + } + } while (0); + + return bytesRead; +} + + +int readSocketExact(UsageEnvironment& env, + int socket, unsigned char* buffer, unsigned bufferSize, + struct sockaddr_in& fromAddress, + struct timeval* timeout) { + /* read EXACTLY bufferSize bytes from the socket into the buffer. + fromaddress is address of last read. + return the number of bytes actually read when an error occurs + */ + int bsize = bufferSize; + int totBytesRead =0; + do { + int bytesRead = readSocket (env, socket, buffer + totBytesRead, bsize, + fromAddress, timeout); + if (bytesRead <= 0) break; + totBytesRead += bytesRead; + bsize -= bytesRead; + } while (bsize != 0); + + return totBytesRead; +} + +Boolean writeSocket(UsageEnvironment& env, + int socket, struct in_addr address, Port port, + u_int8_t ttlArg, + unsigned char* buffer, unsigned bufferSize) { + do { + if (ttlArg != 0) { + // Before sending, set the socket's TTL: +#if defined(__WIN32__) || defined(_WIN32) +#define TTL_TYPE int +#else +#define TTL_TYPE u_int8_t +#endif + TTL_TYPE ttl = (TTL_TYPE)ttlArg; + if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, + (const char*)&ttl, sizeof ttl) < 0) { + socketErr(env, "setsockopt(IP_MULTICAST_TTL) error: "); + break; + } + } + + MAKE_SOCKADDR_IN(dest, address.s_addr, port.num()); + int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0, + (struct sockaddr*)&dest, sizeof dest); + if (bytesSent != (int)bufferSize) { + char tmpBuf[100]; + sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize); + socketErr(env, tmpBuf); + break; + } + + return True; + } while (0); + + return False; +} + +static unsigned getBufferSize(UsageEnvironment& env, int bufOptName, + int socket) { + unsigned curSize; + SOCKLEN_T sizeSize = sizeof curSize; + if (getsockopt(socket, SOL_SOCKET, bufOptName, + (char*)&curSize, &sizeSize) < 0) { + socketErr(env, "getBufferSize() error: "); + return 0; + } + + return curSize; +} +unsigned getSendBufferSize(UsageEnvironment& env, int socket) { + return getBufferSize(env, SO_SNDBUF, socket); +} +unsigned getReceiveBufferSize(UsageEnvironment& env, int socket) { + return getBufferSize(env, SO_RCVBUF, socket); +} + +static unsigned setBufferTo(UsageEnvironment& env, int bufOptName, + int socket, unsigned requestedSize) { + SOCKLEN_T sizeSize = sizeof requestedSize; + int retval = setsockopt(socket, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize); + if (retval != 0) + { + socketErr(env, "setBufferTo() error: "); + return 0; + } + // Get and return the actual, resulting buffer size: + return getBufferSize(env, bufOptName, socket); +} +unsigned setSendBufferTo(UsageEnvironment& env, + int socket, unsigned requestedSize) { + return setBufferTo(env, SO_SNDBUF, socket, requestedSize); +} +unsigned setReceiveBufferTo(UsageEnvironment& env, + int socket, unsigned requestedSize) { + return setBufferTo(env, SO_RCVBUF, socket, requestedSize); +} + +static unsigned increaseBufferTo(UsageEnvironment& env, int bufOptName, + int socket, unsigned requestedSize) { + // First, get the current buffer size. If it's already at least + // as big as what we're requesting, do nothing. + unsigned curSize = getBufferSize(env, bufOptName, socket); + + // Next, try to increase the buffer to the requested size, + // or to some smaller size, if that's not possible: + while (requestedSize > curSize) { + SOCKLEN_T sizeSize = sizeof requestedSize; + if (setsockopt(socket, SOL_SOCKET, bufOptName, + (char*)&requestedSize, sizeSize) >= 0) { + // success + return requestedSize; + } + requestedSize = (requestedSize+curSize)/2; + } + + return getBufferSize(env, bufOptName, socket); +} +unsigned increaseSendBufferTo(UsageEnvironment& env, + int socket, unsigned requestedSize) { + return increaseBufferTo(env, SO_SNDBUF, socket, requestedSize); +} +unsigned increaseReceiveBufferTo(UsageEnvironment& env, + int socket, unsigned requestedSize) { + return increaseBufferTo(env, SO_RCVBUF, socket, requestedSize); +} + +Boolean socketJoinGroup(UsageEnvironment& env, int socket, + netAddressBits groupAddress){ + if (!IsMulticastAddress(groupAddress)) return True; // ignore this case + + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = groupAddress; + imr.imr_interface.s_addr = ReceivingInterfaceAddr; + if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char*)&imr, sizeof (struct ip_mreq)) < 0) { +#if defined(__WIN32__) || defined(_WIN32) + if (env.getErrno() != 0) { + // That piece-of-shit toy operating system (Windows) sometimes lies + // about setsockopt() failing! +#endif + socketErr(env, "setsockopt(IP_ADD_MEMBERSHIP) error: "); + return False; +#if defined(__WIN32__) || defined(_WIN32) + } +#endif + } + + return True; +} + +Boolean socketLeaveGroup(UsageEnvironment&, int socket, + netAddressBits groupAddress) { + if (!IsMulticastAddress(groupAddress)) return True; // ignore this case + + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = groupAddress; + imr.imr_interface.s_addr = ReceivingInterfaceAddr; + if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (const char*)&imr, sizeof (struct ip_mreq)) < 0) { + return False; + } + + return True; +} + +// The source-specific join/leave operations require special setsockopt() +// commands, and a special structure (ip_mreq_source). If the include files +// didn't define these, we do so here: +#if !defined(IP_ADD_SOURCE_MEMBERSHIP) +struct ip_mreq_source { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_sourceaddr; /* IP address of source */ + struct in_addr imr_interface; /* local IP address of interface */ +}; +#endif + +#ifndef IP_ADD_SOURCE_MEMBERSHIP + +#ifdef LINUX +#define IP_ADD_SOURCE_MEMBERSHIP 39 +#define IP_DROP_SOURCE_MEMBERSHIP 40 +#else +#define IP_ADD_SOURCE_MEMBERSHIP 25 +#define IP_DROP_SOURCE_MEMBERSHIP 26 +#endif + +#endif + +Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket, + netAddressBits groupAddress, + netAddressBits sourceFilterAddr) { + if (!IsMulticastAddress(groupAddress)) return True; // ignore this case + + struct ip_mreq_source imr; +#ifdef __ANDROID__ + imr.imr_multiaddr = groupAddress; + imr.imr_sourceaddr = sourceFilterAddr; + imr.imr_interface = ReceivingInterfaceAddr; +#else + imr.imr_multiaddr.s_addr = groupAddress; + imr.imr_sourceaddr.s_addr = sourceFilterAddr; + imr.imr_interface.s_addr = ReceivingInterfaceAddr; +#endif + if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, + (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) { + socketErr(env, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP) error: "); + return False; + } + + return True; +} + +Boolean socketLeaveGroupSSM(UsageEnvironment& /*env*/, int socket, + netAddressBits groupAddress, + netAddressBits sourceFilterAddr) { + if (!IsMulticastAddress(groupAddress)) return True; // ignore this case + + struct ip_mreq_source imr; +#ifdef __ANDROID__ + imr.imr_multiaddr = groupAddress; + imr.imr_sourceaddr = sourceFilterAddr; + imr.imr_interface = ReceivingInterfaceAddr; +#else + imr.imr_multiaddr.s_addr = groupAddress; + imr.imr_sourceaddr.s_addr = sourceFilterAddr; + imr.imr_interface.s_addr = ReceivingInterfaceAddr; +#endif + if (setsockopt(socket, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, + (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) { + return False; + } + + return True; +} + +static Boolean getSourcePort0(int socket, portNumBits& resultPortNum/*host order*/) { + sockaddr_in test; test.sin_port = 0; + SOCKLEN_T len = sizeof test; + if (getsockname(socket, (struct sockaddr*)&test, &len) < 0) return False; + + resultPortNum = ntohs(test.sin_port); + return True; +} + +Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port) { + portNumBits portNum = 0; + if (!getSourcePort0(socket, portNum) || portNum == 0) { + // Hack - call bind(), then try again: + MAKE_SOCKADDR_IN(name, INADDR_ANY, 0); + int retval = bind(socket, (struct sockaddr*)&name, sizeof name); + if (retval != 0) + { + socketErr(env, "bind() error: "); + return False; + } + + if (!getSourcePort0(socket, portNum) || portNum == 0) { + socketErr(env, "getsockname() error: "); + return False; + } + } + + port = Port(portNum); + return True; +} + +static Boolean badAddress(netAddressBits addr) { + // Check for some possible erroneous addresses: + netAddressBits hAddr = ntohl(addr); + return (hAddr == 0x7F000001 /* 127.0.0.1 */ + || hAddr == 0 + || hAddr == (netAddressBits)(~0)); +} + +Boolean loopbackWorks = 1; + +netAddressBits ourIPAddress(UsageEnvironment& env) { + static netAddressBits ourAddress = 0; + struct in_addr testAddr; + + if (ourAddress == 0) { + // We need to find our source address + struct sockaddr_in fromAddr; + fromAddr.sin_addr.s_addr = 0; + int sock; + + // Get our address by sending a (0-TTL) multicast packet, + // receiving it, and looking at the source address used. + // (This is kinda bogus, but it provides the best guarantee + // that other nodes will think our address is the same as we do.) + do { + loopbackWorks = 0; // until we learn otherwise + + testAddr.s_addr = our_inet_addr("228.67.43.91"); // arbitrary + Port testPort(15947); // ditto + + sock = setupDatagramSocket(env, testPort); + if (sock < 0) break; + + if (!socketJoinGroup(env, sock, testAddr.s_addr)) break; + + unsigned char testString[] = "hostIdTest"; + unsigned testStringLength = sizeof testString; + + if (!writeSocket(env, sock, testAddr, testPort, 0, + testString, testStringLength)) break; + + unsigned char readBuffer[20]; + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + int bytesRead = readSocket(env, sock, + readBuffer, sizeof readBuffer, + fromAddr, &timeout); + if (bytesRead == 0 // timeout occurred + || bytesRead != (int)testStringLength + || strncmp((char*)readBuffer, (char*)testString, + testStringLength) != 0) { + break; + } + + loopbackWorks = 1; + } while (0); + + if (!loopbackWorks) do { + // We couldn't find our address using multicast loopback + // so try instead to look it up directly. + char hostname[100]; + hostname[0] = '\0'; +#ifndef CRIS + gethostname(hostname, sizeof hostname); +#endif + if (hostname[0] == '\0') { + env.setResultErrMsg("initial gethostname() failed"); + break; + } + +#if defined(VXWORKS) +#include + if (ERROR == (ourAddress = hostGetByName( hostname ))) break; +#else + struct hostent* hstent + = (struct hostent*)gethostbyname(hostname); + if (hstent == NULL || hstent->h_length != 4) { + env.setResultErrMsg("initial gethostbyname() failed"); + break; + } + // Take the first address that's not bad + // (This code, like many others, won't handle IPv6) + netAddressBits addr = 0; + for (unsigned i = 0; ; ++i) { + char* addrPtr = hstent->h_addr_list[i]; + if (addrPtr == NULL) break; + + netAddressBits a = *(netAddressBits*)addrPtr; + if (!badAddress(a)) { + addr = a; + break; + } + } + if (addr != 0) { + fromAddr.sin_addr.s_addr = addr; + } else { + env.setResultMsg("no address"); + break; + } + } while (0); + + // Make sure we have a good address: + netAddressBits from = fromAddr.sin_addr.s_addr; + if (badAddress(from)) { + char tmp[100]; + sprintf(tmp, + "This computer has an invalid IP address: 0x%x", + (netAddressBits)(ntohl(from))); + env.setResultMsg(tmp); + from = 0; + } + + ourAddress = from; +#endif + + if (sock >= 0) { + socketLeaveGroup(env, sock, testAddr.s_addr); + closeSocket(sock); + } + + // Use our newly-discovered IP address, and the current time, + // to initialize the random number generator's seed: + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + unsigned seed = ourAddress^timeNow.tv_sec^timeNow.tv_usec; + our_srandom(seed); + } + return ourAddress; +} + +netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env) { + // First, a hack to ensure that our random number generator is seeded: + (void) ourIPAddress(env); + + // Choose a random address in the range [232.0.1.0, 232.255.255.255) + // i.e., [0xE8000100, 0xE8FFFFFF) + netAddressBits const first = 0xE8000100, lastPlus1 = 0xE8FFFFFF; + netAddressBits const range = lastPlus1 - first; + + return htonl(first + ((netAddressBits)our_random())%range); +} + +char const* timestampString() { + struct timeval tvNow; + gettimeofday(&tvNow, NULL); + +#if !defined(_WIN32_WCE) + static char timeString[9]; // holds hh:mm:ss plus trailing '\0' + char const* ctimeResult = ctime((time_t*)&tvNow.tv_sec); + char const* from = &ctimeResult[11]; + int i; + for (i = 0; i < 8; ++i) { + timeString[i] = from[i]; + } + timeString[i] = '\0'; +#else + // WinCE apparently doesn't have "ctime()", so instead, construct + // a timestamp string just using the integer and fractional parts + // of "tvNow": + static char timeString[50]; + sprintf(timeString, "%lu.%06ld", tvNow.tv_sec, tvNow.tv_usec); +#endif + + return (char const*)&timeString; +} + +#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__) +// For Windoze, we need to implement our own gettimeofday() +#if !defined(_WIN32_WCE) +#include +#endif + +int gettimeofday(struct timeval* tp, int* /*tz*/) { +#if defined(_WIN32_WCE) + /* FILETIME of Jan 1 1970 00:00:00. */ + static const unsigned __int64 epoch = 116444736000000000LL; + + FILETIME file_time; + SYSTEMTIME system_time; + ULARGE_INTEGER ularge; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + ularge.LowPart = file_time.dwLowDateTime; + ularge.HighPart = file_time.dwHighDateTime; + + tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L); + tp->tv_usec = (long) (system_time.wMilliseconds * 1000); +#else + static LARGE_INTEGER tickFrequency, epochOffset; + + // For our first call, use "ftime()", so that we get a time with a proper epoch. + // For subsequent calls, use "QueryPerformanceCount()", because it's more fine-grain. + static Boolean isFirstCall = True; + + LARGE_INTEGER tickNow; + QueryPerformanceCounter(&tickNow); + + if (isFirstCall) { + struct timeb tb; + ftime(&tb); + tp->tv_sec = (long) tb.time; + tp->tv_usec = 1000*tb.millitm; + + // Also get our counter frequency: + QueryPerformanceFrequency(&tickFrequency); + + // And compute an offset to add to subsequent counter times, so we get a proper epoch: + epochOffset.QuadPart + = tb.time*tickFrequency.QuadPart + (tb.millitm*tickFrequency.QuadPart)/1000 - tickNow.QuadPart; + + isFirstCall = False; // for next time + } else { + // Adjust our counter time so that we get a proper epoch: + tickNow.QuadPart += epochOffset.QuadPart; + + tp->tv_sec = (long) (tickNow.QuadPart / tickFrequency.QuadPart); + tp->tv_usec = (long) (((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart); + } +#endif + return 0; +} +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/GroupEId.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/GroupEId.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/GroupEId.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/GroupEId.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,99 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "multikit" Multicast Application Shell +// Copyright (c) 1996-2012, Live Networks, Inc. All rights reserved +// "Group Endpoint Id" +// C++ header + +#ifndef _GROUPEID_HH +#define _GROUPEID_HH + +#ifndef _BOOLEAN_HH +#include "Boolean.hh" +#endif + +#ifndef _NET_ADDRESS_HH +#include "NetAddress.hh" +#endif + +const u_int8_t MAX_TTL = 255; + +class Scope { + public: + Scope(u_int8_t ttl = 0, const char* publicKey = NULL); + Scope(const Scope& orig); + Scope& operator=(const Scope& rightSide); + ~Scope(); + + u_int8_t ttl() const + { return fTTL; } + + const char* publicKey() const + { return fPublicKey; } + unsigned publicKeySize() const; + + private: + void assign(u_int8_t ttl, const char* publicKey); + void clean(); + + u_int8_t fTTL; + char* fPublicKey; +}; + +class GroupEId { +public: + GroupEId(struct in_addr const& groupAddr, + portNumBits portNum, Scope const& scope, + unsigned numSuccessiveGroupAddrs = 1); + // used for a 'source-independent multicast' group + GroupEId(struct in_addr const& groupAddr, + struct in_addr const& sourceFilterAddr, + portNumBits portNum, + unsigned numSuccessiveGroupAddrs = 1); + // used for a 'source-specific multicast' group + // margro: removed to fix Coverity CID 135519 + //GroupEId(); // used only as a temp constructor prior to initialization + + struct in_addr const& groupAddress() const { return fGroupAddress; } + struct in_addr const& sourceFilterAddress() const { return fSourceFilterAddress; } + + Boolean isSSM() const; + + unsigned numSuccessiveGroupAddrs() const { + // could be >1 for hier encoding + return fNumSuccessiveGroupAddrs; + } + + portNumBits portNum() const { return fPortNum; } + + const Scope& scope() const { return fScope; } + +private: + void init(struct in_addr const& groupAddr, + struct in_addr const& sourceFilterAddr, + portNumBits portNum, + Scope const& scope, + unsigned numSuccessiveGroupAddrs); + +private: + struct in_addr fGroupAddress; + struct in_addr fSourceFilterAddress; + unsigned fNumSuccessiveGroupAddrs; + portNumBits fPortNum; + Scope fScope; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/GroupsockHelper.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/GroupsockHelper.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/GroupsockHelper.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/GroupsockHelper.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,134 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Helper routines to implement 'group sockets' +// C++ header + +#ifndef _GROUPSOCK_HELPER_HH +#define _GROUPSOCK_HELPER_HH + +#ifndef _NET_ADDRESS_HH +#include "NetAddress.hh" +#endif + +int setupDatagramSocket(UsageEnvironment& env, Port port); +int setupStreamSocket(UsageEnvironment& env, + Port port, Boolean makeNonBlocking = True); + +int readSocket(UsageEnvironment& env, + int socket, unsigned char* buffer, unsigned bufferSize, + struct sockaddr_in& fromAddress, + struct timeval* timeout = NULL); + +int readSocketExact(UsageEnvironment& env, + int socket, unsigned char* buffer, unsigned bufferSize, + struct sockaddr_in& fromAddress, + struct timeval* timeout = NULL); + // like "readSocket()", except that it rereads as many times as needed until + // *exactly* "bufferSize" bytes are read. + +Boolean writeSocket(UsageEnvironment& env, + int socket, struct in_addr address, Port port, + u_int8_t ttlArg, + unsigned char* buffer, unsigned bufferSize); + +unsigned getSendBufferSize(UsageEnvironment& env, int socket); +unsigned getReceiveBufferSize(UsageEnvironment& env, int socket); +unsigned setSendBufferTo(UsageEnvironment& env, + int socket, unsigned requestedSize); +unsigned setReceiveBufferTo(UsageEnvironment& env, + int socket, unsigned requestedSize); +unsigned increaseSendBufferTo(UsageEnvironment& env, + int socket, unsigned requestedSize); +unsigned increaseReceiveBufferTo(UsageEnvironment& env, + int socket, unsigned requestedSize); + +Boolean makeSocketNonBlocking(int sock); +Boolean makeSocketBlocking(int sock); + +Boolean socketJoinGroup(UsageEnvironment& env, int socket, + netAddressBits groupAddress); +Boolean socketLeaveGroup(UsageEnvironment&, int socket, + netAddressBits groupAddress); + +// source-specific multicast join/leave +Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket, + netAddressBits groupAddress, + netAddressBits sourceFilterAddr); +Boolean socketLeaveGroupSSM(UsageEnvironment&, int socket, + netAddressBits groupAddress, + netAddressBits sourceFilterAddr); + +Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port); + +netAddressBits ourIPAddress(UsageEnvironment& env); // in network order + +// IP addresses of our sending and receiving interfaces. (By default, these +// are INADDR_ANY (i.e., 0), specifying the default interface.) +extern netAddressBits SendingInterfaceAddr; +extern netAddressBits ReceivingInterfaceAddr; + +// Allocates a randomly-chosen IPv4 SSM (multicast) address: +netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env); + +// Returns a simple "hh:mm:ss" string, for use in debugging output (e.g.) +char const* timestampString(); + + +#ifdef HAVE_SOCKADDR_LEN +#define SET_SOCKADDR_SIN_LEN(var) var.sin_len = sizeof var +#else +#define SET_SOCKADDR_SIN_LEN(var) +#endif + +#define MAKE_SOCKADDR_IN(var,adr,prt) /*adr,prt must be in network order*/\ + struct sockaddr_in var;\ + var.sin_family = AF_INET;\ + var.sin_addr.s_addr = (adr);\ + var.sin_port = (prt);\ + memset(var.sin_zero, 0, sizeof(var.sin_zero));\ + SET_SOCKADDR_SIN_LEN(var); + + +// By default, we create sockets with the SO_REUSE_* flag set. +// If, instead, you want to create sockets without the SO_REUSE_* flags, +// Then enclose the creation code with: +// { +// NoReuse dummy; +// ... +// } +class NoReuse { +public: + NoReuse(); + ~NoReuse(); +}; + + +#if (defined(__WIN32__) || defined(_WIN32)) && !defined(IMN_PIM) +// For Windoze, we need to implement our own gettimeofday() +extern int gettimeofday(struct timeval*, int*); +#endif + +// The following are implemented in inet.c: +extern "C" netAddressBits our_inet_addr(char const*); +extern "C" char* our_inet_ntoa(struct in_addr); +extern "C" struct hostent* our_gethostbyname(char* name); +extern "C" void our_srandom(int x); +extern "C" long our_random(); +extern "C" u_int32_t our_random32(); // because "our_random()" returns a 31-bit number + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/Groupsock.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/Groupsock.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/Groupsock.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/Groupsock.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,203 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// 'Group sockets' +// C++ header + +#ifndef _GROUPSOCK_HH +#define _GROUPSOCK_HH + +#ifndef _GROUPSOCK_VERSION_HH +#include "groupsock_version.hh" +#endif + +#ifndef _NET_INTERFACE_HH +#include "NetInterface.hh" +#endif + +#ifndef _GROUPEID_HH +#include "GroupEId.hh" +#endif + +// An "OutputSocket" is (by default) used only to send packets. +// No packets are received on it (unless a subclass arranges this) + +class OutputSocket: public Socket { +public: + OutputSocket(UsageEnvironment& env); + virtual ~OutputSocket(); + + Boolean write(netAddressBits address, Port port, u_int8_t ttl, + unsigned char* buffer, unsigned bufferSize); + +protected: + OutputSocket(UsageEnvironment& env, Port port); + + portNumBits sourcePortNum() const {return fSourcePort.num();} + +private: // redefined virtual function + virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize, + unsigned& bytesRead, + struct sockaddr_in& fromAddress); + +private: + Port fSourcePort; + u_int8_t fLastSentTTL; +}; + +class destRecord { +public: + destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, + destRecord* next); + virtual ~destRecord(); + +public: + destRecord* fNext; + GroupEId fGroupEId; + Port fPort; +}; + +// A "Groupsock" is used to both send and receive packets. +// As the name suggests, it was originally designed to send/receive +// multicast, but it can send/receive unicast as well. + +class Groupsock: public OutputSocket { +public: + Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr, + Port port, u_int8_t ttl); + // used for a 'source-independent multicast' group + Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr, + struct in_addr const& sourceFilterAddr, + Port port); + // used for a 'source-specific multicast' group + virtual ~Groupsock(); + + void changeDestinationParameters(struct in_addr const& newDestAddr, + Port newDestPort, int newDestTTL); + // By default, the destination address, port and ttl for + // outgoing packets are those that were specified in + // the constructor. This works OK for multicast sockets, + // but for unicast we usually want the destination port + // number, at least, to be different from the source port. + // (If a parameter is 0 (or ~0 for ttl), then no change made.) + + // As a special case, we also allow multiple destinations (addresses & ports) + // (This can be used to implement multi-unicast.) + void addDestination(struct in_addr const& addr, Port const& port); + void removeDestination(struct in_addr const& addr, Port const& port); + void removeAllDestinations(); + + struct in_addr const& groupAddress() const { + return fIncomingGroupEId.groupAddress(); + } + struct in_addr const& sourceFilterAddress() const { + return fIncomingGroupEId.sourceFilterAddress(); + } + + Boolean isSSM() const { + return fIncomingGroupEId.isSSM(); + } + + u_int8_t ttl() const { return fTTL; } + + void multicastSendOnly(); // send, but don't receive any multicast packets + + Boolean output(UsageEnvironment& env, u_int8_t ttl, + unsigned char* buffer, unsigned bufferSize, + DirectedNetInterface* interfaceNotToFwdBackTo = NULL); + + DirectedNetInterfaceSet& members() { return fMembers; } + + Boolean deleteIfNoMembers; + Boolean isSlave; // for tunneling + + static NetInterfaceTrafficStats statsIncoming; + static NetInterfaceTrafficStats statsOutgoing; + static NetInterfaceTrafficStats statsRelayedIncoming; + static NetInterfaceTrafficStats statsRelayedOutgoing; + NetInterfaceTrafficStats statsGroupIncoming; // *not* static + NetInterfaceTrafficStats statsGroupOutgoing; // *not* static + NetInterfaceTrafficStats statsGroupRelayedIncoming; // *not* static + NetInterfaceTrafficStats statsGroupRelayedOutgoing; // *not* static + + Boolean wasLoopedBackFromUs(UsageEnvironment& env, + struct sockaddr_in& fromAddress); + +public: // redefined virtual functions + virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize, + unsigned& bytesRead, + struct sockaddr_in& fromAddress); + +private: + int outputToAllMembersExcept(DirectedNetInterface* exceptInterface, + u_int8_t ttlToFwd, + unsigned char* data, unsigned size, + netAddressBits sourceAddr); + +private: + GroupEId fIncomingGroupEId; + destRecord* fDests; + u_int8_t fTTL; + DirectedNetInterfaceSet fMembers; +}; + +UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g); + +// A data structure for looking up a 'groupsock' +// by (multicast address, port), or by socket number +class GroupsockLookupTable { +public: + Groupsock* Fetch(UsageEnvironment& env, netAddressBits groupAddress, + Port port, u_int8_t ttl, Boolean& isNew); + // Creates a new Groupsock if none already exists + Groupsock* Fetch(UsageEnvironment& env, netAddressBits groupAddress, + netAddressBits sourceFilterAddr, + Port port, Boolean& isNew); + // Creates a new Groupsock if none already exists + Groupsock* Lookup(netAddressBits groupAddress, Port port); + // Returns NULL if none already exists + Groupsock* Lookup(netAddressBits groupAddress, + netAddressBits sourceFilterAddr, + Port port); + // Returns NULL if none already exists + Groupsock* Lookup(UsageEnvironment& env, int sock); + // Returns NULL if none already exists + Boolean Remove(Groupsock const* groupsock); + + // Used to iterate through the groupsocks in the table + class Iterator { + public: + Iterator(GroupsockLookupTable& groupsocks); + + Groupsock* next(); // NULL iff none + + private: + AddressPortLookupTable::Iterator fIter; + }; + +private: + Groupsock* AddNew(UsageEnvironment& env, + netAddressBits groupAddress, + netAddressBits sourceFilterAddress, + Port port, u_int8_t ttl); + +private: + friend class Iterator; + AddressPortLookupTable fTable; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/groupsock_version.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/groupsock_version.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/groupsock_version.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/groupsock_version.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,10 @@ +// Version information for the "groupsock" library +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. + +#ifndef _GROUPSOCK_VERSION_HH +#define _GROUPSOCK_VERSION_HH + +#define GROUPSOCK_LIBRARY_VERSION_STRING "2010.03.16" +#define GROUPSOCK_LIBRARY_VERSION_INT 1268697600 + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/IOHandlers.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/IOHandlers.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/IOHandlers.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/IOHandlers.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,31 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// IO event handlers +// C++ header + +#ifndef _IO_HANDLERS_HH +#define _IO_HANDLERS_HH + +#ifndef _NET_INTERFACE_HH +#include "NetInterface.hh" +#endif + +// Handles incoming data on sockets: +void socketReadHandler(Socket* sock, int mask); + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/NetAddress.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/NetAddress.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/NetAddress.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/NetAddress.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,162 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Network Addresses +// C++ header + +#ifndef _NET_ADDRESS_HH +#define _NET_ADDRESS_HH + +#ifndef _HASH_TABLE_HH +#include "HashTable.hh" +#endif + +#ifndef _NET_COMMON_H +#include "NetCommon.h" +#endif + +#ifndef _USAGE_ENVIRONMENT_HH +#include "UsageEnvironment.hh" +#endif + +// Definition of a type representing a low-level network address. +// At present, this is 32-bits, for IPv4. Later, generalize it, +// to allow for IPv6. +typedef u_int32_t netAddressBits; + +class NetAddress { +public: + NetAddress(u_int8_t const* data, + unsigned length = 4 /* default: 32 bits */); + NetAddress(unsigned length = 4); // sets address data to all-zeros + NetAddress(NetAddress const& orig); + NetAddress& operator=(NetAddress const& rightSide); + virtual ~NetAddress(); + + unsigned length() const { return fLength; } + u_int8_t const* data() const // always in network byte order + { return fData; } + +private: + void assign(u_int8_t const* data, unsigned length); + void clean(); + + unsigned fLength; + u_int8_t* fData; +}; + +class NetAddressList { +public: + NetAddressList(char const* hostname); + NetAddressList(NetAddressList const& orig); + NetAddressList& operator=(NetAddressList const& rightSide); + virtual ~NetAddressList(); + + unsigned numAddresses() const { return fNumAddresses; } + + NetAddress const* firstAddress() const; + + // Used to iterate through the addresses in a list: + class Iterator { + public: + Iterator(NetAddressList const& addressList); + NetAddress const* nextAddress(); // NULL iff none + private: + NetAddressList const& fAddressList; + unsigned fNextIndex; + }; + +private: + void assign(netAddressBits numAddresses, NetAddress** addressArray); + void clean(); + + friend class Iterator; + unsigned fNumAddresses; + NetAddress** fAddressArray; +}; + +typedef u_int16_t portNumBits; + +class Port { +public: + Port(portNumBits num /* in host byte order */); + + portNumBits num() const { return fPortNum; } // in network byte order + +private: + portNumBits fPortNum; // stored in network byte order +#ifdef IRIX + portNumBits filler; // hack to overcome a bug in IRIX C++ compiler +#endif +}; + +UsageEnvironment& operator<<(UsageEnvironment& s, const Port& p); + + +// A generic table for looking up objects by (address1, address2, port) +class AddressPortLookupTable { +public: + AddressPortLookupTable(); + virtual ~AddressPortLookupTable(); + + void* Add(netAddressBits address1, netAddressBits address2, Port port, void* value); + // Returns the old value if different, otherwise 0 + Boolean Remove(netAddressBits address1, netAddressBits address2, Port port); + void* Lookup(netAddressBits address1, netAddressBits address2, Port port); + // Returns 0 if not found + void* RemoveNext() { return fTable->RemoveNext(); } + + // Used to iterate through the entries in the table + class Iterator { + public: + Iterator(AddressPortLookupTable& table); + virtual ~Iterator(); + + void* next(); // NULL iff none + + private: + HashTable::Iterator* fIter; + }; + +private: + friend class Iterator; + HashTable* fTable; +}; + + +Boolean IsMulticastAddress(netAddressBits address); + + +// A mechanism for displaying an IPv4 address in ASCII. This is intended to replace "inet_ntoa()", which is not thread-safe. +class AddressString { +public: + AddressString(struct sockaddr_in const& addr); + AddressString(struct in_addr const& addr); + AddressString(netAddressBits addr); // "addr" is assumed to be in host byte order here + + virtual ~AddressString(); + + char const* val() const { return fVal; } + +private: + void init(netAddressBits addr); // used to implement each of the constructors + +private: + char* fVal; // The result ASCII string: allocated by the constructor; deleted by the destructor +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/NetCommon.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/NetCommon.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/NetCommon.h 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/NetCommon.h 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,132 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +/* "groupsock" interface + * Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. + * Common include files, typically used for networking + */ + +#ifndef _NET_COMMON_H +#define _NET_COMMON_H + +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE) +/* Windows */ +#if defined(WINNT) || defined(_WINNT) || defined(__BORLANDC__) || defined(__MINGW32__) || defined(_WIN32_WCE) || defined (_MSC_VER) +#define _MSWSOCK_ +#pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" +#include +#include +#pragma warning(default:4005) +#endif +#include +#include +#include + +#define closeSocket closesocket +#ifdef EWOULDBLOCK +#undef EWOULDBLOCK +#endif +#ifdef EINPROGRESS +#undef EINPROGRESS +#endif +#ifdef EAGAIN +#undef EAGAIN +#endif +#ifdef EINTR +#undef EINTR +#endif +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINPROGRESS WSAEWOULDBLOCK +#define EAGAIN WSAEWOULDBLOCK +#define EINTR WSAEINTR + +#if defined(_WIN32_WCE) +#define NO_STRSTREAM 1 +#endif + +/* Definitions of size-specific types: */ +typedef __int64 int64_t; +typedef unsigned __int64 u_int64_t; + +typedef int int32_t; +typedef unsigned u_int32_t; + +typedef short int16_t; +typedef unsigned short u_int16_t; + +typedef unsigned char u_int8_t; + +// For "uintptr_t" and "intptr_t", we assume that if they're not already defined, then this must be +// an old, 32-bit version of Windows: +#if !defined(_MSC_STDINT_H_) && !defined(_UINTPTR_T_DEFINED) && !defined(_UINTPTR_T_DECLARED) && !defined(_UINTPTR_T) +typedef unsigned uintptr_t; +#endif +#if !defined(_MSC_STDINT_H_) && !defined(_INTPTR_T_DEFINED) && !defined(_INTPTR_T_DECLARED) && !defined(_INTPTR_T) +typedef int intptr_t; +#endif + +#elif defined(VXWORKS) +/* VxWorks */ +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned int u_int32_t; +typedef unsigned short u_int16_t; +typedef unsigned char u_int8_t; + +#else +/* Unix */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_QNX4) +#include +#include +#endif + +#define closeSocket close + +#ifdef SOLARIS +#define u_int64_t uint64_t +#define u_int32_t uint32_t +#define u_int16_t uint16_t +#define u_int8_t uint8_t +#endif +#endif + +#ifndef SOCKLEN_T +#define SOCKLEN_T int +#endif + +#ifndef SOCKET_ERROR +#define SOCKET_ERROR -1 +#endif + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/NetInterface.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/NetInterface.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/NetInterface.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/NetInterface.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,149 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Network Interfaces +// C++ header + +#ifndef _NET_INTERFACE_HH +#define _NET_INTERFACE_HH + +#ifndef _NET_ADDRESS_HH +#include "NetAddress.hh" +#endif + +class NetInterface { +public: + virtual ~NetInterface(); + + static UsageEnvironment* DefaultUsageEnvironment; + // if non-NULL, used for each new interfaces + +protected: + NetInterface(); // virtual base class +}; + +class DirectedNetInterface: public NetInterface { +public: + virtual ~DirectedNetInterface(); + + virtual Boolean write(unsigned char* data, unsigned numBytes) = 0; + + virtual Boolean SourceAddrOKForRelaying(UsageEnvironment& env, + unsigned addr) = 0; + +protected: + DirectedNetInterface(); // virtual base class +}; + +class DirectedNetInterfaceSet { +public: + DirectedNetInterfaceSet(); + virtual ~DirectedNetInterfaceSet(); + + DirectedNetInterface* Add(DirectedNetInterface const* interf); + // Returns the old value if different, otherwise 0 + Boolean Remove(DirectedNetInterface const* interf); + + Boolean IsEmpty() { return fTable->IsEmpty(); } + + // Used to iterate through the interfaces in the set + class Iterator { + public: + Iterator(DirectedNetInterfaceSet& interfaces); + virtual ~Iterator(); + + DirectedNetInterface* next(); // NULL iff none + + private: + HashTable::Iterator* fIter; + }; + +private: + friend class Iterator; + HashTable* fTable; +}; + +class Socket: public NetInterface { +public: + virtual ~Socket(); + void reset(); // closes the socket, and sets "fSocketNum" to -1 + + virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize, + unsigned& bytesRead, + struct sockaddr_in& fromAddress) = 0; + // Returns False on error; resultData == NULL if data ignored + + int socketNum() const { return fSocketNum; } + + Port port() const { + return fPort; + } + + UsageEnvironment& env() const { return fEnv; } + + static int DebugLevel; + +protected: + Socket(UsageEnvironment& env, Port port); // virtual base class + + Boolean changePort(Port newPort); // will also cause socketNum() to change + +private: + int fSocketNum; + UsageEnvironment& fEnv; + Port fPort; +}; + +UsageEnvironment& operator<<(UsageEnvironment& s, const Socket& sock); + +// A data structure for looking up a Socket by port: + +class SocketLookupTable { +public: + virtual ~SocketLookupTable(); + + Socket* Fetch(UsageEnvironment& env, Port port, Boolean& isNew); + // Creates a new Socket if none already exists + Boolean Remove(Socket const* sock); + +protected: + SocketLookupTable(); // abstract base class + virtual Socket* CreateNew(UsageEnvironment& env, Port port) = 0; + +private: + HashTable* fTable; +}; + +// A data structure for counting traffic: + +class NetInterfaceTrafficStats { +public: + NetInterfaceTrafficStats(); + + void countPacket(unsigned packetSize); + + float totNumPackets() const {return fTotNumPackets;} + float totNumBytes() const {return fTotNumBytes;} + + Boolean haveSeenTraffic() const; + +private: + float fTotNumPackets; + float fTotNumBytes; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/TunnelEncaps.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/TunnelEncaps.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/include/TunnelEncaps.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/include/TunnelEncaps.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,101 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Encapsulation trailer for tunnels +// C++ header + +#ifndef _TUNNEL_ENCAPS_HH +#define _TUNNEL_ENCAPS_HH + +#ifndef _NET_ADDRESS_HH +#include "NetAddress.hh" +#endif + +typedef u_int16_t Cookie; + +class TunnelEncapsulationTrailer { + // The trailer is layed out as follows: + // bytes 0-1: source 'cookie' + // bytes 2-3: destination 'cookie' + // bytes 4-7: address + // bytes 8-9: port + // byte 10: ttl + // byte 11: command + + // Optionally, there may also be a 4-byte 'auxilliary address' + // (e.g., for 'source-specific multicast' preceding this) + // bytes -4 through -1: auxilliary address + + public: + Cookie& srcCookie() + { return *(Cookie*)byteOffset(0); } + Cookie& dstCookie() + { return *(Cookie*)byteOffset(2); } + u_int32_t& address() + { return *(u_int32_t*)byteOffset(4); } + Port& port() + { return *(Port*)byteOffset(8); } + u_int8_t& ttl() + { return *(u_int8_t*)byteOffset(10); } + u_int8_t& command() + { return *(u_int8_t*)byteOffset(11); } + + u_int32_t& auxAddress() + { return *(u_int32_t*)byteOffset(-4); } + + private: + inline char* byteOffset(int charIndex) + { return ((char*)this) + charIndex; } +}; + +const unsigned TunnelEncapsulationTrailerSize = 12; // bytes +const unsigned TunnelEncapsulationTrailerAuxSize = 4; // bytes +const unsigned TunnelEncapsulationTrailerMaxSize + = TunnelEncapsulationTrailerSize + TunnelEncapsulationTrailerAuxSize; + +// Command codes: +// 0: unused +const u_int8_t TunnelDataCmd = 1; +const u_int8_t TunnelJoinGroupCmd = 2; +const u_int8_t TunnelLeaveGroupCmd = 3; +const u_int8_t TunnelTearDownCmd = 4; +const u_int8_t TunnelProbeCmd = 5; +const u_int8_t TunnelProbeAckCmd = 6; +const u_int8_t TunnelProbeNackCmd = 7; +const u_int8_t TunnelJoinRTPGroupCmd = 8; +const u_int8_t TunnelLeaveRTPGroupCmd = 9; +// 0x0A through 0x10: currently unused. +const u_int8_t TunnelExtensionFlag = 0x80; // a flag, not a cmd code +const u_int8_t TunnelDataAuxCmd + = (TunnelExtensionFlag|TunnelDataCmd); +const u_int8_t TunnelJoinGroupAuxCmd + = (TunnelExtensionFlag|TunnelJoinGroupCmd); +const u_int8_t TunnelLeaveGroupAuxCmd + = (TunnelExtensionFlag|TunnelLeaveGroupCmd); +// Note: the TearDown, Probe, ProbeAck, ProbeNack cmds have no Aux version +// 0x84 through 0x87: currently unused. +const u_int8_t TunnelJoinRTPGroupAuxCmd + = (TunnelExtensionFlag|TunnelJoinRTPGroupCmd); +const u_int8_t TunnelLeaveRTPGroupAuxCmd + = (TunnelExtensionFlag|TunnelLeaveRTPGroupCmd); +// 0x8A through 0xFF: currently unused + +inline Boolean TunnelIsAuxCmd(u_int8_t cmd) { + return (cmd&TunnelExtensionFlag) != 0; +} + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/inet.c kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/inet.c --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/inet.c 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/inet.c 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,451 @@ +#ifndef _NET_COMMON_H +#include "NetCommon.h" +#endif + +#include + +#ifdef VXWORKS +#include +#endif + +/* Some systems (e.g., SunOS) have header files that erroneously declare inet_addr() as taking no arguments. + * This confuses C++. To overcome this, we use our own routine, implemented in C. + */ + +unsigned our_inet_addr(cp) + char const* cp; +{ + return inet_addr(cp); +} + +#if defined(__WIN32__) || defined(_WIN32) +#ifndef IMN_PIM +#define WS_VERSION_CHOICE1 0x202/*MAKEWORD(2,2)*/ +#define WS_VERSION_CHOICE2 0x101/*MAKEWORD(1,1)*/ +int initializeWinsockIfNecessary(void) { + /* We need to call an initialization routine before + * we can do anything with winsock. (How fucking lame!): + */ + static int _haveInitializedWinsock = 0; + WSADATA wsadata; + + if (!_haveInitializedWinsock) { + if ((WSAStartup(WS_VERSION_CHOICE1, &wsadata) != 0) + && ((WSAStartup(WS_VERSION_CHOICE2, &wsadata)) != 0)) { + return 0; /* error in initialization */ + } + if ((wsadata.wVersion != WS_VERSION_CHOICE1) + && (wsadata.wVersion != WS_VERSION_CHOICE2)) { + WSACleanup(); + return 0; /* desired Winsock version was not available */ + } + _haveInitializedWinsock = 1; + } + + return 1; +} +#else +int initializeWinsockIfNecessary(void) { return 1; } +#endif +#else +#define initializeWinsockIfNecessary() 1 +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#ifdef USE_SYSTEM_RANDOM +/* Use the system-supplied "random()" and "srandom()" functions */ +#include +long our_random() { +#if defined(__WIN32__) || defined(_WIN32) + return rand(); +#else + return random(); +#endif +} +void our_srandom(unsigned int x) { +#if defined(__WIN32__) || defined(_WIN32) + srand(x); +#else + srandom(x); +#endif +} + +#else + +/* Use our own implementation of the "random()" and "srandom()" functions */ +/* + * random.c: + * + * An improved random number generation package. In addition to the standard + * rand()/srand() like interface, this package also has a special state info + * interface. The our_initstate() routine is called with a seed, an array of + * bytes, and a count of how many bytes are being passed in; this array is + * then initialized to contain information for random number generation with + * that much state information. Good sizes for the amount of state + * information are 32, 64, 128, and 256 bytes. The state can be switched by + * calling the our_setstate() routine with the same array as was initiallized + * with our_initstate(). By default, the package runs with 128 bytes of state + * information and generates far better random numbers than a linear + * congruential generator. If the amount of state information is less than + * 32 bytes, a simple linear congruential R.N.G. is used. + * + * Internally, the state information is treated as an array of longs; the + * zeroeth element of the array is the type of R.N.G. being used (small + * integer); the remainder of the array is the state information for the + * R.N.G. Thus, 32 bytes of state information will give 7 longs worth of + * state information, which will allow a degree seven polynomial. (Note: + * the zeroeth word of state information also has some other information + * stored in it -- see our_setstate() for details). + * + * The random number generation technique is a linear feedback shift register + * approach, employing trinomials (since there are fewer terms to sum up that + * way). In this approach, the least significant bit of all the numbers in + * the state table will act as a linear feedback shift register, and will + * have period 2^deg - 1 (where deg is the degree of the polynomial being + * used, assuming that the polynomial is irreducible and primitive). The + * higher order bits will have longer periods, since their values are also + * influenced by pseudo-random carries out of the lower bits. The total + * period of the generator is approximately deg*(2**deg - 1); thus doubling + * the amount of state information has a vast influence on the period of the + * generator. Note: the deg*(2**deg - 1) is an approximation only good for + * large deg, when the period of the shift register is the dominant factor. + * With deg equal to seven, the period is actually much longer than the + * 7*(2**7 - 1) predicted by this formula. + */ + +/* + * For each of the currently supported random number generators, we have a + * break value on the amount of state information (you need at least this + * many bytes of state info to support this random number generator), a degree + * for the polynomial (actually a trinomial) that the R.N.G. is based on, and + * the separation between the two lower order coefficients of the trinomial. + */ +#define TYPE_0 0 /* linear congruential */ +#define BREAK_0 8 +#define DEG_0 0 +#define SEP_0 0 + +#define TYPE_1 1 /* x**7 + x**3 + 1 */ +#define BREAK_1 32 +#define DEG_1 7 +#define SEP_1 3 + +#define TYPE_2 2 /* x**15 + x + 1 */ +#define BREAK_2 64 +#define DEG_2 15 +#define SEP_2 1 + +#define TYPE_3 3 /* x**31 + x**3 + 1 */ +#define BREAK_3 128 +#define DEG_3 31 +#define SEP_3 3 + +#define TYPE_4 4 /* x**63 + x + 1 */ +#define BREAK_4 256 +#define DEG_4 63 +#define SEP_4 1 + +/* + * Array versions of the above information to make code run faster -- + * relies on fact that TYPE_i == i. + */ +#define MAX_TYPES 5 /* max number of types above */ + +static int const degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 }; +static int const seps [MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 }; + +/* + * Initially, everything is set up as if from: + * + * our_initstate(1, &randtbl, 128); + * + * Note that this initialization takes advantage of the fact that srandom() + * advances the front and rear pointers 10*rand_deg times, and hence the + * rear pointer which starts at 0 will also end up at zero; thus the zeroeth + * element of the state information, which contains info about the current + * position of the rear pointer is just + * + * MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3. + */ + +static long randtbl[DEG_3 + 1] = { + TYPE_3, + 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, 0xde3b81e0, 0xdf0a6fb5, + 0xf103bc02, 0x48f340fb, 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd, + 0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88, + 0xe369735d, 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, + 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, 0x8999220b, + 0x27fb47b9, +}; + +/* + * fptr and rptr are two pointers into the state info, a front and a rear + * pointer. These two pointers are always rand_sep places aparts, as they + * cycle cyclically through the state information. (Yes, this does mean we + * could get away with just one pointer, but the code for random() is more + * efficient this way). The pointers are left positioned as they would be + * from the call + * + * our_initstate(1, randtbl, 128); + * + * (The position of the rear pointer, rptr, is really 0 (as explained above + * in the initialization of randtbl) because the state table pointer is set + * to point to randtbl[1] (as explained below). + */ +static long* fptr = &randtbl[SEP_3 + 1]; +static long* rptr = &randtbl[1]; + +/* + * The following things are the pointer to the state information table, the + * type of the current generator, the degree of the current polynomial being + * used, and the separation between the two pointers. Note that for efficiency + * of random(), we remember the first location of the state information, not + * the zeroeth. Hence it is valid to access state[-1], which is used to + * store the type of the R.N.G. Also, we remember the last location, since + * this is more efficient than indexing every time to find the address of + * the last element to see if the front and rear pointers have wrapped. + */ +static long *state = &randtbl[1]; +static int rand_type = TYPE_3; +static int rand_deg = DEG_3; +static int rand_sep = SEP_3; +static long* end_ptr = &randtbl[DEG_3 + 1]; + +/* + * srandom: + * + * Initialize the random number generator based on the given seed. If the + * type is the trivial no-state-information type, just remember the seed. + * Otherwise, initializes state[] based on the given "seed" via a linear + * congruential generator. Then, the pointers are set to known locations + * that are exactly rand_sep places apart. Lastly, it cycles the state + * information a given number of times to get rid of any initial dependencies + * introduced by the L.C.R.N.G. Note that the initialization of randtbl[] + * for default usage relies on values produced by this routine. + */ +long our_random(void); /*forward*/ +void +our_srandom(unsigned int x) +{ + register int i; + + if (rand_type == TYPE_0) + state[0] = x; + else { + state[0] = x; + for (i = 1; i < rand_deg; i++) + state[i] = 1103515245 * state[i - 1] + 12345; + fptr = &state[rand_sep]; + rptr = &state[0]; + for (i = 0; i < 10 * rand_deg; i++) + (void)our_random(); + } +} + +/* + * our_initstate: + * + * Initialize the state information in the given array of n bytes for future + * random number generation. Based on the number of bytes we are given, and + * the break values for the different R.N.G.'s, we choose the best (largest) + * one we can and set things up for it. srandom() is then called to + * initialize the state information. + * + * Note that on return from srandom(), we set state[-1] to be the type + * multiplexed with the current value of the rear pointer; this is so + * successive calls to our_initstate() won't lose this information and will be + * able to restart with our_setstate(). + * + * Note: the first thing we do is save the current state, if any, just like + * our_setstate() so that it doesn't matter when our_initstate is called. + * + * Returns a pointer to the old state. + */ +char * +our_initstate(seed, arg_state, n) + unsigned int seed; /* seed for R.N.G. */ + char *arg_state; /* pointer to state array */ + int n; /* # bytes of state info */ +{ + register char *ostate = (char *)(&state[-1]); + + if (rand_type == TYPE_0) + state[-1] = rand_type; + else + state[-1] = MAX_TYPES * (rptr - state) + rand_type; + if (n < BREAK_0) { +#ifdef DEBUG + (void)fprintf(stderr, + "random: not enough state (%d bytes); ignored.\n", n); +#endif + return(0); + } + if (n < BREAK_1) { + rand_type = TYPE_0; + rand_deg = DEG_0; + rand_sep = SEP_0; + } else if (n < BREAK_2) { + rand_type = TYPE_1; + rand_deg = DEG_1; + rand_sep = SEP_1; + } else if (n < BREAK_3) { + rand_type = TYPE_2; + rand_deg = DEG_2; + rand_sep = SEP_2; + } else if (n < BREAK_4) { + rand_type = TYPE_3; + rand_deg = DEG_3; + rand_sep = SEP_3; + } else { + rand_type = TYPE_4; + rand_deg = DEG_4; + rand_sep = SEP_4; + } + state = &(((long *)arg_state)[1]); /* first location */ + end_ptr = &state[rand_deg]; /* must set end_ptr before srandom */ + our_srandom(seed); + if (rand_type == TYPE_0) + state[-1] = rand_type; + else + state[-1] = MAX_TYPES*(rptr - state) + rand_type; + return(ostate); +} + +/* + * our_setstate: + * + * Restore the state from the given state array. + * + * Note: it is important that we also remember the locations of the pointers + * in the current state information, and restore the locations of the pointers + * from the old state information. This is done by multiplexing the pointer + * location into the zeroeth word of the state information. + * + * Note that due to the order in which things are done, it is OK to call + * our_setstate() with the same state as the current state. + * + * Returns a pointer to the old state information. + */ +char * +our_setstate(arg_state) + char *arg_state; +{ + register long *new_state = (long *)arg_state; + register int type = new_state[0] % MAX_TYPES; + register int rear = new_state[0] / MAX_TYPES; + char *ostate = (char *)(&state[-1]); + + if (rand_type == TYPE_0) + state[-1] = rand_type; + else + state[-1] = MAX_TYPES * (rptr - state) + rand_type; + switch(type) { + case TYPE_0: + case TYPE_1: + case TYPE_2: + case TYPE_3: + case TYPE_4: + rand_type = type; + rand_deg = degrees[type]; + rand_sep = seps[type]; + break; + default: +#ifdef DEBUG + (void)fprintf(stderr, + "random: state info corrupted; not changed.\n"); +#endif + break; + } + state = &new_state[1]; + if (rand_type != TYPE_0) { + rptr = &state[rear]; + fptr = &state[(rear + rand_sep) % rand_deg]; + } + end_ptr = &state[rand_deg]; /* set end_ptr too */ + return(ostate); +} + +/* + * random: + * + * If we are using the trivial TYPE_0 R.N.G., just do the old linear + * congruential bit. Otherwise, we do our fancy trinomial stuff, which is + * the same in all the other cases due to all the global variables that have + * been set up. The basic operation is to add the number at the rear pointer + * into the one at the front pointer. Then both pointers are advanced to + * the next location cyclically in the table. The value returned is the sum + * generated, reduced to 31 bits by throwing away the "least random" low bit. + * + * Note: the code takes advantage of the fact that both the front and + * rear pointers can't wrap on the same call by not testing the rear + * pointer if the front one has wrapped. + * + * Returns a 31-bit random number. + */ +long our_random() { + long i; + + if (rand_type == TYPE_0) { + i = state[0] = (state[0] * 1103515245 + 12345) & 0x7fffffff; + } else { + /* Make copies of "rptr" and "fptr" before working with them, in case we're being called concurrently by multiple threads: */ + long* rp = rptr; + long* fp = fptr; + + /* Make sure "rp" and "fp" are separated by the correct distance (again, allowing for concurrent access): */ + if (!(fp == rp+SEP_3 || fp+DEG_3 == rp+SEP_3)) { + /* A rare case that should occur only if we're being called concurrently by multiple threads. */ + /* Restore the proper separation between the pointers: */ + if (rp <= fp) rp = fp-SEP_3; else rp = fp+DEG_3-SEP_3; + } + + *fp += *rp; + i = (*fp >> 1) & 0x7fffffff; /* chucking least random bit */ + if (++fp >= end_ptr) { + fp = state; + ++rp; + } else if (++rp >= end_ptr) { + rp = state; + } + + /* Restore "rptr" and "fptr" from our working copies: */ + rptr = rp; + fptr = fp; + } + + return i; +} +#endif + +u_int32_t our_random32() { + /* Return a 32-bit random number. + Because "our_random()" returns a 31-bit random number, we call it a second + time, to generate the high bit. + (Actually, to increase the likelhood of randomness, we take the middle 16 bits of two successive calls to "our_random()") + */ + long random_1 = our_random(); + u_int32_t random16_1 = (u_int32_t)(random_1&0x00FFFF00); + + long random_2 = our_random(); + u_int32_t random16_2 = (u_int32_t)(random_2&0x00FFFF00); + + return (random16_1<<8) | (random16_2>>8); +} + +#ifdef USE_OUR_BZERO +#ifndef __bzero +void +__bzero (to, count) + char *to; + int count; +{ + while (count-- > 0) + { + *to++ = 0; + } +} +#endif +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/IOHandlers.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/IOHandlers.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/IOHandlers.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/IOHandlers.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,46 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// IO event handlers +// Implementation + +#include "IOHandlers.hh" +#include "TunnelEncaps.hh" + +//##### TEMP: Use a single buffer, sized for UDP tunnels: +//##### This assumes that the I/O handlers are non-reentrant +static unsigned const maxPacketLength = 50*1024; // bytes + // This is usually overkill, because UDP packets are usually no larger + // than the typical Ethernet MTU (1500 bytes). However, I've seen + // reports of Windows Media Servers sending UDP packets as large as + // 27 kBytes. These will probably undego lots of IP-level + // fragmentation, but that occurs below us. We just have to hope that + // fragments don't get lost. +static unsigned const ioBufferSize + = maxPacketLength + TunnelEncapsulationTrailerMaxSize; +static unsigned char ioBuffer[ioBufferSize]; + + +void socketReadHandler(Socket* sock, int /*mask*/) { + unsigned bytesRead; + struct sockaddr_in fromAddress; + UsageEnvironment& saveEnv = sock->env(); + // because handleRead(), if it fails, may delete "sock" + if (!sock->handleRead(ioBuffer, ioBufferSize, bytesRead, fromAddress)) { + saveEnv.reportBackgroundError(); + } +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/NetAddress.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/NetAddress.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/NetAddress.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/NetAddress.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,312 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Network Addresses +// Implementation + +#include "NetAddress.hh" +#include "GroupsockHelper.hh" + +#include +#include +#if defined(__WIN32__) || defined(_WIN32) +#define USE_GETHOSTBYNAME 1 /*because at least some Windows don't have getaddrinfo()*/ +#else +#ifndef INADDR_NONE +#define INADDR_NONE 0xFFFFFFFF +#endif +#endif + +////////// NetAddress ////////// + +NetAddress::NetAddress(u_int8_t const* data, unsigned length) { + assign(data, length); +} + +NetAddress::NetAddress(unsigned length) { + fData = new u_int8_t[length]; + if (fData == NULL) { + fLength = 0; + return; + } + + for (unsigned i = 0; i < length; ++i) fData[i] = 0; + fLength = length; +} + +NetAddress::NetAddress(NetAddress const& orig) { + assign(orig.data(), orig.length()); +} + +NetAddress& NetAddress::operator=(NetAddress const& rightSide) { + if (&rightSide != this) { + clean(); + assign(rightSide.data(), rightSide.length()); + } + return *this; +} + +NetAddress::~NetAddress() { + clean(); +} + +void NetAddress::assign(u_int8_t const* data, unsigned length) { + fData = new u_int8_t[length]; + if (fData == NULL) { + fLength = 0; + return; + } + + for (unsigned i = 0; i < length; ++i) fData[i] = data[i]; + fLength = length; +} + +void NetAddress::clean() { + delete[] fData; fData = NULL; + fLength = 0; +} + + +////////// NetAddressList ////////// + +NetAddressList::NetAddressList(char const* hostname) + : fNumAddresses(0), fAddressArray(NULL) { + // First, check whether "hostname" is an IP address string: + netAddressBits addr = our_inet_addr((char*)hostname); + if (addr != INADDR_NONE) { + // Yes, it was an IP address string. Return a 1-element list with this address: + fNumAddresses = 1; + fAddressArray = new NetAddress*[fNumAddresses]; + if (fAddressArray == NULL) return; + + fAddressArray[0] = new NetAddress((u_int8_t*)&addr, sizeof (netAddressBits)); + return; + } + + // "hostname" is not an IP address string; try resolving it as a real host name instead: +#if defined(USE_GETHOSTBYNAME) || defined(VXWORKS) + struct hostent* host; +#if defined(VXWORKS) + char hostentBuf[512]; + + host = (struct hostent*)resolvGetHostByName((char*)hostname, (char*)&hostentBuf, sizeof hostentBuf); +#else + host = gethostbyname((char*)hostname); +#endif + if (host == NULL || host->h_length != 4 || host->h_addr_list == NULL) return; // no luck + + u_int8_t const** const hAddrPtr = (u_int8_t const**)host->h_addr_list; + // First, count the number of addresses: + u_int8_t const** hAddrPtr1 = hAddrPtr; + while (*hAddrPtr1 != NULL) { + ++fNumAddresses; + ++hAddrPtr1; + } + + // Next, set up the list: + fAddressArray = new NetAddress*[fNumAddresses]; + if (fAddressArray == NULL) return; + + for (unsigned i = 0; i < fNumAddresses; ++i) { + fAddressArray[i] = new NetAddress(hAddrPtr[i], host->h_length); + } +#else + // Use "getaddrinfo()" (rather than the older, deprecated "gethostbyname()"): + struct addrinfo addrinfoHints; + memset(&addrinfoHints, 0, sizeof addrinfoHints); + addrinfoHints.ai_family = AF_INET; // For now, we're interested in IPv4 addresses only + struct addrinfo* addrinfoResultPtr = NULL; + int result = getaddrinfo(hostname, NULL, &addrinfoHints, &addrinfoResultPtr); + if (result != 0 || addrinfoResultPtr == NULL) return; // no luck + + // First, count the number of addresses: + const struct addrinfo* p = addrinfoResultPtr; + while (p != NULL) { + if (p->ai_addrlen < 4) continue; // sanity check: skip over addresses that are too small + ++fNumAddresses; + p = p->ai_next; + } + + // Next, set up the list: + fAddressArray = new NetAddress*[fNumAddresses]; + if (fAddressArray == NULL) return; + + unsigned i = 0; + p = addrinfoResultPtr; + while (p != NULL) { + if (p->ai_addrlen < 4) continue; + fAddressArray[i++] = new NetAddress((u_int8_t const*)&(((struct sockaddr_in*)p->ai_addr)->sin_addr.s_addr), 4); + p = p->ai_next; + } + + // Finally, free the data that we had allocated by calling "getaddrinfo()": + freeaddrinfo(addrinfoResultPtr); +#endif +} + +NetAddressList::NetAddressList(NetAddressList const& orig) { + assign(orig.numAddresses(), orig.fAddressArray); +} + +NetAddressList& NetAddressList::operator=(NetAddressList const& rightSide) { + if (&rightSide != this) { + clean(); + assign(rightSide.numAddresses(), rightSide.fAddressArray); + } + return *this; +} + +NetAddressList::~NetAddressList() { + clean(); +} + +void NetAddressList::assign(unsigned numAddresses, NetAddress** addressArray) { + fAddressArray = new NetAddress*[numAddresses]; + if (fAddressArray == NULL) { + fNumAddresses = 0; + return; + } + + for (unsigned i = 0; i < numAddresses; ++i) { + fAddressArray[i] = new NetAddress(*addressArray[i]); + } + fNumAddresses = numAddresses; +} + +void NetAddressList::clean() { + while (fNumAddresses-- > 0) { + delete fAddressArray[fNumAddresses]; + } + delete[] fAddressArray; fAddressArray = NULL; +} + +NetAddress const* NetAddressList::firstAddress() const { + if (fNumAddresses == 0) return NULL; + + return fAddressArray[0]; +} + +////////// NetAddressList::Iterator ////////// +NetAddressList::Iterator::Iterator(NetAddressList const& addressList) + : fAddressList(addressList), fNextIndex(0) {} + +NetAddress const* NetAddressList::Iterator::nextAddress() { + if (fNextIndex >= fAddressList.numAddresses()) return NULL; // no more + return fAddressList.fAddressArray[fNextIndex++]; +} + + +////////// Port ////////// + +Port::Port(portNumBits num /* in host byte order */) { + fPortNum = htons(num); +} + +UsageEnvironment& operator<<(UsageEnvironment& s, const Port& p) { + return s << ntohs(p.num()); +} + + +////////// AddressPortLookupTable ////////// + +AddressPortLookupTable::AddressPortLookupTable() + : fTable(HashTable::create(3)) { // three-word keys are used +} + +AddressPortLookupTable::~AddressPortLookupTable() { + delete fTable; +} + +void* AddressPortLookupTable::Add(netAddressBits address1, + netAddressBits address2, + Port port, void* value) { + int key[3]; + key[0] = (int)address1; + key[1] = (int)address2; + key[2] = (int)port.num(); + return fTable->Add((char*)key, value); +} + +void* AddressPortLookupTable::Lookup(netAddressBits address1, + netAddressBits address2, + Port port) { + int key[3]; + key[0] = (int)address1; + key[1] = (int)address2; + key[2] = (int)port.num(); + return fTable->Lookup((char*)key); +} + +Boolean AddressPortLookupTable::Remove(netAddressBits address1, + netAddressBits address2, + Port port) { + int key[3]; + key[0] = (int)address1; + key[1] = (int)address2; + key[2] = (int)port.num(); + return fTable->Remove((char*)key); +} + +AddressPortLookupTable::Iterator::Iterator(AddressPortLookupTable& table) + : fIter(HashTable::Iterator::create(*(table.fTable))) { +} + +AddressPortLookupTable::Iterator::~Iterator() { + delete fIter; +} + +void* AddressPortLookupTable::Iterator::next() { + char const* key; // dummy + return fIter->next(key); +} + + +////////// isMulticastAddress() implementation ////////// + +Boolean IsMulticastAddress(netAddressBits address) { + // Note: We return False for addresses in the range 224.0.0.0 + // through 224.0.0.255, because these are non-routable + // Note: IPv4-specific ##### + netAddressBits addressInNetworkOrder = htonl(address); + return addressInNetworkOrder > 0xE00000FF && + addressInNetworkOrder <= 0xEFFFFFFF; +} + + +////////// AddressString implementation ////////// + +AddressString::AddressString(struct sockaddr_in const& addr) { + init(addr.sin_addr.s_addr); +} + +AddressString::AddressString(struct in_addr const& addr) { + init(addr.s_addr); +} + +AddressString::AddressString(netAddressBits addr) { + init(addr); +} + +void AddressString::init(netAddressBits addr) { + fVal = new char[16]; // large enough for "abc.def.ghi.jkl" + netAddressBits addrNBO = htonl(addr); // make sure we have a value in a known byte order: big endian + sprintf(fVal, "%u.%u.%u.%u", (addrNBO>>24)&0xFF, (addrNBO>>16)&0xFF, (addrNBO>>8)&0xFF, addrNBO&0xFF); +} + +AddressString::~AddressString() { + delete[] fVal; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/NetInterface.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/NetInterface.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/groupsock/NetInterface.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/groupsock/NetInterface.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,174 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "mTunnel" multicast access service +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Network Interfaces +// Implementation + +#include "NetInterface.hh" +#include "GroupsockHelper.hh" + +#ifndef NO_SSTREAM +#include +#endif +#include + +////////// NetInterface ////////// + +UsageEnvironment* NetInterface::DefaultUsageEnvironment = NULL; + +NetInterface::NetInterface() { +} + +NetInterface::~NetInterface() { +} + + +////////// NetInterface ////////// + +DirectedNetInterface::DirectedNetInterface() { +} + +DirectedNetInterface::~DirectedNetInterface() { +} + + +////////// DirectedNetInterfaceSet ////////// + +DirectedNetInterfaceSet::DirectedNetInterfaceSet() + : fTable(HashTable::create(ONE_WORD_HASH_KEYS)) { +} + +DirectedNetInterfaceSet::~DirectedNetInterfaceSet() { + delete fTable; +} + +DirectedNetInterface* +DirectedNetInterfaceSet::Add(DirectedNetInterface const* interf) { + return (DirectedNetInterface*) fTable->Add((char*)interf, (void*)interf); +} + +Boolean +DirectedNetInterfaceSet::Remove(DirectedNetInterface const* interf) { + return fTable->Remove((char*)interf); +} + +DirectedNetInterfaceSet::Iterator:: +Iterator(DirectedNetInterfaceSet& interfaces) + : fIter(HashTable::Iterator::create(*(interfaces.fTable))) { +} + +DirectedNetInterfaceSet::Iterator::~Iterator() { + delete fIter; +} + +DirectedNetInterface* DirectedNetInterfaceSet::Iterator::next() { + char const* key; // dummy + return (DirectedNetInterface*) fIter->next(key); +}; + + +////////// Socket ////////// + +int Socket::DebugLevel = 1; // default value + +Socket::Socket(UsageEnvironment& env, Port port) + : fEnv(DefaultUsageEnvironment != NULL ? *DefaultUsageEnvironment : env), fPort(port) { + fSocketNum = setupDatagramSocket(fEnv, port); +} + +void Socket::reset() { + closeSocket(fSocketNum); + fSocketNum = -1; +} + +Socket::~Socket() { + reset(); +} + +Boolean Socket::changePort(Port newPort) { + int oldSocketNum = fSocketNum; + unsigned oldReceiveBufferSize = getReceiveBufferSize(fEnv, fSocketNum); + unsigned oldSendBufferSize = getSendBufferSize(fEnv, fSocketNum); + closeSocket(fSocketNum); + + fSocketNum = setupDatagramSocket(fEnv, newPort); + if (fSocketNum < 0) { + fEnv.taskScheduler().turnOffBackgroundReadHandling(oldSocketNum); + return False; + } + + setReceiveBufferTo(fEnv, fSocketNum, oldReceiveBufferSize); + setSendBufferTo(fEnv, fSocketNum, oldSendBufferSize); + if (fSocketNum != oldSocketNum) { // the socket number has changed, so move any event handling for it: + fEnv.taskScheduler().moveSocketHandling(oldSocketNum, fSocketNum); + } + return True; +} + +UsageEnvironment& operator<<(UsageEnvironment& s, const Socket& sock) { + return s << timestampString() << " Socket(" << sock.socketNum() << ")"; +} + +////////// SocketLookupTable ////////// + +SocketLookupTable::SocketLookupTable() + : fTable(HashTable::create(ONE_WORD_HASH_KEYS)) { +} + +SocketLookupTable::~SocketLookupTable() { + delete fTable; +} + +Socket* SocketLookupTable::Fetch(UsageEnvironment& env, Port port, + Boolean& isNew) { + isNew = False; + Socket* sock; + do { + sock = (Socket*) fTable->Lookup((char*)(long)(port.num())); + if (sock == NULL) { // we need to create one: + sock = CreateNew(env, port); + if (sock == NULL || sock->socketNum() < 0) break; + + fTable->Add((char*)(long)(port.num()), (void*)sock); + isNew = True; + } + + return sock; + } while (0); + + delete sock; + return NULL; +} + +Boolean SocketLookupTable::Remove(Socket const* sock) { + return fTable->Remove( (char*)(long)(sock->port().num()) ); +} + +////////// NetInterfaceTrafficStats ////////// + +NetInterfaceTrafficStats::NetInterfaceTrafficStats() { + fTotNumPackets = fTotNumBytes = 0.0; +} + +void NetInterfaceTrafficStats::countPacket(unsigned packetSize) { + fTotNumPackets += 1.0; + fTotNumBytes += packetSize; +} + +Boolean NetInterfaceTrafficStats::haveSeenTraffic() const { + return fTotNumPackets != 0.0; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/Base64.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/Base64.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/Base64.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/Base64.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,122 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Base64 encoding and decoding +// implementation + +#include "Base64.hh" +#include +#include + +static char base64DecodeTable[256]; + +static void initBase64DecodeTable() { + int i; + for (i = 0; i < 256; ++i) base64DecodeTable[i] = (char)0x80; + // default value: invalid + + for (i = 'A'; i <= 'Z'; ++i) base64DecodeTable[i] = 0 + (i - 'A'); + for (i = 'a'; i <= 'z'; ++i) base64DecodeTable[i] = 26 + (i - 'a'); + for (i = '0'; i <= '9'; ++i) base64DecodeTable[i] = 52 + (i - '0'); + base64DecodeTable[(unsigned char)'+'] = 62; + base64DecodeTable[(unsigned char)'/'] = 63; + base64DecodeTable[(unsigned char)'='] = 0; +} + +unsigned char* base64Decode(char const* in, unsigned& resultSize, + Boolean trimTrailingZeros) { + if (in == NULL) return NULL; // sanity check + return base64Decode(in, strlen(in), resultSize, trimTrailingZeros); +} + +unsigned char* base64Decode(char const* in, unsigned inSize, + unsigned& resultSize, + Boolean trimTrailingZeros) { + static Boolean haveInitializedBase64DecodeTable = False; + if (!haveInitializedBase64DecodeTable) { + initBase64DecodeTable(); + haveInitializedBase64DecodeTable = True; + } + + unsigned char* out = (unsigned char*)strDupSize(in); // ensures we have enough space + int k = 0; + int paddingCount = 0; + int const jMax = inSize - 3; + // in case "inSize" is not a multiple of 4 (although it should be) + for (int j = 0; j < jMax; j += 4) { + char inTmp[4], outTmp[4]; + for (int i = 0; i < 4; ++i) { + inTmp[i] = in[i+j]; + if (inTmp[i] == '=') ++paddingCount; + outTmp[i] = base64DecodeTable[(unsigned char)inTmp[i]]; + if ((outTmp[i]&0x80) != 0) outTmp[i] = 0; // this happens only if there was an invalid character; pretend that it was 'A' + } + + out[k++] = (outTmp[0]<<2) | (outTmp[1]>>4); + out[k++] = (outTmp[1]<<4) | (outTmp[2]>>2); + out[k++] = (outTmp[2]<<6) | outTmp[3]; + } + + if (trimTrailingZeros) { + while (paddingCount > 0 && k > 0 && out[k-1] == '\0') { --k; --paddingCount; } + } + resultSize = k; + unsigned char* result = new unsigned char[resultSize]; + memmove(result, out, resultSize); + delete[] out; + + return result; +} + +static const char base64Char[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +char* base64Encode(char const* origSigned, unsigned origLength) { + unsigned char const* orig = (unsigned char const*)origSigned; // in case any input bytes have the MSB set + if (orig == NULL) return NULL; + + unsigned const numOrig24BitValues = origLength/3; + Boolean havePadding = origLength > numOrig24BitValues*3; + Boolean havePadding2 = origLength == numOrig24BitValues*3 + 2; + unsigned const numResultBytes = 4*(numOrig24BitValues + havePadding); + char* result = new char[numResultBytes+1]; // allow for trailing '\0' + + // Map each full group of 3 input bytes into 4 output base-64 characters: + unsigned i; + for (i = 0; i < numOrig24BitValues; ++i) { + result[4*i+0] = base64Char[(orig[3*i]>>2)&0x3F]; + result[4*i+1] = base64Char[(((orig[3*i]&0x3)<<4) | (orig[3*i+1]>>4))&0x3F]; + result[4*i+2] = base64Char[((orig[3*i+1]<<2) | (orig[3*i+2]>>6))&0x3F]; + result[4*i+3] = base64Char[orig[3*i+2]&0x3F]; + } + + // Now, take padding into account. (Note: i == numOrig24BitValues) + if (havePadding) { + result[4*i+0] = base64Char[(orig[3*i]>>2)&0x3F]; + if (havePadding2) { + result[4*i+1] = base64Char[(((orig[3*i]&0x3)<<4) | (orig[3*i+1]>>4))&0x3F]; + result[4*i+2] = base64Char[(orig[3*i+1]<<2)&0x3F]; + } else { + result[4*i+1] = base64Char[((orig[3*i]&0x3)<<4)&0x3F]; + result[4*i+2] = '='; + } + result[4*i+3] = '='; + } + + result[numResultBytes] = '\0'; + return result; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/BasicUDPSource.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/BasicUDPSource.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/BasicUDPSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/BasicUDPSource.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,73 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// A simple UDP source, where every UDP payload is a complete frame +// Implementation + +#include "BasicUDPSource.hh" +#include + +BasicUDPSource* BasicUDPSource::createNew(UsageEnvironment& env, + Groupsock* inputGS) { + return new BasicUDPSource(env, inputGS); +} + +BasicUDPSource::BasicUDPSource(UsageEnvironment& env, Groupsock* inputGS) + : FramedSource(env), fInputGS(inputGS), fHaveStartedReading(False) { + // Try to use a large receive buffer (in the OS): + increaseReceiveBufferTo(env, inputGS->socketNum(), 50*1024); + + // Make the socket non-blocking, even though it will be read from only asynchronously, when packets arrive. + // The reason for this is that, in some OSs, reads on a blocking socket can (allegedly) sometimes block, + // even if the socket was previously reported (e.g., by "select()") as having data available. + // (This can supposedly happen if the UDP checksum fails, for example.) + makeSocketNonBlocking(fInputGS->socketNum()); +} + +BasicUDPSource::~BasicUDPSource(){ + envir().taskScheduler().turnOffBackgroundReadHandling(fInputGS->socketNum()); +} + +void BasicUDPSource::doGetNextFrame() { + if (!fHaveStartedReading) { + // Await incoming packets: + envir().taskScheduler().turnOnBackgroundReadHandling(fInputGS->socketNum(), + (TaskScheduler::BackgroundHandlerProc*)&incomingPacketHandler, this); + fHaveStartedReading = True; + } +} + +void BasicUDPSource::doStopGettingFrames() { + envir().taskScheduler().turnOffBackgroundReadHandling(fInputGS->socketNum()); + fHaveStartedReading = False; +} + + +void BasicUDPSource::incomingPacketHandler(BasicUDPSource* source, int /*mask*/){ + source->incomingPacketHandler1(); +} + +void BasicUDPSource::incomingPacketHandler1() { + if (!isCurrentlyAwaitingData()) return; // we're not ready for the data yet + + // Read the packet into our desired destination: + struct sockaddr_in fromAddress; + if (!fInputGS->handleRead(fTo, fMaxSize, fFrameSize, fromAddress)) return; + + // Tell our client that we have new data: + afterGetting(this); // we're preceded by a net read; no infinite recursion +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/COPYING kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/COPYING --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/COPYING 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/COPYING 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/DigestAuthentication.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/DigestAuthentication.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/DigestAuthentication.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/DigestAuthentication.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,157 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// A class used for digest authentication. +// Implementation + +#include "DigestAuthentication.hh" +#include "our_md5.h" +#include +#include // for gettimeofday() +#include +#include +#include + +Authenticator::Authenticator() { + assign(NULL, NULL, NULL, NULL, False); +} + +Authenticator::Authenticator(const Authenticator& orig) { + assign(orig.realm(), orig.nonce(), orig.username(), orig.password(), + orig.fPasswordIsMD5); +} + +Authenticator& Authenticator::operator=(const Authenticator& rightSide) { + if (&rightSide != this) { + reset(); + assign(rightSide.realm(), rightSide.nonce(), + rightSide.username(), rightSide.password(), rightSide.fPasswordIsMD5); + } + + return *this; +} + +Authenticator::~Authenticator() { + reset(); +} + +void Authenticator::reset() { + resetRealmAndNonce(); + resetUsernameAndPassword(); +} + +void Authenticator::setRealmAndNonce(char const* realm, char const* nonce) { + resetRealmAndNonce(); + assignRealmAndNonce(realm, nonce); +} + +void Authenticator::setRealmAndRandomNonce(char const* realm) { + resetRealmAndNonce(); + + // Construct data to seed the random nonce: + struct { + struct timeval timestamp; + unsigned counter; + } seedData; + gettimeofday(&seedData.timestamp, NULL); + static unsigned counter = 0; + seedData.counter = ++counter; + + // Use MD5 to compute a 'random' nonce from this seed data: + char nonceBuf[33]; + our_MD5Data((unsigned char*)(&seedData), sizeof seedData, nonceBuf); + + assignRealmAndNonce(realm, nonceBuf); +} + +void Authenticator::setUsernameAndPassword(char const* username, + char const* password, + Boolean passwordIsMD5) { + resetUsernameAndPassword(); + assignUsernameAndPassword(username, password, passwordIsMD5); +} + +char const* Authenticator::computeDigestResponse(char const* cmd, + char const* url) const { + // The "response" field is computed as: + // md5(md5(::)::md5(:)) + // or, if "fPasswordIsMD5" is True: + // md5(::md5(:)) + char ha1Buf[33]; + if (fPasswordIsMD5) { + strncpy(ha1Buf, password(), 32); + ha1Buf[32] = '\0'; // just in case + } else { + unsigned const ha1DataLen = strlen(username()) + 1 + + strlen(realm()) + 1 + strlen(password()); + unsigned char* ha1Data = new unsigned char[ha1DataLen+1]; + sprintf((char*)ha1Data, "%s:%s:%s", username(), realm(), password()); + our_MD5Data(ha1Data, ha1DataLen, ha1Buf); + delete[] ha1Data; + } + + unsigned const ha2DataLen = strlen(cmd) + 1 + strlen(url); + unsigned char* ha2Data = new unsigned char[ha2DataLen+1]; + sprintf((char*)ha2Data, "%s:%s", cmd, url); + char ha2Buf[33]; + our_MD5Data(ha2Data, ha2DataLen, ha2Buf); + delete[] ha2Data; + + unsigned const digestDataLen + = 32 + 1 + strlen(nonce()) + 1 + 32; + unsigned char* digestData = new unsigned char[digestDataLen+1]; + sprintf((char*)digestData, "%s:%s:%s", + ha1Buf, nonce(), ha2Buf); + char const* result = our_MD5Data(digestData, digestDataLen, NULL); + delete[] digestData; + return result; +} + +void Authenticator::reclaimDigestResponse(char const* responseStr) const { + free((char*)responseStr); // NOT delete, because it was malloc-allocated +} + +void Authenticator::resetRealmAndNonce() { + delete[] fRealm; fRealm = NULL; + delete[] fNonce; fNonce = NULL; +} + +void Authenticator::resetUsernameAndPassword() { + delete[] fUsername; fUsername = NULL; + delete[] fPassword; fPassword = NULL; + fPasswordIsMD5 = False; +} + +void Authenticator::assignRealmAndNonce(char const* realm, char const* nonce) { + fRealm = strDup(realm); + fNonce = strDup(nonce); +} + +void Authenticator +::assignUsernameAndPassword(char const* username, char const* password, + Boolean passwordIsMD5) { + fUsername = strDup(username); + fPassword = strDup(password); + fPasswordIsMD5 = passwordIsMD5; +} + +void Authenticator::assign(char const* realm, char const* nonce, + char const* username, char const* password, + Boolean passwordIsMD5) { + assignRealmAndNonce(realm, nonce); + assignUsernameAndPassword(username, password, passwordIsMD5); +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/FramedFilter.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/FramedFilter.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/FramedFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/FramedFilter.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,51 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Framed Filters +// Implementation + +#include "FramedFilter.hh" + +////////// FramedFilter ////////// +#include + +FramedFilter::FramedFilter(UsageEnvironment& env, + FramedSource* inputSource) + : FramedSource(env), + fInputSource(inputSource) { +} + +FramedFilter::~FramedFilter() { + Medium::close(fInputSource); +} + +// Default implementations of needed virtual functions. These merely +// call the same function in the input source - i.e., act like a 'null filter + +char const* FramedFilter::MIMEtype() const { + if (fInputSource == NULL) return ""; + + return fInputSource->MIMEtype(); +} + +void FramedFilter::getAttributes() const { + if (fInputSource != NULL) fInputSource->getAttributes(); +} + +void FramedFilter::doStopGettingFrames() { + if (fInputSource != NULL) fInputSource->stopGettingFrames(); +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/FramedSource.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/FramedSource.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/FramedSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/FramedSource.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,121 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Framed Sources +// Implementation + +#include "FramedSource.hh" +#include + +////////// FramedSource ////////// + +FramedSource::FramedSource(UsageEnvironment& env) + : MediaSource(env), + fAfterGettingFunc(NULL), fAfterGettingClientData(NULL), + fOnCloseFunc(NULL), fOnCloseClientData(NULL), + fIsCurrentlyAwaitingData(False), + fTo(NULL), fMaxSize(0), fFrameSize(0), fNumTruncatedBytes(0), fDurationInMicroseconds(0) +{ + fPresentationTime.tv_sec = fPresentationTime.tv_usec = 0; // initially +} + +FramedSource::~FramedSource() { +} + +Boolean FramedSource::isFramedSource() const { + return True; +} + +Boolean FramedSource::lookupByName(UsageEnvironment& env, char const* sourceName, + FramedSource*& resultSource) { + resultSource = NULL; // unless we succeed + + MediaSource* source; + if (!MediaSource::lookupByName(env, sourceName, source)) return False; + + if (!source->isFramedSource()) { + env.setResultMsg(sourceName, " is not a framed source"); + return False; + } + + resultSource = (FramedSource*)source; + return True; +} + +void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize, + afterGettingFunc* afterGettingFunc, + void* afterGettingClientData, + onCloseFunc* onCloseFunc, + void* onCloseClientData) { + // Make sure we're not already being read: + if (fIsCurrentlyAwaitingData) { + envir() << "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!\n"; + envir().internalError(); + } + + fTo = to; + fMaxSize = maxSize; + fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame() + fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame() + fAfterGettingFunc = afterGettingFunc; + fAfterGettingClientData = afterGettingClientData; + fOnCloseFunc = onCloseFunc; + fOnCloseClientData = onCloseClientData; + fIsCurrentlyAwaitingData = True; + + doGetNextFrame(); +} + +void FramedSource::afterGetting(FramedSource* source) { + source->fIsCurrentlyAwaitingData = False; + // indicates that we can be read again + // Note that this needs to be done here, in case the "fAfterFunc" + // called below tries to read another frame (which it usually will) + + if (source->fAfterGettingFunc != NULL) { + (*(source->fAfterGettingFunc))(source->fAfterGettingClientData, + source->fFrameSize, source->fNumTruncatedBytes, + source->fPresentationTime, + source->fDurationInMicroseconds); + } +} + +void FramedSource::handleClosure(void* clientData) { + FramedSource* source = (FramedSource*)clientData; + source->fIsCurrentlyAwaitingData = False; // because we got a close instead + if (source->fOnCloseFunc != NULL) { + (*(source->fOnCloseFunc))(source->fOnCloseClientData); + } +} + +void FramedSource::stopGettingFrames() { + fIsCurrentlyAwaitingData = False; // indicates that we can be read again + + // Perform any specialized action now: + doStopGettingFrames(); +} + +void FramedSource::doStopGettingFrames() { + // Default implementation: Do nothing + // Subclasses may wish to specialize this so as to ensure that a + // subsequent reader can pick up where this one left off. +} + +unsigned FramedSource::maxFrameSize() const { + // By default, this source has no maximum frame size. + return 0; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/Base64.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/Base64.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/Base64.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/Base64.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,43 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Base64 encoding and decoding +// C++ header + +#ifndef _BASE64_HH +#define _BASE64_HH + +#ifndef _BOOLEAN_HH +#include "Boolean.hh" +#endif + +unsigned char* base64Decode(char const* in, unsigned& resultSize, + Boolean trimTrailingZeros = True); + // returns a newly allocated array - of size "resultSize" - that + // the caller is responsible for delete[]ing. + +unsigned char* base64Decode(char const* in, unsigned inSize, + unsigned& resultSize, + Boolean trimTrailingZeros = True); + // As above, but includes the size of the input string (i.e., the number of bytes to decode) as a parameter. + // This saves an extra call to "strlen()" if we already know the length of the input string. + +char* base64Encode(char const* orig, unsigned origLength); + // returns a 0-terminated string that + // the caller is responsible for delete[]ing. + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/BasicUDPSource.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/BasicUDPSource.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/BasicUDPSource.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/BasicUDPSource.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,55 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// A simple UDP source, where every UDP payload is a complete frame +// C++ header + +#ifndef _BASIC_UDP_SOURCE_HH +#define _BASIC_UDP_SOURCE_HH + +#ifndef _FRAMED_SOURCE_HH +#include "FramedSource.hh" +#endif +#ifndef _GROUPSOCK_HH +#include "Groupsock.hh" +#endif + +class BasicUDPSource: public FramedSource { +public: + static BasicUDPSource* createNew(UsageEnvironment& env, Groupsock* inputGS); + + virtual ~BasicUDPSource(); + + Groupsock* gs() const { return fInputGS; } + +private: + BasicUDPSource(UsageEnvironment& env, Groupsock* inputGS); + // called only by createNew() + + static void incomingPacketHandler(BasicUDPSource* source, int mask); + void incomingPacketHandler1(); + +private: // redefined virtual functions: + virtual void doGetNextFrame(); + virtual void doStopGettingFrames(); + +private: + Groupsock* fInputGS; + Boolean fHaveStartedReading; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/DigestAuthentication.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/DigestAuthentication.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/DigestAuthentication.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/DigestAuthentication.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,72 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// A class used for digest authentication. +// C++ header + +#ifndef _DIGEST_AUTHENTICATION_HH +#define _DIGEST_AUTHENTICATION_HH + +#ifndef _BOOLEAN_HH +#include +#endif + +// A class used for digest authentication. +// The "realm", and "nonce" fields are supplied by the server +// (in a "401 Unauthorized" response). +// The "username" and "password" fields are supplied by the client. +class Authenticator { +public: + Authenticator(); + Authenticator(const Authenticator& orig); + Authenticator& operator=(const Authenticator& rightSide); + virtual ~Authenticator(); + + void reset(); + void setRealmAndNonce(char const* realm, char const* nonce); + void setRealmAndRandomNonce(char const* realm); + // as above, except that the nonce is created randomly. + // (This is used by servers.) + void setUsernameAndPassword(char const* username, char const* password, + Boolean passwordIsMD5 = False); + // If "passwordIsMD5" is True, then "password" is actually the value computed + // by md5(::) + + char const* realm() const { return fRealm; } + char const* nonce() const { return fNonce; } + char const* username() const { return fUsername; } + char const* password() const { return fPassword; } + + char const* computeDigestResponse(char const* cmd, char const* url) const; + void reclaimDigestResponse(char const* responseStr) const; + +private: + void resetRealmAndNonce(); + void resetUsernameAndPassword(); + void assignRealmAndNonce(char const* realm, char const* nonce); + void assignUsernameAndPassword(char const* username, char const* password, + Boolean passwordIsMD5); + void assign(char const* realm, char const* nonce, + char const* username, char const* password, Boolean passwordIsMD5); + +private: + char* fRealm; char* fNonce; + char* fUsername; char* fPassword; + Boolean fPasswordIsMD5; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/FramedFilter.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/FramedFilter.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/FramedFilter.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/FramedFilter.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,52 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Framed Filters +// C++ header + +#ifndef _FRAMED_FILTER_HH +#define _FRAMED_FILTER_HH + +#ifndef _FRAMED_SOURCE_HH +#include "FramedSource.hh" +#endif + +class FramedFilter: public FramedSource { +public: + FramedSource* inputSource() const { return fInputSource; } + + void reassignInputSource(FramedSource* newInputSource) { fInputSource = newInputSource; } + + // Call before destruction if you want to prevent the destructor from closing the input source + void detachInputSource() { reassignInputSource(NULL); } + +protected: + FramedFilter(UsageEnvironment& env, FramedSource* inputSource); + // abstract base class + virtual ~FramedFilter(); + +protected: + // Redefined virtual functions (with default 'null' implementations): + virtual char const* MIMEtype() const; + virtual void getAttributes() const; + virtual void doStopGettingFrames(); + +protected: + FramedSource* fInputSource; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/FramedSource.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/FramedSource.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/FramedSource.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/FramedSource.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,94 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Framed Sources +// C++ header + +#ifndef _FRAMED_SOURCE_HH +#define _FRAMED_SOURCE_HH + +#ifndef _NET_COMMON_H +#include "NetCommon.h" +#endif +#ifndef _MEDIA_SOURCE_HH +#include "MediaSource.hh" +#endif + +class FramedSource: public MediaSource { +public: + static Boolean lookupByName(UsageEnvironment& env, char const* sourceName, + FramedSource*& resultSource); + + typedef void (afterGettingFunc)(void* clientData, unsigned frameSize, + unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds); + typedef void (onCloseFunc)(void* clientData); + void getNextFrame(unsigned char* to, unsigned maxSize, + afterGettingFunc* afterGettingFunc, + void* afterGettingClientData, + onCloseFunc* onCloseFunc, + void* onCloseClientData); + + static void handleClosure(void* clientData); + // This should be called (on ourself) if the source is discovered + // to be closed (i.e., no longer readable) + + void stopGettingFrames(); + + virtual unsigned maxFrameSize() const; + // size of the largest possible frame that we may serve, or 0 + // if no such maximum is known (default) + + virtual void doGetNextFrame() = 0; + // called by getNextFrame() + + Boolean isCurrentlyAwaitingData() const {return fIsCurrentlyAwaitingData;} + + static void afterGetting(FramedSource* source); + // doGetNextFrame() should arrange for this to be called after the + // frame has been read (*iff* it is read successfully) + +protected: + FramedSource(UsageEnvironment& env); // abstract base class + virtual ~FramedSource(); + + virtual void doStopGettingFrames(); + +protected: + // The following variables are typically accessed/set by doGetNextFrame() + unsigned char* fTo; // in + unsigned fMaxSize; // in + unsigned fFrameSize; // out + unsigned fNumTruncatedBytes; // out + struct timeval fPresentationTime; // out + unsigned fDurationInMicroseconds; // out + +private: + // redefined virtual functions: + virtual Boolean isFramedSource() const; + +private: + afterGettingFunc* fAfterGettingFunc; + void* fAfterGettingClientData; + onCloseFunc* fOnCloseFunc; + void* fOnCloseClientData; + + Boolean fIsCurrentlyAwaitingData; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/liveMedia.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/liveMedia.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/liveMedia.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/liveMedia.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,106 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Inclusion of header files representing the interface +// for the entire library +// +// Programs that use the library can include this header file, +// instead of each of the individual media header files + +#ifndef _LIVEMEDIA_HH +#define _LIVEMEDIA_HH + +//#include "MPEG1or2AudioRTPSink.hh" +//#include "MP3ADURTPSink.hh" +//#include "MPEG1or2VideoRTPSink.hh" +//#include "MPEG4ESVideoRTPSink.hh" +//#include "AMRAudioFileSink.hh" +//#include "H264VideoFileSink.hh" +//#include "BasicUDPSink.hh" +//#include "MPEG1or2VideoHTTPSink.hh" +//#include "GSMAudioRTPSink.hh" +//#include "H263plusVideoRTPSink.hh" +//#include "H264VideoRTPSink.hh" +//#include "DVVideoRTPSource.hh" +//#include "DVVideoRTPSink.hh" +//#include "DVVideoStreamFramer.hh" +//#include "H264VideoStreamFramer.hh" +//#include "JPEGVideoRTPSink.hh" +//#include "SimpleRTPSink.hh" +//#include "uLawAudioFilter.hh" +//#include "MPEG2IndexFromTransportStream.hh" +//#include "MPEG2TransportStreamTrickModeFilter.hh" +//#include "ByteStreamMultiFileSource.hh" +#include "BasicUDPSource.hh" +#include "SimpleRTPSource.hh" +//#include "MPEG1or2AudioRTPSource.hh" +//#include "MPEG4LATMAudioRTPSource.hh" +//#include "MPEG4LATMAudioRTPSink.hh" +//#include "MPEG4ESVideoRTPSource.hh" +//#include "MPEG4GenericRTPSource.hh" +//#include "MP3ADURTPSource.hh" +//#include "QCELPAudioRTPSource.hh" +//#include "AMRAudioRTPSource.hh" +//#include "JPEGVideoRTPSource.hh" +//#include "JPEGVideoSource.hh" +//#include "MPEG1or2VideoRTPSource.hh" +//#include "MPEG2TransportStreamFromPESSource.hh" +//#include "MPEG2TransportStreamFromESSource.hh" +#include "MPEG2TransportStreamFramer.hh" +//#include "ADTSAudioFileSource.hh" +//#include "H261VideoRTPSource.hh" +//#include "H263plusVideoRTPSource.hh" +//#include "H264VideoRTPSource.hh" +//#include "MP3HTTPSource.hh" +//#include "MP3ADU.hh" +//#include "MP3ADUinterleaving.hh" +//#include "MP3Transcoder.hh" +//#include "MPEG1or2DemuxedElementaryStream.hh" +//#include "MPEG1or2AudioStreamFramer.hh" +//#include "H263plusVideoStreamFramer.hh" +//#include "AC3AudioStreamFramer.hh" +//#include "AC3AudioRTPSource.hh" +//#include "AC3AudioRTPSink.hh" +//#include "MPEG4GenericRTPSink.hh" +//#include "MPEG1or2VideoStreamDiscreteFramer.hh" +//#include "MPEG4VideoStreamDiscreteFramer.hh" +//#include "DeviceSource.hh" +//#include "AudioInputDevice.hh" +//#include "WAVAudioFileSource.hh" +//#include "RTSPServer.hh" +//#include "RTSPOverHTTPServer.hh" +#include "RTSPClient.hh" +//#include "SIPClient.hh" +//#include "QuickTimeFileSink.hh" +//#include "QuickTimeGenericRTPSource.hh" +//#include "AVIFileSink.hh" +//#include "PassiveServerMediaSubsession.hh" +//#include "MPEG4VideoFileServerMediaSubsession.hh" +//#include "WAVAudioFileServerMediaSubsession.hh" +//#include "AMRAudioFileServerMediaSubsession.hh" +//#include "AMRAudioFileSource.hh" +//#include "AMRAudioRTPSink.hh" +//#include "MP3AudioFileServerMediaSubsession.hh" +//#include "MPEG1or2VideoFileServerMediaSubsession.hh" +//#include "MPEG1or2FileServerDemux.hh" +//#include "MPEG2TransportFileServerMediaSubsession.hh" +//#include "H263plusVideoFileServerMediaSubsession.hh" +//#include "ADTSAudioFileServerMediaSubsession.hh" +//#include "DVVideoFileServerMediaSubsession.hh" +//#include "DarwinInjector.hh" + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/liveMedia_version.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/liveMedia_version.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/liveMedia_version.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/liveMedia_version.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,10 @@ +// Version information for the "liveMedia" library +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. + +#ifndef _LIVEMEDIA_VERSION_HH +#define _LIVEMEDIA_VERSION_HH + +#define LIVEMEDIA_LIBRARY_VERSION_STRING "2010.03.16" +#define LIVEMEDIA_LIBRARY_VERSION_INT 1268697600 + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/Locale.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/Locale.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/Locale.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/Locale.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,70 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Support for temporarily setting the locale (e.g., to "C" or "POSIX") for (e.g.) parsing or printing +// floating-point numbers in protocol headers, or calling toupper()/tolower() on human-input strings. +// C++ header + +#ifndef _LOCALE_HH +#define _LOCALE_HH + +// If you're on a system that (for whatever reason) doesn't have either the "setlocale()" or the "newlocale()" function, then +// add "-DLOCALE_NOT_USED" to your "config.*" file. + +// If you're on a system that (for whatever reason) has "setlocale()" but not "newlocale()", then +// add "-DXLOCALE_NOT_USED" to your "config.*" file. +// (Note that -DLOCALE_NOT_USED implies -DXLOCALE_NOT_USED; you do not need both.) +// Also, for Windows systems, we define "XLOCALE_NOT_USED" by default, because at least some Windows systems +// (or their development environments) don't have "newlocale()". If, however, your Windows system *does* have "newlocale()", +// then you can override this by defining "XLOCALE_USED" before #including this file. + +#ifdef XLOCALE_USED +#undef LOCALE_NOT_USED +#undef XLOCALE_NOT_USED +#else +#if defined(__WIN32__) || defined(_WIN32) +#define XLOCALE_NOT_USED 1 +#endif +#endif + +#ifndef LOCALE_NOT_USED +#include +#ifndef XLOCALE_NOT_USED +#include // because, on some systems, doesn't include ; this makes sure that we get both +#endif +#endif + + +enum LocaleCategory { All, Numeric }; // define and implement more categories later, as needed + +class Locale { +public: + Locale(char const* newLocale, LocaleCategory category = All); + virtual ~Locale(); + +private: +#ifndef LOCALE_NOT_USED +#ifndef XLOCALE_NOT_USED + locale_t fLocale, fPrevLocale; +#else + int fCategoryNum; + char* fPrevLocale; +#endif +#endif +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/Media.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/Media.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/Media.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/Media.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,137 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Medium +// C++ header + +#ifndef _MEDIA_HH +#define _MEDIA_HH + +#ifndef _LIVEMEDIA_VERSION_HH +#include "liveMedia_version.hh" +#endif + +#ifndef _HASH_TABLE_HH +#include "HashTable.hh" +#endif + +#ifndef _USAGE_ENVIRONMENT_HH +#include "UsageEnvironment.hh" +#endif + +// Lots of files end up needing the following, so just #include them here: +#ifndef _NET_COMMON_H +#include "NetCommon.h" +#endif +#include + +// The following makes the Borland compiler happy: +#ifdef __BORLANDC__ +#define _strnicmp strnicmp +#define fabsf(x) fabs(x) +#endif + +#define mediumNameMaxLen 30 + +class Medium { +public: + static Boolean lookupByName(UsageEnvironment& env, + char const* mediumName, + Medium*& resultMedium); + static void close(UsageEnvironment& env, char const* mediumName); + static void close(Medium* medium); // alternative close() method using ptrs + // (has no effect if medium == NULL) + + UsageEnvironment& envir() const {return fEnviron;} + + char const* name() const {return fMediumName;} + + // Test for specific types of media: + virtual Boolean isSource() const; + virtual Boolean isSink() const; + virtual Boolean isRTCPInstance() const; + virtual Boolean isRTSPClient() const; + virtual Boolean isRTSPServer() const; + virtual Boolean isMediaSession() const; + virtual Boolean isServerMediaSession() const; + +protected: + friend class MediaLookupTable; + Medium(UsageEnvironment& env); // abstract base class + virtual ~Medium(); // instances are deleted using close() only + + TaskToken& nextTask() { + return fNextTask; + } + +private: + UsageEnvironment& fEnviron; + char fMediumName[mediumNameMaxLen]; + TaskToken fNextTask; +}; + + +// A data structure for looking up a Medium by its string name. +// (It is used only to implement "Medium", but we make it visible here, in case developers want to use it to iterate over +// the whole set of "Medium" objects that we've created.) +class MediaLookupTable { +public: + static MediaLookupTable* ourMedia(UsageEnvironment& env); + HashTable const& getTable() { return *fTable; } + +protected: + MediaLookupTable(UsageEnvironment& env); + virtual ~MediaLookupTable(); + +private: + friend class Medium; + + Medium* lookup(char const* name) const; + // Returns NULL if none already exists + + void addNew(Medium* medium, char* mediumName); + void remove(char const* name); + + void generateNewName(char* mediumName, unsigned maxLen); + +private: + UsageEnvironment& fEnv; + HashTable* fTable; + unsigned fNameGenerator; +}; + + +// The structure pointed to by the "liveMediaPriv" UsageEnvironment field: +class _Tables { +public: + static _Tables* getOurTables(UsageEnvironment& env, Boolean createIfNotPresent = True); + // returns a pointer to a "_Tables" structure (creating it if necessary) + void reclaimIfPossible(); + // used to delete ourselves when we're no longer used + + MediaLookupTable* mediaTable; + void* socketTable; + +protected: + _Tables(UsageEnvironment& env); + virtual ~_Tables(); + +private: + UsageEnvironment& fEnv; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MediaSession.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MediaSession.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MediaSession.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MediaSession.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,307 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// A data structure that represents a session that consists of +// potentially multiple (audio and/or video) sub-sessions +// (This data structure is used for media *receivers* - i.e., clients. +// For media streamers, use "ServerMediaSession" instead.) +// C++ header + +#ifndef _MEDIA_SESSION_HH +#define _MEDIA_SESSION_HH + +#ifndef _RTCP_HH +#include "RTCP.hh" +#endif + +class MediaSubsession; // forward + +class MediaSession: public Medium { +public: + static MediaSession* createNew(UsageEnvironment& env, + char const* sdpDescription); + + static Boolean lookupByName(UsageEnvironment& env, char const* sourceName, + MediaSession*& resultSession); + + Boolean hasSubsessions() const { return fSubsessionsHead != NULL; } + double& playStartTime() { return fMaxPlayStartTime; } + double& playEndTime() { return fMaxPlayEndTime; } + char* connectionEndpointName() const { return fConnectionEndpointName; } + char const* CNAME() const { return fCNAME; } + struct in_addr const& sourceFilterAddr() const { return fSourceFilterAddr; } + float& scale() { return fScale; } + char* mediaSessionType() const { return fMediaSessionType; } + char* sessionName() const { return fSessionName; } + char* sessionDescription() const { return fSessionDescription; } + char const* controlPath() const { return fControlPath; } + + Boolean initiateByMediaType(char const* mimeType, + MediaSubsession*& resultSubsession, + int useSpecialRTPoffset = -1); + // Initiates the first subsession with the specified MIME type + // Returns the resulting subsession, or 'multi source' (not both) + +#ifdef SUPPORT_REAL_RTSP + // Attributes specific to RealNetworks streams: + Boolean isRealNetworksRDT; + unsigned fRealFlags; + unsigned char* fRealTitle; unsigned fRealTitleSize; + unsigned char* fRealAuthor; unsigned fRealAuthorSize; + unsigned char* fRealCopyright; unsigned fRealCopyrightSize; + unsigned char* fRealAbstract; unsigned fRealAbstractSize; +#endif + +protected: // redefined virtual functions + virtual Boolean isMediaSession() const; + +protected: + MediaSession(UsageEnvironment& env); + // called only by createNew(); + virtual ~MediaSession(); + + Boolean initializeWithSDP(char const* sdpDescription); + Boolean parseSDPLine(char const* input, char const*& nextLine); + Boolean parseSDPLine_s(char const* sdpLine); + Boolean parseSDPLine_i(char const* sdpLine); + Boolean parseSDPLine_c(char const* sdpLine); + Boolean parseSDPAttribute_type(char const* sdpLine); + Boolean parseSDPAttribute_control(char const* sdpLine); + Boolean parseSDPAttribute_range(char const* sdpLine); + Boolean parseSDPAttribute_source_filter(char const* sdpLine); + + static char* lookupPayloadFormat(unsigned char rtpPayloadType, + unsigned& rtpTimestampFrequency, + unsigned& numChannels); + static unsigned guessRTPTimestampFrequency(char const* mediumName, + char const* codecName); + +protected: + friend class MediaSubsessionIterator; + char* fCNAME; // used for RTCP + + // Linkage fields: + MediaSubsession* fSubsessionsHead; + MediaSubsession* fSubsessionsTail; + + // Fields set from a SDP description: + char* fConnectionEndpointName; + double fMaxPlayStartTime; + double fMaxPlayEndTime; + struct in_addr fSourceFilterAddr; // used for SSM + float fScale; // set from a RTSP "Scale:" header + char* fMediaSessionType; // holds a=type value + char* fSessionName; // holds s= value + char* fSessionDescription; // holds i= value + char* fControlPath; // holds optional a=control: string +}; + + +class MediaSubsessionIterator { +public: + MediaSubsessionIterator(MediaSession const& session); + virtual ~MediaSubsessionIterator(); + + MediaSubsession* next(); // NULL if none + void reset(); + +private: + MediaSession const& fOurSession; + MediaSubsession* fNextPtr; +}; + + +class MediaSubsession { +public: + MediaSession& parentSession() { return fParent; } + MediaSession const& parentSession() const { return fParent; } + + unsigned short clientPortNum() const { return fClientPortNum; } + unsigned char rtpPayloadFormat() const { return fRTPPayloadFormat; } + char const* savedSDPLines() const { return fSavedSDPLines; } + char const* mediumName() const { return fMediumName; } + char const* codecName() const { return fCodecName; } + char const* protocolName() const { return fProtocolName; } + char const* controlPath() const { return fControlPath; } + Boolean isSSM() const { return fSourceFilterAddr.s_addr != 0; } + + unsigned short videoWidth() const { return fVideoWidth; } + unsigned short videoHeight() const { return fVideoHeight; } + unsigned videoFPS() const { return fVideoFPS; } + unsigned numChannels() const { return fNumChannels; } + float& scale() { return fScale; } + + RTPSource* rtpSource() { return fRTPSource; } + RTCPInstance* rtcpInstance() { return fRTCPInstance; } + unsigned rtpTimestampFrequency() const { return fRTPTimestampFrequency; } + FramedSource* readSource() { return fReadSource; } + // This is the source that client sinks read from. It is usually + // (but not necessarily) the same as "rtpSource()" + + double playStartTime() const; + double playEndTime() const; + // Used only to set the local fields: + double& _playStartTime() { return fPlayStartTime; } + double& _playEndTime() { return fPlayEndTime; } + + Boolean initiate(int useSpecialRTPoffset = -1); + // Creates a "RTPSource" for this subsession. (Has no effect if it's + // already been created.) Returns True iff this succeeds. + void deInitiate(); // Destroys any previously created RTPSource + Boolean setClientPortNum(unsigned short portNum); + // Sets the preferred client port number that any "RTPSource" for + // this subsession would use. (By default, the client port number + // is gotten from the original SDP description, or - if the SDP + // description does not specfy a client port number - an ephemeral + // (even) port number is chosen.) This routine should *not* be + // called after initiate(). + char*& connectionEndpointName() { return fConnectionEndpointName; } + char const* connectionEndpointName() const { + return fConnectionEndpointName; + } + + // Various parameters set in "a=fmtp:" SDP lines: + unsigned fmtp_auxiliarydatasizelength() const { return fAuxiliarydatasizelength; } + unsigned fmtp_constantduration() const { return fConstantduration; } + unsigned fmtp_constantsize() const { return fConstantsize; } + unsigned fmtp_crc() const { return fCRC; } + unsigned fmtp_ctsdeltalength() const { return fCtsdeltalength; } + unsigned fmtp_de_interleavebuffersize() const { return fDe_interleavebuffersize; } + unsigned fmtp_dtsdeltalength() const { return fDtsdeltalength; } + unsigned fmtp_indexdeltalength() const { return fIndexdeltalength; } + unsigned fmtp_indexlength() const { return fIndexlength; } + unsigned fmtp_interleaving() const { return fInterleaving; } + unsigned fmtp_maxdisplacement() const { return fMaxdisplacement; } + unsigned fmtp_objecttype() const { return fObjecttype; } + unsigned fmtp_octetalign() const { return fOctetalign; } + unsigned fmtp_profile_level_id() const { return fProfile_level_id; } + unsigned fmtp_robustsorting() const { return fRobustsorting; } + unsigned fmtp_sizelength() const { return fSizelength; } + unsigned fmtp_streamstateindication() const { return fStreamstateindication; } + unsigned fmtp_streamtype() const { return fStreamtype; } + Boolean fmtp_cpresent() const { return fCpresent; } + Boolean fmtp_randomaccessindication() const { return fRandomaccessindication; } + char const* fmtp_config() const { return fConfig; } + char const* fmtp_mode() const { return fMode; } + char const* fmtp_spropparametersets() const { return fSpropParameterSets; } + + netAddressBits connectionEndpointAddress() const; + // Converts "fConnectionEndpointName" to an address (or 0 if unknown) + void setDestinations(netAddressBits defaultDestAddress); + // Uses "fConnectionEndpointName" and "serverPortNum" to set + // the destination address and port of the RTP and RTCP objects. + // This is typically called by RTSP clients after doing "SETUP". + + // Public fields that external callers can use to keep state. + // (They are responsible for all storage management on these fields) + char const* sessionId; // used by RTSP + unsigned short serverPortNum; // in host byte order (used by RTSP) + unsigned char rtpChannelId, rtcpChannelId; // used by RTSP (for RTP/TCP) + MediaSink* sink; // callers can use this to keep track of who's playing us + void* miscPtr; // callers can use this for whatever they want + + // Parameters set from a RTSP "RTP-Info:" header: + struct { + u_int16_t seqNum; + u_int32_t timestamp; + Boolean infoIsNew; // not part of the RTSP header; instead, set whenever this struct is filled in + } rtpInfo; + + double getNormalPlayTime(struct timeval const& presentationTime); + // Computes the stream's "Normal Play Time" (NPT) from the given "presentationTime". + // (For the definition of "Normal Play Time", see RFC 2326, section 3.6.) + // This function is useful only if the "rtpInfo" structure was previously filled in + // (e.g., by a "RTP-Info:" header in a RTSP response). + // Also, for this function to work properly, the RTP stream's presentation times must (eventually) be + // synchronized via RTCP. + +#ifdef SUPPORT_REAL_RTSP + // Attributes specific to RealNetworks streams: + unsigned fRealMaxBitRate, fRealAvgBitRate, fRealMaxPacketSize, fRealAvgPacketSize, fRealPreroll; + char* fRealStreamName; char* fRealMIMEType; + unsigned char* fRealOpaqueData; unsigned fRealOpaqueDataSize; + // A pointer into "fRealOpaqueData": + unsigned char* fRealTypeSpecificData; unsigned fRealTypeSpecificDataSize; + unsigned fRealRuleNumber; +#endif + +protected: + friend class MediaSession; + friend class MediaSubsessionIterator; + MediaSubsession(MediaSession& parent); + virtual ~MediaSubsession(); + + UsageEnvironment& env() { return fParent.envir(); } + void setNext(MediaSubsession* next) { fNext = next; } + + Boolean parseSDPLine_c(char const* sdpLine); + Boolean parseSDPLine_b(char const* sdpLine); + Boolean parseSDPAttribute_rtpmap(char const* sdpLine); + Boolean parseSDPAttribute_control(char const* sdpLine); + Boolean parseSDPAttribute_range(char const* sdpLine); + Boolean parseSDPAttribute_fmtp(char const* sdpLine); + Boolean parseSDPAttribute_source_filter(char const* sdpLine); + Boolean parseSDPAttribute_x_dimensions(char const* sdpLine); + Boolean parseSDPAttribute_framerate(char const* sdpLine); + +protected: + // Linkage fields: + MediaSession& fParent; + MediaSubsession* fNext; + + // Fields set from a SDP description: + char* fConnectionEndpointName; // may also be set by RTSP SETUP response + unsigned short fClientPortNum; // in host byte order + // This field is also set by initiate() + unsigned char fRTPPayloadFormat; + char* fSavedSDPLines; + char* fMediumName; + char* fCodecName; + char* fProtocolName; + unsigned fRTPTimestampFrequency; + char* fControlPath; // holds optional a=control: string + struct in_addr fSourceFilterAddr; // used for SSM + unsigned fBandwidth; // in kilobits-per-second, from b= line + + // Parameters set by "a=fmtp:" SDP lines: + unsigned fAuxiliarydatasizelength, fConstantduration, fConstantsize; + unsigned fCRC, fCtsdeltalength, fDe_interleavebuffersize, fDtsdeltalength; + unsigned fIndexdeltalength, fIndexlength, fInterleaving; + unsigned fMaxdisplacement, fObjecttype; + unsigned fOctetalign, fProfile_level_id, fRobustsorting; + unsigned fSizelength, fStreamstateindication, fStreamtype; + Boolean fCpresent, fRandomaccessindication; + char *fConfig, *fMode, *fSpropParameterSets; + + double fPlayStartTime; + double fPlayEndTime; + unsigned short fVideoWidth, fVideoHeight; + // screen dimensions (set by an optional a=x-dimensions: , line) + unsigned fVideoFPS; + // frame rate (set by an optional "a=framerate: " or "a=x-framerate: " line) + unsigned fNumChannels; + // optionally set by "a=rtpmap:" lines for audio sessions. Default: 1 + float fScale; // set from a RTSP "Scale:" header + double fNPT_PTS_Offset; // set by "getNormalPlayTime()"; add this to a PTS to get NPT + + // Fields set by initiate(): + Groupsock* fRTPSocket; Groupsock* fRTCPSocket; // works even for unicast + RTPSource* fRTPSource; RTCPInstance* fRTCPInstance; + FramedSource* fReadSource; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MediaSink.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MediaSink.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MediaSink.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MediaSink.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,136 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Media Sinks +// C++ header + +#ifndef _MEDIA_SINK_HH +#define _MEDIA_SINK_HH + +#ifndef _FRAMED_SOURCE_HH +#include "FramedSource.hh" +#endif + +class MediaSink: public Medium { +public: + static Boolean lookupByName(UsageEnvironment& env, char const* sinkName, + MediaSink*& resultSink); + + typedef void (afterPlayingFunc)(void* clientData); + Boolean startPlaying(MediaSource& source, + afterPlayingFunc* afterFunc, + void* afterClientData); + virtual void stopPlaying(); + + // Test for specific types of sink: + virtual Boolean isRTPSink() const; + + FramedSource* source() const {return fSource;} + +protected: + MediaSink(UsageEnvironment& env); // abstract base class + virtual ~MediaSink(); + + virtual Boolean sourceIsCompatibleWithUs(MediaSource& source); + // called by startPlaying() + virtual Boolean continuePlaying() = 0; + // called by startPlaying() + + static void onSourceClosure(void* clientData); + // should be called (on ourselves) by continuePlaying() when it + // discovers that the source we're playing from has closed. + + FramedSource* fSource; + +private: + // redefined virtual functions: + virtual Boolean isSink() const; + +private: + // The following fields are used when we're being played: + afterPlayingFunc* fAfterFunc; + void* fAfterClientData; +}; + +// A data structure that a sink may use for an output packet: +class OutPacketBuffer { +public: + OutPacketBuffer(unsigned preferredPacketSize, unsigned maxPacketSize); + ~OutPacketBuffer(); + + static unsigned maxSize; + + unsigned char* curPtr() const {return &fBuf[fPacketStart + fCurOffset];} + unsigned totalBytesAvailable() const { + return fLimit - (fPacketStart + fCurOffset); + } + unsigned totalBufferSize() const { return fLimit; } + unsigned char* packet() const {return &fBuf[fPacketStart];} + unsigned curPacketSize() const {return fCurOffset;} + + void increment(unsigned numBytes) {fCurOffset += numBytes;} + + void enqueue(unsigned char const* from, unsigned numBytes); + void enqueueWord(u_int32_t word); + void insert(unsigned char const* from, unsigned numBytes, unsigned toPosition); + void insertWord(u_int32_t word, unsigned toPosition); + void extract(unsigned char* to, unsigned numBytes, unsigned fromPosition); + u_int32_t extractWord(unsigned fromPosition); + + void skipBytes(unsigned numBytes); + + Boolean isPreferredSize() const {return fCurOffset >= fPreferred;} + Boolean wouldOverflow(unsigned numBytes) const { + return (fCurOffset+numBytes) > fMax; + } + unsigned numOverflowBytes(unsigned numBytes) const { + return (fCurOffset+numBytes) - fMax; + } + Boolean isTooBigForAPacket(unsigned numBytes) const { + return numBytes > fMax; + } + + void setOverflowData(unsigned overflowDataOffset, + unsigned overflowDataSize, + struct timeval const& presentationTime, + unsigned durationInMicroseconds); + unsigned overflowDataSize() const {return fOverflowDataSize;} + struct timeval overflowPresentationTime() const {return fOverflowPresentationTime;} + unsigned overflowDurationInMicroseconds() const {return fOverflowDurationInMicroseconds;} + Boolean haveOverflowData() const {return fOverflowDataSize > 0;} + void useOverflowData(); + + void adjustPacketStart(unsigned numBytes); + void resetPacketStart(); + void resetOffset() { fCurOffset = 0; } + void resetOverflowData() + { + fOverflowDataOffset = fOverflowDataSize = 0; + fOverflowPresentationTime.tv_sec = 0; + fOverflowPresentationTime.tv_usec = 0; + } + +private: + unsigned fPacketStart, fCurOffset, fPreferred, fMax, fLimit; + unsigned char* fBuf; + + unsigned fOverflowDataOffset, fOverflowDataSize; + struct timeval fOverflowPresentationTime; + unsigned fOverflowDurationInMicroseconds; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MediaSource.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MediaSource.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MediaSource.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MediaSource.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,58 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Media Sources +// C++ header + +#ifndef _MEDIA_SOURCE_HH +#define _MEDIA_SOURCE_HH + +#ifndef _MEDIA_HH +#include "Media.hh" +#endif + +class MediaSource: public Medium { +public: + static Boolean lookupByName(UsageEnvironment& env, char const* sourceName, + MediaSource*& resultSource); + virtual void getAttributes() const; + // attributes are returned in "env's" 'result message' + + // The MIME type of this source: + virtual char const* MIMEtype() const; + + // Test for specific types of source: + virtual Boolean isFramedSource() const; + virtual Boolean isRTPSource() const; + virtual Boolean isMPEG1or2VideoStreamFramer() const; + virtual Boolean isMPEG4VideoStreamFramer() const; + virtual Boolean isH264VideoStreamFramer() const; + virtual Boolean isH265VideoStreamFramer() const; + virtual Boolean isDVVideoStreamFramer() const; + virtual Boolean isJPEGVideoSource() const; + virtual Boolean isAMRAudioSource() const; + +protected: + MediaSource(UsageEnvironment& env); // abstract base class + virtual ~MediaSource(); + +private: + // redefined virtual functions: + virtual Boolean isSource() const; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MPEG2TransportStreamFramer.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MPEG2TransportStreamFramer.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MPEG2TransportStreamFramer.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MPEG2TransportStreamFramer.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,72 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// A filter that passes through (unchanged) chunks that contain an integral number +// of MPEG-2 Transport Stream packets, but returning (in "fDurationInMicroseconds") +// an updated estimate of the time gap between chunks. +// C++ header + +#ifndef _MPEG2_TRANSPORT_STREAM_FRAMER_HH +#define _MPEG2_TRANSPORT_STREAM_FRAMER_HH + +#ifndef _FRAMED_FILTER_HH +#include "FramedFilter.hh" +#endif + +#ifndef _HASH_TABLE_HH +#include "HashTable.hh" +#endif + +class MPEG2TransportStreamFramer: public FramedFilter { +public: + static MPEG2TransportStreamFramer* + createNew(UsageEnvironment& env, FramedSource* inputSource); + + unsigned long tsPacketCount() const { return fTSPacketCount; } + + void changeInputSource(FramedSource* newInputSource) { fInputSource = newInputSource; } + + void clearPIDStatusTable(); + +protected: + MPEG2TransportStreamFramer(UsageEnvironment& env, FramedSource* inputSource); + // called only by createNew() + virtual ~MPEG2TransportStreamFramer(); + +private: + // Redefined virtual functions: + virtual void doGetNextFrame(); + virtual void doStopGettingFrames(); + +private: + static void afterGettingFrame(void* clientData, unsigned frameSize, + unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds); + void afterGettingFrame1(unsigned frameSize, + struct timeval presentationTime); + + void updateTSPacketDurationEstimate(unsigned char* pkt, double timeNow); + +private: + unsigned long fTSPacketCount; + double fTSPacketDurationEstimate; + HashTable* fPIDStatusTable; + unsigned long fTSPCRCount; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MultiFramedRTPSink.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MultiFramedRTPSink.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MultiFramedRTPSink.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MultiFramedRTPSink.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,130 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTP sink for a common kind of payload format: Those which pack multiple, +// complete codec frames (as many as possible) into each RTP packet. +// C++ header + +#ifndef _MULTI_FRAMED_RTP_SINK_HH +#define _MULTI_FRAMED_RTP_SINK_HH + +#ifndef _RTP_SINK_HH +#include "RTPSink.hh" +#endif + +class MultiFramedRTPSink: public RTPSink { +public: + void setPacketSizes(unsigned preferredPacketSize, unsigned maxPacketSize); + +protected: + MultiFramedRTPSink(UsageEnvironment& env, + Groupsock* rtpgs, unsigned char rtpPayloadType, + unsigned rtpTimestampFrequency, + char const* rtpPayloadFormatName, + unsigned numChannels = 1); + // we're a virtual base class + + virtual ~MultiFramedRTPSink(); + + virtual void doSpecialFrameHandling(unsigned fragmentationOffset, + unsigned char* frameStart, + unsigned numBytesInFrame, + struct timeval frameTimestamp, + unsigned numRemainingBytes); + // perform any processing specific to the particular payload format + virtual Boolean allowFragmentationAfterStart() const; + // whether a frame can be fragmented if other frame(s) appear earlier + // in the packet (by default: False) + virtual Boolean allowOtherFramesAfterLastFragment() const; + // whether other frames can be packed into a packet following the + // final fragment of a previous, fragmented frame (by default: False) + virtual Boolean frameCanAppearAfterPacketStart(unsigned char const* frameStart, + unsigned numBytesInFrame) const; + // whether this frame can appear in position >1 in a pkt (default: True) + virtual unsigned specialHeaderSize() const; + // returns the size of any special header used (following the RTP header) (default: 0) + virtual unsigned frameSpecificHeaderSize() const; + // returns the size of any frame-specific header used (before each frame + // within the packet) (default: 0) + virtual unsigned computeOverflowForNewFrame(unsigned newFrameSize) const; + // returns the number of overflow bytes that would be produced by adding a new + // frame of size "newFrameSize" to the current RTP packet. + // (By default, this just calls "numOverflowBytes()", but subclasses can redefine + // this to (e.g.) impose a granularity upon RTP payload fragments.) + + // Functions that might be called by doSpecialFrameHandling(), or other subclass virtual functions: + Boolean isFirstPacket() const { return fIsFirstPacket; } + Boolean isFirstFrameInPacket() const { return fNumFramesUsedSoFar == 0; } + unsigned curFragmentationOffset() const { return fCurFragmentationOffset; } + void setMarkerBit(); + void setTimestamp(struct timeval timestamp); + void setSpecialHeaderWord(unsigned word, /* 32 bits, in host order */ + unsigned wordPosition = 0); + void setSpecialHeaderBytes(unsigned char const* bytes, unsigned numBytes, + unsigned bytePosition = 0); + void setFrameSpecificHeaderWord(unsigned word, /* 32 bits, in host order */ + unsigned wordPosition = 0); + void setFrameSpecificHeaderBytes(unsigned char const* bytes, unsigned numBytes, + unsigned bytePosition = 0); + void setFramePadding(unsigned numPaddingBytes); + unsigned numFramesUsedSoFar() const { return fNumFramesUsedSoFar; } + unsigned ourMaxPacketSize() const { return fOurMaxPacketSize; } + +public: // redefined virtual functions: + virtual void stopPlaying(); + +protected: // redefined virtual functions: + virtual Boolean continuePlaying(); + +private: + void buildAndSendPacket(Boolean isFirstPacket); + void packFrame(); + void sendPacketIfNecessary(); + static void sendNext(void* firstArg); + friend void sendNext(void*); + + static void afterGettingFrame(void* clientData, + unsigned numBytesRead, unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds); + void afterGettingFrame1(unsigned numBytesRead, unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds); + Boolean isTooBigForAPacket(unsigned numBytes) const; + + static void ourHandleClosure(void* clientData); + +private: + OutPacketBuffer* fOutBuf; + + Boolean fNoFramesLeft; + unsigned fNumFramesUsedSoFar; + unsigned fCurFragmentationOffset; + Boolean fPreviousFrameEndedFragmentation; + + Boolean fIsFirstPacket; + struct timeval fNextSendTime; + unsigned fTimestampPosition; + unsigned fSpecialHeaderPosition; + unsigned fSpecialHeaderSize; // size in bytes of any special header used + unsigned fCurFrameSpecificHeaderPosition; + unsigned fCurFrameSpecificHeaderSize; // size in bytes of cur frame-specific header + unsigned fTotalFrameSpecificHeaderSizes; // size of all frame-specific hdrs in pkt + unsigned fOurMaxPacketSize; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MultiFramedRTPSource.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MultiFramedRTPSource.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/MultiFramedRTPSource.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/MultiFramedRTPSource.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,157 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTP source for a common kind of payload format: Those which pack multiple, +// complete codec frames (as many as possible) into each RTP packet. +// C++ header + +#ifndef _MULTI_FRAMED_RTP_SOURCE_HH +#define _MULTI_FRAMED_RTP_SOURCE_HH + +#ifndef _RTP_SOURCE_HH +#include "RTPSource.hh" +#endif + +class BufferedPacket; // forward +class BufferedPacketFactory; // forward + +class MultiFramedRTPSource: public RTPSource { +protected: + MultiFramedRTPSource(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + BufferedPacketFactory* packetFactory = NULL); + // virtual base class + virtual ~MultiFramedRTPSource(); + + virtual Boolean processSpecialHeader(BufferedPacket* packet, + unsigned& resultSpecialHeaderSize); + // Subclasses redefine this to handle any special, payload format + // specific header that follows the RTP header. + + virtual Boolean packetIsUsableInJitterCalculation(unsigned char* packet, + unsigned packetSize); + // The default implementation returns True, but this can be redefined + +protected: + Boolean fCurrentPacketBeginsFrame; + Boolean fCurrentPacketCompletesFrame; + +protected: + // redefined virtual functions: + virtual void doStopGettingFrames(); + +private: + // redefined virtual functions: + virtual void doGetNextFrame(); + virtual void setPacketReorderingThresholdTime(unsigned uSeconds); + +private: + void reset(); + void doGetNextFrame1(); + + static void networkReadHandler(MultiFramedRTPSource* source, int /*mask*/); + friend void networkReadHandler(MultiFramedRTPSource*, int); + + Boolean fAreDoingNetworkReads; + Boolean fNeedDelivery; + Boolean fPacketLossInFragmentedFrame; + unsigned char* fSavedTo; + unsigned fSavedMaxSize; + + // A buffer to (optionally) hold incoming pkts that have been reorderered + class ReorderingPacketBuffer* fReorderingBuffer; +}; + + +// A 'packet data' class that's used to implement the above. +// Note that this can be subclassed - if desired - to redefine +// "nextEnclosedFrameSize()". + +class BufferedPacket { +public: + BufferedPacket(); + virtual ~BufferedPacket(); + + Boolean hasUsableData() const { return fTail > fHead; } + unsigned useCount() const { return fUseCount; } + + Boolean fillInData(RTPInterface& rtpInterface); + void assignMiscParams(unsigned short rtpSeqNo, unsigned rtpTimestamp, + struct timeval presentationTime, + Boolean hasBeenSyncedUsingRTCP, + Boolean rtpMarkerBit, struct timeval timeReceived); + void skip(unsigned numBytes); // used to skip over an initial header + void removePadding(unsigned numBytes); // used to remove trailing bytes + void appendData(unsigned char* newData, unsigned numBytes); + void use(unsigned char* to, unsigned toSize, + unsigned& bytesUsed, unsigned& bytesTruncated, + unsigned short& rtpSeqNo, unsigned& rtpTimestamp, + struct timeval& presentationTime, + Boolean& hasBeenSyncedUsingRTCP, Boolean& rtpMarkerBit); + + BufferedPacket*& nextPacket() { return fNextPacket; } + + unsigned short rtpSeqNo() const { return fRTPSeqNo; } + struct timeval const& timeReceived() const { return fTimeReceived; } + + unsigned char* data() const { return &fBuf[fHead]; } + unsigned dataSize() const { return fTail-fHead; } + Boolean rtpMarkerBit() const { return fRTPMarkerBit; } + Boolean& isFirstPacket() { return fIsFirstPacket; } + +protected: + virtual void reset(); + virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr, + unsigned dataSize); + // The above function has been deprecated. Instead, new subclasses should use: + virtual void getNextEnclosedFrameParameters(unsigned char*& framePtr, + unsigned dataSize, + unsigned& frameSize, + unsigned& frameDurationInMicroseconds); + + unsigned fPacketSize; + unsigned char* fBuf; + unsigned fHead; + unsigned fTail; + +private: + BufferedPacket* fNextPacket; // used to link together packets + + unsigned fUseCount; + unsigned short fRTPSeqNo; + unsigned fRTPTimestamp; + struct timeval fPresentationTime; // corresponding to "fRTPTimestamp" + Boolean fHasBeenSyncedUsingRTCP; + Boolean fRTPMarkerBit; + Boolean fIsFirstPacket; + struct timeval fTimeReceived; +}; + +// A 'factory' class for creating "BufferedPacket" objects. +// If you want to subclass "BufferedPacket", then you'll also +// want to subclass this, to redefine createNewPacket() + +class BufferedPacketFactory { +public: + BufferedPacketFactory(); + virtual ~BufferedPacketFactory(); + + virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource); +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTCP.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTCP.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTCP.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTCP.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,200 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTCP +// C++ header + +#ifndef _RTCP_HH +#define _RTCP_HH + +#ifndef _RTP_SINK_HH +#include "RTPSink.hh" +#endif +#ifndef _RTP_SOURCE_HH +#include "RTPSource.hh" +#endif + +class SDESItem { +public: + SDESItem(unsigned char tag, unsigned char const* value); + + unsigned char const* data() const {return fData;} + unsigned totalSize() const; + +private: + unsigned char fData[2 + 0xFF]; // first 2 bytes are tag and length +}; + +class RTCPMemberDatabase; // forward + +class RTCPInstance: public Medium { +public: + static RTCPInstance* createNew(UsageEnvironment& env, Groupsock* RTCPgs, + unsigned totSessionBW, /* in kbps */ + unsigned char const* cname, + RTPSink* sink, + RTPSource const* source, + Boolean isSSMSource = False); + + static Boolean lookupByName(UsageEnvironment& env, char const* instanceName, + RTCPInstance*& resultInstance); + + unsigned numMembers() const; + unsigned totSessionBW() const { return fTotSessionBW; } + + void setByeHandler(TaskFunc* handlerTask, void* clientData, + Boolean handleActiveParticipantsOnly = True); + // Assigns a handler routine to be called if a "BYE" arrives. + // The handler is called once only; for subsequent "BYE"s, + // "setByeHandler()" would need to be called again. + // If "handleActiveParticipantsOnly" is True, then the handler is called + // only if the SSRC is for a known sender (if we have a "RTPSource"), + // or if the SSRC is for a known receiver (if we have a "RTPSink"). + // This prevents (for example) the handler for a multicast receiver being + // called if some other multicast receiver happens to exit. + // If "handleActiveParticipantsOnly" is False, then the handler is called + // for any incoming RTCP "BYE". + void setSRHandler(TaskFunc* handlerTask, void* clientData); + void setRRHandler(TaskFunc* handlerTask, void* clientData); + // Assigns a handler routine to be called if a "SR" or "RR" + // (respectively) arrives. Unlike "setByeHandler()", the handler will + // be called once for each incoming "SR" or "RR". (To turn off handling, + // call the function again with "handlerTask" (and "clientData") as NULL. + void setSpecificRRHandler(netAddressBits fromAddress, Port fromPort, + TaskFunc* handlerTask, void* clientData); + // Like "setRRHandler()", but applies only to "RR" packets that come from + // a specific source address and port. (Note that if both a specific + // and a general "RR" handler function is set, then both will be called.) + + Groupsock* RTCPgs() const { return fRTCPInterface.gs(); } + + void setStreamSocket(int sockNum, unsigned char streamChannelId); + void addStreamSocket(int sockNum, unsigned char streamChannelId); + void removeStreamSocket(int sockNum, unsigned char streamChannelId) { + fRTCPInterface.removeStreamSocket(sockNum, streamChannelId); + } + // hacks to allow sending RTP over TCP (RFC 2236, section 10.12) + + void setAuxilliaryReadHandler(AuxHandlerFunc* handlerFunc, + void* handlerClientData) { + fRTCPInterface.setAuxilliaryReadHandler(handlerFunc, + handlerClientData); + } + +protected: + RTCPInstance(UsageEnvironment& env, Groupsock* RTPgs, unsigned totSessionBW, + unsigned char const* cname, + RTPSink* sink, RTPSource const* source, + Boolean isSSMSource); + // called only by createNew() + virtual ~RTCPInstance(); + +private: + // redefined virtual functions: + virtual Boolean isRTCPInstance() const; + +private: + void addReport(); + void addSR(); + void addRR(); + void enqueueCommonReportPrefix(unsigned char packetType, u_int32_t SSRC, + unsigned numExtraWords = 0); + void enqueueCommonReportSuffix(); + void enqueueReportBlock(RTPReceptionStats* receptionStats); + void addSDES(); + void addBYE(); + + void sendBuiltPacket(); + + static void onExpire(RTCPInstance* instance); + void onExpire1(); + + static void incomingReportHandler(RTCPInstance* instance, int /*mask*/); + void incomingReportHandler1(); + void onReceive(int typeOfPacket, int totPacketSize, u_int32_t ssrc); + + void unsetSpecificRRHandler(netAddressBits fromAddress, Port fromPort); + +private: + unsigned char* fInBuf; + OutPacketBuffer* fOutBuf; + RTPInterface fRTCPInterface; + unsigned fTotSessionBW; + RTPSink* fSink; + RTPSource const* fSource; + Boolean fIsSSMSource; + + SDESItem fCNAME; + RTCPMemberDatabase* fKnownMembers; + unsigned fOutgoingReportCount; // used for SSRC member aging + + double fAveRTCPSize; + int fIsInitial; + double fPrevReportTime; + double fNextReportTime; + int fPrevNumMembers; + + int fLastSentSize; + int fLastReceivedSize; + u_int32_t fLastReceivedSSRC; + int fTypeOfEvent; + int fTypeOfPacket; + Boolean fHaveJustSentPacket; + unsigned fLastPacketSentSize; + + TaskFunc* fByeHandlerTask; + void* fByeHandlerClientData; + Boolean fByeHandleActiveParticipantsOnly; + TaskFunc* fSRHandlerTask; + void* fSRHandlerClientData; + TaskFunc* fRRHandlerTask; + void* fRRHandlerClientData; + AddressPortLookupTable* fSpecificRRHandlerTable; + +public: // because this stuff is used by an external "C" function + void schedule(double nextTime); + void reschedule(double nextTime); + void sendReport(); + void sendBYE(); + int typeOfEvent() {return fTypeOfEvent;} + int sentPacketSize() {return fLastSentSize;} + int packetType() {return fTypeOfPacket;} + int receivedPacketSize() {return fLastReceivedSize;} + int checkNewSSRC(); + void removeLastReceivedSSRC(); + void removeSSRC(u_int32_t ssrc, Boolean alsoRemoveStats); +}; + +// RTCP packet types: +const unsigned char RTCP_PT_SR = 200; +const unsigned char RTCP_PT_RR = 201; +const unsigned char RTCP_PT_SDES = 202; +const unsigned char RTCP_PT_BYE = 203; +const unsigned char RTCP_PT_APP = 204; + +// SDES tags: +const unsigned char RTCP_SDES_END = 0; +const unsigned char RTCP_SDES_CNAME = 1; +const unsigned char RTCP_SDES_NAME = 2; +const unsigned char RTCP_SDES_EMAIL = 3; +const unsigned char RTCP_SDES_PHONE = 4; +const unsigned char RTCP_SDES_LOC = 5; +const unsigned char RTCP_SDES_TOOL = 6; +const unsigned char RTCP_SDES_NOTE = 7; +const unsigned char RTCP_SDES_PRIV = 8; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTPInterface.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTPInterface.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTPInterface.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTPInterface.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,103 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// An abstraction of a network interface used for RTP (or RTCP). +// (This allows the RTP-over-TCP hack (RFC 2326, section 10.12) to +// be implemented transparently.) +// C++ header + +#ifndef _RTP_INTERFACE_HH +#define _RTP_INTERFACE_HH + +#ifndef _MEDIA_HH +#include +#endif +#ifndef _GROUPSOCK_HH +#include "Groupsock.hh" +#endif + +// Typedef for an optional auxilliary handler function, to be called +// when each new packet is read: +typedef void AuxHandlerFunc(void* clientData, unsigned char* packet, + unsigned packetSize); + +typedef void ServerRequestAlternativeByteHandler(void* instance, u_int8_t requestByte); +// A hack that allows a handler for RTP/RTCP packets received over TCP to process RTSP commands that may also appear within +// the same TCP connection. A RTSP server implementation would supply a function like this - as a parameter to +// "ServerMediaSubsession::startStream()". + +class tcpStreamRecord { +public: + tcpStreamRecord(int streamSocketNum, unsigned char streamChannelId, + tcpStreamRecord* next); + virtual ~tcpStreamRecord(); + +public: + tcpStreamRecord* fNext; + int fStreamSocketNum; + unsigned char fStreamChannelId; +}; + +class RTPInterface { +public: + RTPInterface(Medium* owner, Groupsock* gs); + virtual ~RTPInterface(); + + Groupsock* gs() const { return fGS; } + + void setStreamSocket(int sockNum, unsigned char streamChannelId); + void addStreamSocket(int sockNum, unsigned char streamChannelId); + void removeStreamSocket(int sockNum, unsigned char streamChannelId); + void setServerRequestAlternativeByteHandler(ServerRequestAlternativeByteHandler* handler, void* clientData); + + void sendPacket(unsigned char* packet, unsigned packetSize); + void startNetworkReading(TaskScheduler::BackgroundHandlerProc* + handlerProc); + Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize, + unsigned& bytesRead, + struct sockaddr_in& fromAddress); + void stopNetworkReading(); + + UsageEnvironment& envir() const { return fOwner->envir(); } + + void setAuxilliaryReadHandler(AuxHandlerFunc* handlerFunc, + void* handlerClientData) { + fAuxReadHandlerFunc = handlerFunc; + fAuxReadHandlerClientData = handlerClientData; + } + + // A hack for supporting handlers for RTCP packets arriving interleaved over TCP: + int nextTCPReadStreamSocketNum() const { return fNextTCPReadStreamSocketNum; } + unsigned char nextTCPReadStreamChannelId() const { return fNextTCPReadStreamChannelId; } + +private: + friend class SocketDescriptor; + Medium* fOwner; + Groupsock* fGS; + tcpStreamRecord* fTCPStreams; // optional, for RTP-over-TCP streaming/receiving + + unsigned short fNextTCPReadSize; + // how much data (if any) is available to be read from the TCP stream + int fNextTCPReadStreamSocketNum; + unsigned char fNextTCPReadStreamChannelId; + TaskScheduler::BackgroundHandlerProc* fReadHandlerProc; // if any + + AuxHandlerFunc* fAuxReadHandlerFunc; + void* fAuxReadHandlerClientData; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTPSink.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTPSink.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTPSink.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTPSink.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,224 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTP Sinks +// C++ header + +#ifndef _RTP_SINK_HH +#define _RTP_SINK_HH + +#ifndef _MEDIA_SINK_HH +#include "MediaSink.hh" +#endif +#ifndef _RTP_INTERFACE_HH +#include "RTPInterface.hh" +#endif + +class RTPTransmissionStatsDB; // forward + +class RTPSink: public MediaSink { +public: + static Boolean lookupByName(UsageEnvironment& env, char const* sinkName, + RTPSink*& resultSink); + + // used by RTCP: + u_int32_t SSRC() const {return fSSRC;} + // later need a means of changing the SSRC if there's a collision ##### + u_int32_t convertToRTPTimestamp(struct timeval tv); + unsigned packetCount() const {return fPacketCount;} + unsigned octetCount() const {return fOctetCount;} + + // used by RTSP servers: + Groupsock const& groupsockBeingUsed() const { return *(fRTPInterface.gs()); } + Groupsock& groupsockBeingUsed() { return *(fRTPInterface.gs()); } + + unsigned char rtpPayloadType() const { return fRTPPayloadType; } + unsigned rtpTimestampFrequency() const { return fTimestampFrequency; } + void setRTPTimestampFrequency(unsigned freq) { + fTimestampFrequency = freq; + } + char const* rtpPayloadFormatName() const {return fRTPPayloadFormatName;} + + unsigned numChannels() const { return fNumChannels; } + + virtual char const* sdpMediaType() const; // for use in SDP m= lines + virtual char* rtpmapLine() const; // returns a string to be delete[]d + virtual char const* auxSDPLine(); + // optional SDP line (e.g. a=fmtp:...) + + u_int16_t currentSeqNo() const { return fSeqNo; } + u_int32_t presetNextTimestamp(); + // ensures that the next timestamp to be used will correspond to + // the current 'wall clock' time. + + RTPTransmissionStatsDB& transmissionStatsDB() const { + return *fTransmissionStatsDB; + } + + Boolean nextTimestampHasBeenPreset() const { return fNextTimestampHasBeenPreset; } + + void setStreamSocket(int sockNum, unsigned char streamChannelId) { + fRTPInterface.setStreamSocket(sockNum, streamChannelId); + } + void addStreamSocket(int sockNum, unsigned char streamChannelId) { + fRTPInterface.addStreamSocket(sockNum, streamChannelId); + } + void removeStreamSocket(int sockNum, unsigned char streamChannelId) { + fRTPInterface.removeStreamSocket(sockNum, streamChannelId); + } + void setServerRequestAlternativeByteHandler(ServerRequestAlternativeByteHandler* handler, void* clientData) { + fRTPInterface.setServerRequestAlternativeByteHandler(handler, clientData); + } + // hacks to allow sending RTP over TCP (RFC 2236, section 10.12) + + void getTotalBitrate(unsigned& outNumBytes, double& outElapsedTime); + // returns the number of bytes sent since the last time that we + // were called, and resets the counter. + +protected: + RTPSink(UsageEnvironment& env, + Groupsock* rtpGS, unsigned char rtpPayloadType, + u_int32_t rtpTimestampFrequency, + char const* rtpPayloadFormatName, + unsigned numChannels); + // abstract base class + + virtual ~RTPSink(); + + RTPInterface fRTPInterface; + unsigned char fRTPPayloadType; + unsigned fPacketCount, fOctetCount, fTotalOctetCount /*incl RTP hdr*/; + struct timeval fTotalOctetCountStartTime; + u_int32_t fCurrentTimestamp; + u_int16_t fSeqNo; + +private: + // redefined virtual functions: + virtual Boolean isRTPSink() const; + +private: + u_int32_t fSSRC, fTimestampBase; + unsigned fTimestampFrequency; + Boolean fNextTimestampHasBeenPreset; + char const* fRTPPayloadFormatName; + unsigned fNumChannels; + struct timeval fCreationTime; + + RTPTransmissionStatsDB* fTransmissionStatsDB; +}; + + +class RTPTransmissionStats; // forward + +class RTPTransmissionStatsDB { +public: + unsigned numReceivers() const { return fNumReceivers; } + + class Iterator { + public: + Iterator(RTPTransmissionStatsDB& receptionStatsDB); + virtual ~Iterator(); + + RTPTransmissionStats* next(); + // NULL if none + + private: + HashTable::Iterator* fIter; + }; + + // The following is called whenever a RTCP RR packet is received: + void noteIncomingRR(u_int32_t SSRC, struct sockaddr_in const& lastFromAddress, + unsigned lossStats, unsigned lastPacketNumReceived, + unsigned jitter, unsigned lastSRTime, unsigned diffSR_RRTime); + + // The following is called when a RTCP BYE packet is received: + void removeRecord(u_int32_t SSRC); + + RTPTransmissionStats* lookup(u_int32_t SSRC) const; + +private: // constructor and destructor, called only by RTPSink: + friend class RTPSink; + RTPTransmissionStatsDB(RTPSink& rtpSink); + virtual ~RTPTransmissionStatsDB(); + +private: + void add(u_int32_t SSRC, RTPTransmissionStats* stats); + +private: + friend class Iterator; + unsigned fNumReceivers; + RTPSink& fOurRTPSink; + HashTable* fTable; +}; + +class RTPTransmissionStats { +public: + u_int32_t SSRC() const {return fSSRC;} + struct sockaddr_in const& lastFromAddress() const {return fLastFromAddress;} + unsigned lastPacketNumReceived() const {return fLastPacketNumReceived;} + unsigned firstPacketNumReported() const {return fFirstPacketNumReported;} + unsigned totNumPacketsLost() const {return fTotNumPacketsLost;} + unsigned jitter() const {return fJitter;} + unsigned lastSRTime() const { return fLastSRTime; } + unsigned diffSR_RRTime() const { return fDiffSR_RRTime; } + unsigned roundTripDelay() const; + // The round-trip delay (in units of 1/65536 seconds) computed from + // the most recently-received RTCP RR packet. + struct timeval timeCreated() const {return fTimeCreated;} + struct timeval lastTimeReceived() const {return fTimeReceived;} + void getTotalOctetCount(u_int32_t& hi, u_int32_t& lo); + void getTotalPacketCount(u_int32_t& hi, u_int32_t& lo); + + // Information which requires at least two RRs to have been received: + Boolean oldValid() const {return fOldValid;} // Have two RRs been received? + unsigned packetsReceivedSinceLastRR() const; + u_int8_t packetLossRatio() const { return fPacketLossRatio; } + // as an 8-bit fixed-point number + int packetsLostBetweenRR() const; + +private: + // called only by RTPTransmissionStatsDB: + friend class RTPTransmissionStatsDB; + RTPTransmissionStats(RTPSink& rtpSink, u_int32_t SSRC); + virtual ~RTPTransmissionStats(); + + void noteIncomingRR(struct sockaddr_in const& lastFromAddress, + unsigned lossStats, unsigned lastPacketNumReceived, + unsigned jitter, + unsigned lastSRTime, unsigned diffSR_RRTime); + +private: + RTPSink& fOurRTPSink; + u_int32_t fSSRC; + struct sockaddr_in fLastFromAddress; + unsigned fLastPacketNumReceived; + u_int8_t fPacketLossRatio; + unsigned fTotNumPacketsLost; + unsigned fJitter; + unsigned fLastSRTime; + unsigned fDiffSR_RRTime; + struct timeval fTimeCreated, fTimeReceived; + Boolean fOldValid; + unsigned fOldLastPacketNumReceived; + unsigned fOldTotNumPacketsLost; + Boolean fFirstPacket; + unsigned fFirstPacketNumReported; + u_int32_t fLastOctetCount, fTotalOctetCount_hi, fTotalOctetCount_lo; + u_int32_t fLastPacketCount, fTotalPacketCount_hi, fTotalPacketCount_lo; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTPSource.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTPSource.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTPSource.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTPSource.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,256 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTP Sources +// C++ header + +#ifndef _RTP_SOURCE_HH +#define _RTP_SOURCE_HH + +#ifndef _FRAMED_SOURCE_HH +#include "FramedSource.hh" +#endif +#ifndef _RTP_INTERFACE_HH +#include "RTPInterface.hh" +#endif + +class RTPReceptionStatsDB; // forward + +class RTPSource: public FramedSource { +public: + static Boolean lookupByName(UsageEnvironment& env, char const* sourceName, + RTPSource*& resultSource); + + Boolean curPacketMarkerBit() const { return fCurPacketMarkerBit; } + + unsigned char rtpPayloadFormat() const { return fRTPPayloadFormat; } + + virtual Boolean hasBeenSynchronizedUsingRTCP(); + + Groupsock* RTPgs() const { return fRTPInterface.gs(); } + + virtual void setPacketReorderingThresholdTime(unsigned uSeconds) = 0; + + // used by RTCP: + u_int32_t SSRC() const { return fSSRC; } + // Note: This is *our* SSRC, not the SSRC in incoming RTP packets. + // later need a means of changing the SSRC if there's a collision ##### + + unsigned timestampFrequency() const {return fTimestampFrequency;} + + RTPReceptionStatsDB& receptionStatsDB() const { + return *fReceptionStatsDB; + } + + u_int32_t lastReceivedSSRC() const { return fLastReceivedSSRC; } + // Note: This is the SSRC in the most recently received RTP packet; not *our* SSRC + + void setStreamSocket(int sockNum, unsigned char streamChannelId) { + // hack to allow sending RTP over TCP (RFC 2236, section 10.12) + fRTPInterface.setStreamSocket(sockNum, streamChannelId); + } + + void setAuxilliaryReadHandler(AuxHandlerFunc* handlerFunc, + void* handlerClientData) { + fRTPInterface.setAuxilliaryReadHandler(handlerFunc, + handlerClientData); + } + + // Note that RTP receivers will usually not need to call either of the following two functions, because + // RTP sequence numbers and timestamps are usually not useful to receivers. + // (Our implementation of RTP reception already does all needed handling of RTP sequence numbers and timestamps.) + u_int16_t curPacketRTPSeqNum() const { return fCurPacketRTPSeqNum; } + u_int32_t curPacketRTPTimestamp() const { return fCurPacketRTPTimestamp; } + +protected: + RTPSource(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, u_int32_t rtpTimestampFrequency); + // abstract base class + virtual ~RTPSource(); + +protected: + RTPInterface fRTPInterface; + u_int16_t fCurPacketRTPSeqNum; + u_int32_t fCurPacketRTPTimestamp; + Boolean fCurPacketMarkerBit; + Boolean fCurPacketHasBeenSynchronizedUsingRTCP; + u_int32_t fLastReceivedSSRC; + +private: + // redefined virtual functions: + virtual Boolean isRTPSource() const; + virtual void getAttributes() const; + +private: + unsigned char fRTPPayloadFormat; + unsigned fTimestampFrequency; + u_int32_t fSSRC; + + RTPReceptionStatsDB* fReceptionStatsDB; +}; + + +class RTPReceptionStats; // forward + +class RTPReceptionStatsDB { +public: + unsigned totNumPacketsReceived() const { return fTotNumPacketsReceived; } + unsigned numActiveSourcesSinceLastReset() const { + return fNumActiveSourcesSinceLastReset; + } + + void reset(); + // resets periodic stats (called each time they're used to + // generate a reception report) + + class Iterator { + public: + Iterator(RTPReceptionStatsDB& receptionStatsDB); + virtual ~Iterator(); + + RTPReceptionStats* next(Boolean includeInactiveSources = False); + // NULL if none + + private: + HashTable::Iterator* fIter; + }; + + // The following is called whenever a RTP packet is received: + void noteIncomingPacket(u_int32_t SSRC, u_int16_t seqNum, + u_int32_t rtpTimestamp, + unsigned timestampFrequency, + Boolean useForJitterCalculation, + struct timeval& resultPresentationTime, + Boolean& resultHasBeenSyncedUsingRTCP, + unsigned packetSize /* payload only */); + + // The following is called whenever a RTCP SR packet is received: + void noteIncomingSR(u_int32_t SSRC, + u_int32_t ntpTimestampMSW, u_int32_t ntpTimestampLSW, + u_int32_t rtpTimestamp); + + // The following is called when a RTCP BYE packet is received: + void removeRecord(u_int32_t SSRC); + + RTPReceptionStats* lookup(u_int32_t SSRC) const; + +protected: // constructor and destructor, called only by RTPSource: + friend class RTPSource; + RTPReceptionStatsDB(); + virtual ~RTPReceptionStatsDB(); + +protected: + void add(u_int32_t SSRC, RTPReceptionStats* stats); + +protected: + friend class Iterator; + unsigned fNumActiveSourcesSinceLastReset; + +private: + HashTable* fTable; + unsigned fTotNumPacketsReceived; // for all SSRCs +}; + +class RTPReceptionStats { +public: + u_int32_t SSRC() const { return fSSRC; } + unsigned numPacketsReceivedSinceLastReset() const { + return fNumPacketsReceivedSinceLastReset; + } + unsigned totNumPacketsReceived() const { return fTotNumPacketsReceived; } + double totNumKBytesReceived() const; + + unsigned totNumPacketsExpected() const { + return fHighestExtSeqNumReceived - fBaseExtSeqNumReceived; + } + + unsigned baseExtSeqNumReceived() const { return fBaseExtSeqNumReceived; } + unsigned lastResetExtSeqNumReceived() const { + return fLastResetExtSeqNumReceived; + } + unsigned highestExtSeqNumReceived() const { + return fHighestExtSeqNumReceived; + } + + unsigned jitter() const; + + unsigned lastReceivedSR_NTPmsw() const { return fLastReceivedSR_NTPmsw; } + unsigned lastReceivedSR_NTPlsw() const { return fLastReceivedSR_NTPlsw; } + struct timeval const& lastReceivedSR_time() const { + return fLastReceivedSR_time; + } + + unsigned minInterPacketGapUS() const { return fMinInterPacketGapUS; } + unsigned maxInterPacketGapUS() const { return fMaxInterPacketGapUS; } + struct timeval const& totalInterPacketGaps() const { + return fTotalInterPacketGaps; + } + +protected: + // called only by RTPReceptionStatsDB: + friend class RTPReceptionStatsDB; + RTPReceptionStats(u_int32_t SSRC, u_int16_t initialSeqNum); + RTPReceptionStats(u_int32_t SSRC); + virtual ~RTPReceptionStats(); + +private: + void noteIncomingPacket(u_int16_t seqNum, u_int32_t rtpTimestamp, + unsigned timestampFrequency, + Boolean useForJitterCalculation, + struct timeval& resultPresentationTime, + Boolean& resultHasBeenSyncedUsingRTCP, + unsigned packetSize /* payload only */); + void noteIncomingSR(u_int32_t ntpTimestampMSW, u_int32_t ntpTimestampLSW, + u_int32_t rtpTimestamp); + void init(u_int32_t SSRC); + void initSeqNum(u_int16_t initialSeqNum); + void reset(); + // resets periodic stats (called each time they're used to + // generate a reception report) + +protected: + u_int32_t fSSRC; + unsigned fNumPacketsReceivedSinceLastReset; + unsigned fTotNumPacketsReceived; + u_int32_t fTotBytesReceived_hi, fTotBytesReceived_lo; + Boolean fHaveSeenInitialSequenceNumber; + unsigned fBaseExtSeqNumReceived; + unsigned fLastResetExtSeqNumReceived; + unsigned fHighestExtSeqNumReceived; + int fLastTransit; // used in the jitter calculation + u_int32_t fPreviousPacketRTPTimestamp; + double fJitter; + // The following are recorded whenever we receive a RTCP SR for this SSRC: + unsigned fLastReceivedSR_NTPmsw; // NTP timestamp (from SR), most-signif + unsigned fLastReceivedSR_NTPlsw; // NTP timestamp (from SR), least-signif + struct timeval fLastReceivedSR_time; + struct timeval fLastPacketReceptionTime; + unsigned fMinInterPacketGapUS, fMaxInterPacketGapUS; + struct timeval fTotalInterPacketGaps; + +private: + // Used to convert from RTP timestamp to 'wall clock' time: + Boolean fHasBeenSynchronized; + u_int32_t fSyncTimestamp; + struct timeval fSyncTime; +}; + + +Boolean seqNumLT(u_int16_t s1, u_int16_t s2); + // a 'less-than' on 16-bit sequence numbers + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTSPClient.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTSPClient.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTSPClient.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTSPClient.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,240 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// A generic RTSP client +// C++ header + +#ifndef _RTSP_CLIENT_HH +#define _RTSP_CLIENT_HH + +#ifndef _MEDIA_SESSION_HH +#include "MediaSession.hh" +#endif +#ifndef _NET_ADDRESS_HH +#include "NetAddress.hh" +#endif +#ifndef _DIGEST_AUTHENTICATION_HH +#include "DigestAuthentication.hh" +#endif + +class RTSPClient: public Medium { +public: + static RTSPClient* createNew(UsageEnvironment& env, + int verbosityLevel = 0, + char const* applicationName = NULL, + portNumBits tunnelOverHTTPPortNum = 0); + // If "tunnelOverHTTPPortNum" is non-zero, we tunnel RTSP (and RTP) + // over a HTTP connection with the given port number, using the technique + // described in Apple's document + + int socketNum() const { return fInputSocketNum; } + + static Boolean lookupByName(UsageEnvironment& env, + char const* sourceName, + RTSPClient*& resultClient); + + char* describeURL(char const* url, Authenticator* authenticator = NULL, + Boolean allowKasennaProtocol = False, int timeout = -1); + // Issues a RTSP "DESCRIBE" command + // Returns the SDP description of a session, or NULL if none + // (This is dynamically allocated, and must later be freed + // by the caller - using "delete[]") + char* describeWithPassword(char const* url, + char const* username, char const* password, + Boolean allowKasennaProtocol = False, + int timeout = -1); + // Uses "describeURL()" to do a "DESCRIBE" - first + // without using "password", then (if we get an Unauthorized + // response) with an authentication response computed from "password" + + Boolean announceSDPDescription(char const* url, + char const* sdpDescription, + Authenticator* authenticator = NULL, + int timeout = -1); + // Issues a RTSP "ANNOUNCE" command + // Returns True iff this command succeeds + Boolean announceWithPassword(char const* url, char const* sdpDescription, + char const* username, char const* password, int timeout = -1); + // Uses "announceSDPDescription()" to do an "ANNOUNCE" - first + // without using "password", then (if we get an Unauthorized + // response) with an authentication response computed from "password" + + char* sendOptionsCmd(char const* url, + char* username = NULL, char* password = NULL, + Authenticator* authenticator = NULL, + int timeout = -1); + // Issues a RTSP "OPTIONS" command + // Returns a string containing the list of options, or NULL + + Boolean setupMediaSubsession(MediaSubsession& subsession, + Boolean streamOutgoing = False, + Boolean streamUsingTCP = False, + Boolean forceMulticastOnUnspecified = False); + // Issues a RTSP "SETUP" command on "subsession". + // Returns True iff this command succeeds + // If "forceMulticastOnUnspecified" is True (and "streamUsingTCP" is False), + // then the client will request a multicast stream if the media address + // in the original SDP response was unspecified (i.e., 0.0.0.0). + // Note, however, that not all servers will support this. + + Boolean playMediaSession(MediaSession& session, + double start = 0.0f, double end = -1.0f, + float scale = 1.0f); + // Issues an aggregate RTSP "PLAY" command on "session". + // Returns True iff this command succeeds + // (Note: start=-1 means 'resume'; end=-1 means 'play to end') + Boolean playMediaSubsession(MediaSubsession& subsession, + double start = 0.0f, double end = -1.0f, + float scale = 1.0f, + Boolean hackForDSS = False); + // Issues a RTSP "PLAY" command on "subsession". + // Returns True iff this command succeeds + // (Note: start=-1 means 'resume'; end=-1 means 'play to end') + + Boolean pauseMediaSession(MediaSession& session); + // Issues an aggregate RTSP "PAUSE" command on "session". + // Returns True iff this command succeeds + Boolean pauseMediaSubsession(MediaSubsession& subsession); + // Issues a RTSP "PAUSE" command on "subsession". + // Returns True iff this command succeeds + + Boolean recordMediaSubsession(MediaSubsession& subsession); + // Issues a RTSP "RECORD" command on "subsession". + // Returns True iff this command succeeds + + Boolean setMediaSessionParameter(MediaSession& session, + char const* parameterName, + char const* parameterValue); + // Issues a RTSP "SET_PARAMETER" command on "subsession". + // Returns True iff this command succeeds + + Boolean getMediaSessionParameter(MediaSession& session, + char const* parameterName, + char*& parameterValue); + // Issues a RTSP "GET_PARAMETER" command on "subsession". + // Returns True iff this command succeeds + + Boolean teardownMediaSession(MediaSession& session); + // Issues an aggregate RTSP "TEARDOWN" command on "session". + // Returns True iff this command succeeds + Boolean teardownMediaSubsession(MediaSubsession& subsession); + // Issues a RTSP "TEARDOWN" command on "subsession". + // Returns True iff this command succeeds + + static Boolean parseRTSPURL(UsageEnvironment& env, char const* url, + NetAddress& address, portNumBits& portNum, + char const** urlSuffix = NULL); + // (ignores any "[:]@" in "url") + static Boolean parseRTSPURLUsernamePassword(char const* url, + char*& username, + char*& password); + + unsigned describeStatus() const { return fDescribeStatusCode; } + + void setUserAgentString(char const* userAgentStr); + // sets an alternative string to be used in RTSP "User-Agent:" headers + + unsigned sessionTimeoutParameter() const { return fSessionTimeoutParameter; } + +#ifdef SUPPORT_REAL_RTSP + Boolean usingRealNetworksChallengeResponse() const { return fRealChallengeStr != NULL; } +#endif + +protected: + RTSPClient(UsageEnvironment& env, int verbosityLevel, + char const* applicationName, portNumBits tunnelOverHTTPPortNum); + // called only by createNew(); + virtual ~RTSPClient(); + +private: // redefined virtual functions + virtual Boolean isRTSPClient() const; + +private: + void reset(); + void resetTCPSockets(); + + Boolean openConnectionFromURL(char const* url, Authenticator* authenticator, + int timeout = -1); + char* createAuthenticatorString(Authenticator const* authenticator, + char const* cmd, char const* url); + static void checkForAuthenticationFailure(unsigned responseCode, + char*& nextLineStart, + Authenticator* authenticator); + Boolean sendRequest(char const* requestString, char const* tag, + Boolean base64EncodeIfOverHTTP = True); + Boolean getResponse(char const* tag, + unsigned& bytesRead, unsigned& responseCode, + char*& firstLine, char*& nextLineStart, + Boolean checkFor200Response = True); + unsigned getResponse1(char*& responseBuffer, unsigned responseBufferSize); + Boolean parseResponseCode(char const* line, unsigned& responseCode); + Boolean parseTransportResponse(char const* line, + char*& serverAddressStr, + portNumBits& serverPortNum, + unsigned char& rtpChannelId, + unsigned char& rtcpChannelId); + Boolean parseRTPInfoHeader(char*& line, u_int16_t& seqNum, u_int32_t& timestamp); + Boolean parseScaleHeader(char const* line, float& scale); + Boolean parseGetParameterHeader(char const* line, + const char* param, + char*& value); + char const* sessionURL(MediaSession const& session) const; + void constructSubsessionURL(MediaSubsession const& subsession, + char const*& prefix, + char const*& separator, + char const*& suffix); + Boolean setupHTTPTunneling(char const* urlSuffix, Authenticator* authenticator); + + // Support for handling requests sent back by a server: + static void incomingRequestHandler(void*, int /*mask*/); + void incomingRequestHandler1(); + void handleCmd_notSupported(char const* cseq); + +private: + int fVerbosityLevel; + portNumBits fTunnelOverHTTPPortNum; + char* fUserAgentHeaderStr; + size_t fUserAgentHeaderStrSize; + int fInputSocketNum, fOutputSocketNum; + unsigned fServerAddress; + static unsigned fCSeq; // sequence number, used in consecutive requests + // Note: it's static, to ensure that it differs if more than one + // connection is made to the same server, using the same URL. + // Some servers (e.g., DSS) may have problems with this otherwise. + char* fBaseURL; + Authenticator fCurrentAuthenticator; + unsigned char fTCPStreamIdCount; // used for (optional) RTP/TCP + char* fLastSessionId; + unsigned fSessionTimeoutParameter; // optionally set in response "Session:" headers +#ifdef SUPPORT_REAL_RTSP + char* fRealChallengeStr; + char* fRealETagStr; +#endif + unsigned fDescribeStatusCode; + // 0: OK; 1: connection failed; 2: stream unavailable + char* fResponseBuffer; + size_t fResponseBufferSize; + + // The following fields are used to implement the non-standard Kasenna protocol: + Boolean fServerIsKasenna; + char* fKasennaContentType; + + // The following is used to deal with Microsoft servers' non-standard use of RTSP: + Boolean fServerIsMicrosoft; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTSPCommon.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTSPCommon.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/RTSPCommon.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/RTSPCommon.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,53 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Common routines used by both RTSP clients and servers +// C++ header + +#ifndef _RTSP_COMMON_HH +#define _RTSP_COMMON_HH + +#ifndef _BOOLEAN_HH +#include "Boolean.hh" +#endif + +#ifndef _MEDIA_HH +#include // includes some definitions perhaps needed for Borland compilers? +#endif + +#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4) +#define _strncasecmp _strnicmp +#define snprintf _snprintf +#else +#define _strncasecmp strncasecmp +#endif + +#define RTSP_PARAM_STRING_MAX 200 + +Boolean parseRTSPRequestString(char const *reqStr, unsigned reqStrSize, + char *resultCmdName, + unsigned resultCmdNameMaxSize, + char* resultURLPreSuffix, + unsigned resultURLPreSuffixMaxSize, + char* resultURLSuffix, + unsigned resultURLSuffixMaxSize, + char* resultCSeq, + unsigned resultCSeqMaxSize); + +Boolean parseRangeHeader(char const* buf, double& rangeStart, double& rangeEnd); + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/SimpleRTPSink.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/SimpleRTPSink.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/SimpleRTPSink.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/SimpleRTPSink.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,73 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// A simple RTP sink that packs frames into each outgoing +// packet, without any fragmentation or special headers. +// C++ header + +#ifndef _SIMPLE_RTP_SINK_HH +#define _SIMPLE_RTP_SINK_HH + +#ifndef _MULTI_FRAMED_RTP_SINK_HH +#include "MultiFramedRTPSink.hh" +#endif + +class SimpleRTPSink: public MultiFramedRTPSink { +public: + static SimpleRTPSink* + createNew(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + char const* sdpMediaTypeString, + char const* rtpPayloadFormatName, + unsigned numChannels = 1, + Boolean allowMultipleFramesPerPacket = True, + Boolean doNormalMBitRule = True); + // "doNormalMBitRule" means: If the medium (i.e., "sdpMediaTypeString") is other than "audio", set the RTP "M" bit + // on each outgoing packet iff it contains the last (or only) fragment of a frame. + // Otherwise (i.e., if "doNormalMBitRule" is False, or the medium is "audio"), leave the "M" bit unset. +protected: + SimpleRTPSink(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + char const* sdpMediaTypeString, + char const* rtpPayloadFormatName, + unsigned numChannels, + Boolean allowMultipleFramesPerPacket, + Boolean doNormalMBitRule); + // called only by createNew() + + virtual ~SimpleRTPSink(); + +protected: // redefined virtual functions + virtual void doSpecialFrameHandling(unsigned fragmentationOffset, + unsigned char* frameStart, + unsigned numBytesInFrame, + struct timeval framePresentationTime, + unsigned numRemainingBytes); + virtual + Boolean frameCanAppearAfterPacketStart(unsigned char const* frameStart, + unsigned numBytesInFrame) const; + virtual char const* sdpMediaType() const; + +private: + char const* fSDPMediaTypeString; + Boolean fAllowMultipleFramesPerPacket; + Boolean fSetMBitOnLastFrames; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/SimpleRTPSource.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/SimpleRTPSource.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/include/SimpleRTPSource.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/include/SimpleRTPSource.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,67 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// A RTP source for a simple RTP payload format that +// - doesn't have any special headers following the RTP header +// (if necessary, the "offset" parameter can be used to specify a +// special header that we just skip over) +// - doesn't have any special framing apart from the packet data itself +// C++ header + +#ifndef _SIMPLE_RTP_SOURCE_HH +#define _SIMPLE_RTP_SOURCE_HH + +#ifndef _MULTI_FRAMED_RTP_SOURCE_HH +#include "MultiFramedRTPSource.hh" +#endif + +class SimpleRTPSource: public MultiFramedRTPSource { +public: + static SimpleRTPSource* createNew(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + char const* mimeTypeString, + unsigned offset = 0, + Boolean doNormalMBitRule = True); + // "doNormalMBitRule" means: If the medium is not audio, use the RTP "M" + // bit on each incoming packet to indicate the last (or only) fragment + // of a frame. Otherwise (i.e., if "doNormalMBitRule" is False, or the medium is "audio"), the "M" bit is ignored. + +protected: + virtual ~SimpleRTPSource(); + +protected: + SimpleRTPSource(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + char const* mimeTypeString, unsigned offset, + Boolean doNormalMBitRule); + // called only by createNew() + +private: + // redefined virtual functions: + virtual Boolean processSpecialHeader(BufferedPacket* packet, + unsigned& resultSpecialHeaderSize); + virtual char const* MIMEtype() const; + +private: + char const* fMIMEtypeString; + unsigned fOffset; + Boolean fUseMBitForFrameEnd; +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/Locale.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/Locale.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/Locale.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/Locale.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,60 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Support for temporarily setting the locale (e.g., to "C" or "POSIX") for (e.g.) parsing or printing +// floating-point numbers in protocol headers, or calling toupper()/tolower() on human-input strings. +// Implementation + +#include "Locale.hh" +#include + +Locale::Locale(char const* newLocale, LocaleCategory category) { +#ifndef LOCALE_NOT_USED +#ifndef XLOCALE_NOT_USED + int categoryMask; + switch (category) { + case All: { categoryMask = LC_ALL_MASK; break; } + case Numeric: { categoryMask = LC_NUMERIC_MASK; break; } + } + fLocale = newlocale(categoryMask, newLocale, NULL); + fPrevLocale = uselocale(fLocale); +#else + switch (category) { + case All: { fCategoryNum = LC_ALL; break; } + case Numeric: { fCategoryNum = LC_NUMERIC; break; } + } + fPrevLocale = strDup(setlocale(fCategoryNum, NULL)); + setlocale(fCategoryNum, newLocale); +#endif +#endif +} + +Locale::~Locale() { +#ifndef LOCALE_NOT_USED +#ifndef XLOCALE_NOT_USED + if (fLocale != (locale_t)0) { + uselocale(fPrevLocale); + freelocale(fLocale); + } +#else + if (fPrevLocale != NULL) { + setlocale(fCategoryNum, fPrevLocale); + delete[] fPrevLocale; + } +#endif +#endif +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/Media.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/Media.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/Media.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/Media.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,163 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Media +// Implementation + +#include "Media.hh" +#include "HashTable.hh" + +////////// Medium ////////// + +Medium::Medium(UsageEnvironment& env) + : fEnviron(env), fNextTask(NULL) { + // First generate a name for the new medium: + MediaLookupTable::ourMedia(env)->generateNewName(fMediumName, mediumNameMaxLen); + env.setResultMsg(fMediumName); + + // Then add it to our table: + MediaLookupTable::ourMedia(env)->addNew(this, fMediumName); +} + +Medium::~Medium() { + // Remove any tasks that might be pending for us: + fEnviron.taskScheduler().unscheduleDelayedTask(fNextTask); +} + +Boolean Medium::lookupByName(UsageEnvironment& env, char const* mediumName, + Medium*& resultMedium) { + resultMedium = MediaLookupTable::ourMedia(env)->lookup(mediumName); + if (resultMedium == NULL) { + env.setResultMsg("Medium ", mediumName, " does not exist"); + return False; + } + + return True; +} + +void Medium::close(UsageEnvironment& env, char const* name) { + MediaLookupTable::ourMedia(env)->remove(name); +} + +void Medium::close(Medium* medium) { + if (medium == NULL) return; + + close(medium->envir(), medium->name()); +} + +Boolean Medium::isSource() const { + return False; // default implementation +} + +Boolean Medium::isSink() const { + return False; // default implementation +} + +Boolean Medium::isRTCPInstance() const { + return False; // default implementation +} + +Boolean Medium::isRTSPClient() const { + return False; // default implementation +} + +Boolean Medium::isRTSPServer() const { + return False; // default implementation +} + +Boolean Medium::isMediaSession() const { + return False; // default implementation +} + +Boolean Medium::isServerMediaSession() const { + return False; // default implementation +} + + +////////// _Tables implementation ////////// + +_Tables* _Tables::getOurTables(UsageEnvironment& env, Boolean createIfNotPresent) { + if (env.liveMediaPriv == NULL && createIfNotPresent) { + env.liveMediaPriv = new _Tables(env); + } + return (_Tables*)(env.liveMediaPriv); +} + +void _Tables::reclaimIfPossible() { + if (mediaTable == NULL && socketTable == NULL) { + fEnv.liveMediaPriv = NULL; + delete this; + } +} + +_Tables::_Tables(UsageEnvironment& env) + : mediaTable(NULL), socketTable(NULL), fEnv(env) { +} + +_Tables::~_Tables() { +} + + +////////// MediaLookupTable implementation ////////// + +MediaLookupTable* MediaLookupTable::ourMedia(UsageEnvironment& env) { + _Tables* ourTables = _Tables::getOurTables(env); + if (ourTables->mediaTable == NULL) { + // Create a new table to record the media that are to be created in + // this environment: + ourTables->mediaTable = new MediaLookupTable(env); + } + return ourTables->mediaTable; +} + +Medium* MediaLookupTable::lookup(char const* name) const { + return (Medium*)(fTable->Lookup(name)); +} + +void MediaLookupTable::addNew(Medium* medium, char* mediumName) { + fTable->Add(mediumName, (void*)medium); +} + +void MediaLookupTable::remove(char const* name) { + Medium* medium = lookup(name); + if (medium != NULL) { + fTable->Remove(name); + if (fTable->IsEmpty()) { + // We can also delete ourselves (to reclaim space): + _Tables* ourTables = _Tables::getOurTables(fEnv); + delete this; + ourTables->mediaTable = NULL; + ourTables->reclaimIfPossible(); + } + + delete medium; + } +} + +void MediaLookupTable::generateNewName(char* mediumName, + unsigned /*maxLen*/) { + // We should really use snprintf() here, but not all systems have it + sprintf(mediumName, "liveMedia%d", fNameGenerator++); +} + +MediaLookupTable::MediaLookupTable(UsageEnvironment& env) + : fEnv(env), fTable(HashTable::create(STRING_HASH_KEYS)), fNameGenerator(0) { +} + +MediaLookupTable::~MediaLookupTable() { + delete fTable; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MediaSession.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MediaSession.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MediaSession.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MediaSession.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,1245 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// A data structure that represents a session that consists of +// potentially multiple (audio and/or video) sub-sessions +// Implementation + +#include "liveMedia.hh" +#include "Locale.hh" +#ifdef SUPPORT_REAL_RTSP +#include "../RealRTSP/include/RealRTSP.hh" +#endif +#include "GroupsockHelper.hh" +#include + +////////// MediaSession ////////// + +MediaSession* MediaSession::createNew(UsageEnvironment& env, + char const* sdpDescription) { + MediaSession* newSession = new MediaSession(env); + if (newSession != NULL) { + if (!newSession->initializeWithSDP(sdpDescription)) { + delete newSession; + return NULL; + } + } + + return newSession; +} + +Boolean MediaSession::lookupByName(UsageEnvironment& env, + char const* instanceName, + MediaSession*& resultSession) { + resultSession = NULL; // unless we succeed + + Medium* medium; + if (!Medium::lookupByName(env, instanceName, medium)) return False; + + if (!medium->isMediaSession()) { + env.setResultMsg(instanceName, " is not a 'MediaSession' object"); + return False; + } + + resultSession = (MediaSession*)medium; + return True; +} + +MediaSession::MediaSession(UsageEnvironment& env) + : Medium(env), + fSubsessionsHead(NULL), fSubsessionsTail(NULL), + fConnectionEndpointName(NULL), fMaxPlayStartTime(0.0f), fMaxPlayEndTime(0.0f), + fScale(1.0f), fMediaSessionType(NULL), fSessionName(NULL), fSessionDescription(NULL), + fControlPath(NULL) { +#ifdef SUPPORT_REAL_RTSP + RealInitSDPAttributes(this); +#endif + fSourceFilterAddr.s_addr = 0; + + // Get our host name, and use this for the RTCP CNAME: + const unsigned maxCNAMElen = 100; + char CNAME[maxCNAMElen+1]; +#ifndef CRIS + gethostname((char*)CNAME, maxCNAMElen); +#else + // "gethostname()" isn't defined for this platform + sprintf(CNAME, "unknown host %d", (unsigned)(our_random()*0x7FFFFFFF)); +#endif + CNAME[maxCNAMElen] = '\0'; // just in case + fCNAME = strDup(CNAME); +} + +MediaSession::~MediaSession() { + delete fSubsessionsHead; + delete[] fCNAME; + delete[] fConnectionEndpointName; + delete[] fMediaSessionType; + delete[] fSessionName; + delete[] fSessionDescription; + delete[] fControlPath; +#ifdef SUPPORT_REAL_RTSP + RealReclaimSDPAttributes(this); +#endif +} + +Boolean MediaSession::isMediaSession() const { + return True; +} + +Boolean MediaSession::initializeWithSDP(char const* sdpDescription) { + if (sdpDescription == NULL) return False; + + // Begin by processing all SDP lines until we see the first "m=" + char const* sdpLine = sdpDescription; + char const* nextSDPLine; + while (1) { + if (!parseSDPLine(sdpLine, nextSDPLine)) return False; + //##### We should really check for: + // - "a=control:" attributes (to set the URL for aggregate control) + // - the correct SDP version (v=0) + if (sdpLine[0] == 'm') break; + sdpLine = nextSDPLine; + if (sdpLine == NULL) break; // there are no m= lines at all + + // Check for various special SDP lines that we understand: + if (parseSDPLine_s(sdpLine)) continue; + if (parseSDPLine_i(sdpLine)) continue; + if (parseSDPLine_c(sdpLine)) continue; + if (parseSDPAttribute_control(sdpLine)) continue; + if (parseSDPAttribute_range(sdpLine)) continue; + if (parseSDPAttribute_type(sdpLine)) continue; + if (parseSDPAttribute_source_filter(sdpLine)) continue; +#ifdef SUPPORT_REAL_RTSP + if (RealParseSDPAttributes(this, sdpLine)) continue; +#endif + } + + while (sdpLine != NULL) { + // We have a "m=" line, representing a new sub-session: + MediaSubsession* subsession = new MediaSubsession(*this); + if (subsession == NULL) { + envir().setResultMsg("Unable to create new MediaSubsession"); + return False; + } + + // Parse the line as "m= RTP/AVP " + // or "m= / RTP/AVP " + // (Should we be checking for >1 payload format number here?)##### + char* mediumName = strDupSize(sdpLine); // ensures we have enough space + char const* protocolName = NULL; + unsigned payloadFormat; + if ((sscanf(sdpLine, "m=%s %hu RTP/AVP %u", mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 || + sscanf(sdpLine, "m=%s %hu/%*u RTP/AVP %u", mediumName, &subsession->fClientPortNum, &payloadFormat) == 3) && payloadFormat <= 127) + { + protocolName = "RTP"; + } else if ((sscanf(sdpLine, "m=%s %hu UDP %u", + mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 || + sscanf(sdpLine, "m=%s %hu udp %u", + mediumName, &subsession->fClientPortNum, &payloadFormat) == 3 || + sscanf(sdpLine, "m=%s %hu RAW/RAW/UDP %u", + mediumName, &subsession->fClientPortNum, &payloadFormat) == 3) + && payloadFormat <= 127) { + // This is a RAW UDP source + protocolName = "UDP"; + } else { + // This "m=" line is bad; output an error message saying so: + char* sdpLineStr; + if (nextSDPLine == NULL) { + sdpLineStr = (char*)sdpLine; + } else { + sdpLineStr = strDup(sdpLine); + sdpLineStr[nextSDPLine-sdpLine] = '\0'; + } + envir() << "Bad SDP \"m=\" line: " << sdpLineStr << "\n"; + if (sdpLineStr != (char*)sdpLine) delete[] sdpLineStr; + + delete[] mediumName; + delete subsession; + + // Skip the following SDP lines, up until the next "m=": + while (1) { + sdpLine = nextSDPLine; + if (sdpLine == NULL) break; // we've reached the end + if (!parseSDPLine(sdpLine, nextSDPLine)) return False; + + if (sdpLine[0] == 'm') break; // we've reached the next subsession + } + continue; + } + + // Insert this subsession at the end of the list: + if (fSubsessionsTail == NULL) { + fSubsessionsHead = fSubsessionsTail = subsession; + } else { + fSubsessionsTail->setNext(subsession); + fSubsessionsTail = subsession; + } + + subsession->serverPortNum = subsession->fClientPortNum; // by default + + char const* mStart = sdpLine; + subsession->fSavedSDPLines = strDup(mStart); + + subsession->fMediumName = strDup(mediumName); + delete[] mediumName; + subsession->fProtocolName = strDup(protocolName); + subsession->fRTPPayloadFormat = payloadFormat; + + // Process the following SDP lines, up until the next "m=": + while (1) { + sdpLine = nextSDPLine; + if (sdpLine == NULL) break; // we've reached the end + if (!parseSDPLine(sdpLine, nextSDPLine)) return False; + + if (sdpLine[0] == 'm') break; // we've reached the next subsession + + // Check for various special SDP lines that we understand: + if (subsession->parseSDPLine_c(sdpLine)) continue; + if (subsession->parseSDPLine_b(sdpLine)) continue; + if (subsession->parseSDPAttribute_rtpmap(sdpLine)) continue; + if (subsession->parseSDPAttribute_control(sdpLine)) continue; + if (subsession->parseSDPAttribute_range(sdpLine)) continue; + if (subsession->parseSDPAttribute_fmtp(sdpLine)) continue; + if (subsession->parseSDPAttribute_source_filter(sdpLine)) continue; + if (subsession->parseSDPAttribute_x_dimensions(sdpLine)) continue; + if (subsession->parseSDPAttribute_framerate(sdpLine)) continue; +#ifdef SUPPORT_REAL_RTSP + if (RealParseSDPAttributes(subsession, sdpLine)) continue; +#endif + + // (Later, check for malformed lines, and other valid SDP lines#####) + } + if (sdpLine != NULL) subsession->fSavedSDPLines[sdpLine-mStart] = '\0'; + + // If we don't yet know the codec name, try looking it up from the + // list of static payload types: + if (subsession->fCodecName == NULL) { + subsession->fCodecName = lookupPayloadFormat(subsession->fRTPPayloadFormat, + subsession->fRTPTimestampFrequency, + subsession->fNumChannels); + if (subsession->fCodecName == NULL) { + char typeStr[20]; + sprintf(typeStr, "%d", subsession->fRTPPayloadFormat); + envir().setResultMsg("Unknown codec name for RTP payload type ", typeStr); + return False; + } + } + + // If we don't yet know this subsession's RTP timestamp frequency + // (because it uses a dynamic payload type and the corresponding + // SDP "rtpmap" attribute erroneously didn't specify it), + // then guess it now: + if (subsession->fRTPTimestampFrequency == 0) { + subsession->fRTPTimestampFrequency = guessRTPTimestampFrequency(subsession->fMediumName, subsession->fCodecName); + } + } + + return True; +} + +Boolean MediaSession::parseSDPLine(char const* inputLine, char const*& nextLine) +{ + // Begin by finding the start of the next line (if any): + nextLine = NULL; + for (char const* ptr = inputLine; *ptr != '\0'; ++ptr) { + if (*ptr == '\r' || *ptr == '\n') { + // We found the end of the line + ++ptr; + while (*ptr == '\r' || *ptr == '\n') ++ptr; + nextLine = ptr; + if (nextLine[0] == '\0') nextLine = NULL; // special case for end + break; + } + } + + // Then, check that this line is a SDP line of the form = + // (However, we also accept blank lines in the input.) + if (inputLine[0] == '\r' || inputLine[0] == '\n') return True; + if (strlen(inputLine) < 2 || inputLine[1] != '=' + || inputLine[0] < 'a' || inputLine[0] > 'z') { + envir().setResultMsg("Invalid SDP line: ", inputLine); + return False; + } + + return True; +} + +static char* parseCLine(char const* sdpLine) { + char* resultStr = NULL; + char* buffer = strDupSize(sdpLine); // ensures we have enough space + if (sscanf(sdpLine, "c=IN IP4 %[^/\r\n]", buffer) == 1) { + // Later, handle the optional / and / ##### + resultStr = strDup(buffer); + } + delete[] buffer; + + return resultStr; +} + +Boolean MediaSession::parseSDPLine_s(char const* sdpLine) { + // Check for "s=" line + char* buffer = strDupSize(sdpLine); + Boolean parseSuccess = False; + + if (sscanf(sdpLine, "s=%[^\r\n]", buffer) == 1) { + delete[] fSessionName; fSessionName = strDup(buffer); + parseSuccess = True; + } + delete[] buffer; + + return parseSuccess; +} + +Boolean MediaSession::parseSDPLine_i(char const* sdpLine) { + // Check for "i=" line + char* buffer = strDupSize(sdpLine); + Boolean parseSuccess = False; + + if (sscanf(sdpLine, "i=%[^\r\n]", buffer) == 1) { + delete[] fSessionDescription; fSessionDescription = strDup(buffer); + parseSuccess = True; + } + delete[] buffer; + + return parseSuccess; +} + +Boolean MediaSession::parseSDPLine_c(char const* sdpLine) { + // Check for "c=IN IP4 " + // or "c=IN IP4 /" + // (Later, do something with also #####) + char* connectionEndpointName = parseCLine(sdpLine); + if (connectionEndpointName != NULL) { + delete[] fConnectionEndpointName; + fConnectionEndpointName = connectionEndpointName; + return True; + } + + return False; +} + +Boolean MediaSession::parseSDPAttribute_type(char const* sdpLine) { + // Check for a "a=type:broadcast|meeting|moderated|test|H.332|recvonly" line: + Boolean parseSuccess = False; + + char* buffer = strDupSize(sdpLine); + if (sscanf(sdpLine, "a=type: %[^ ]", buffer) == 1) { + delete[] fMediaSessionType; + fMediaSessionType = strDup(buffer); + parseSuccess = True; + } + delete[] buffer; + + return parseSuccess; +} + +static Boolean parseRangeAttribute(char const* sdpLine, double& startTime, double& endTime) { + return sscanf(sdpLine, "a=range: npt = %lg - %lg", &startTime, &endTime) == 2; +} + +Boolean MediaSession::parseSDPAttribute_control(char const* sdpLine) { + // Check for a "a=control:" line: + Boolean parseSuccess = False; + + char* controlPath = strDupSize(sdpLine); // ensures we have enough space + if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) { + parseSuccess = True; + delete[] fControlPath; fControlPath = strDup(controlPath); + } + delete[] controlPath; + + return parseSuccess; +} + +Boolean MediaSession::parseSDPAttribute_range(char const* sdpLine) { + // Check for a "a=range:npt=-" line: + // (Later handle other kinds of "a=range" attributes also???#####) + Boolean parseSuccess = False; + + double playStartTime; + double playEndTime; + if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) { + parseSuccess = True; + if (playStartTime > fMaxPlayStartTime) { + fMaxPlayStartTime = playStartTime; + } + if (playEndTime > fMaxPlayEndTime) { + fMaxPlayEndTime = playEndTime; + } + } + + return parseSuccess; +} + +static Boolean parseSourceFilterAttribute(char const* sdpLine, + struct in_addr& sourceAddr) { + // Check for a "a=source-filter:incl IN IP4 " line. + // Note: At present, we don't check that really matches + // one of our multicast addresses. We also don't support more than + // one ##### + Boolean result = False; // until we succeed + char* sourceName = strDupSize(sdpLine); // ensures we have enough space + do { + if (sscanf(sdpLine, "a=source-filter: incl IN IP4 %*s %s", sourceName) != 1) break; + + // Now, convert this name to an address, if we can: + NetAddressList addresses(sourceName); + if (addresses.numAddresses() == 0) break; + + netAddressBits sourceAddrBits + = *(netAddressBits*)(addresses.firstAddress()->data()); + if (sourceAddrBits == 0) break; + + sourceAddr.s_addr = sourceAddrBits; + result = True; + } while (0); + + delete[] sourceName; + return result; +} + +Boolean MediaSession +::parseSDPAttribute_source_filter(char const* sdpLine) { + return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr); +} + +char* MediaSession::lookupPayloadFormat(unsigned char rtpPayloadType, + unsigned& freq, unsigned& nCh) { + // Look up the codec name and timestamp frequency for known (static) + // RTP payload formats. + char const* temp = NULL; + switch (rtpPayloadType) { + case 0: {temp = "PCMU"; freq = 8000; nCh = 1; break;} + case 2: {temp = "G726-32"; freq = 8000; nCh = 1; break;} + case 3: {temp = "GSM"; freq = 8000; nCh = 1; break;} + case 4: {temp = "G723"; freq = 8000; nCh = 1; break;} + case 5: {temp = "DVI4"; freq = 8000; nCh = 1; break;} + case 6: {temp = "DVI4"; freq = 16000; nCh = 1; break;} + case 7: {temp = "LPC"; freq = 8000; nCh = 1; break;} + case 8: {temp = "PCMA"; freq = 8000; nCh = 1; break;} + case 9: {temp = "G722"; freq = 8000; nCh = 1; break;} + case 10: {temp = "L16"; freq = 44100; nCh = 2; break;} + case 11: {temp = "L16"; freq = 44100; nCh = 1; break;} + case 12: {temp = "QCELP"; freq = 8000; nCh = 1; break;} + case 14: {temp = "MPA"; freq = 90000; nCh = 1; break;} + // 'number of channels' is actually encoded in the media stream + case 15: {temp = "G728"; freq = 8000; nCh = 1; break;} + case 16: {temp = "DVI4"; freq = 11025; nCh = 1; break;} + case 17: {temp = "DVI4"; freq = 22050; nCh = 1; break;} + case 18: {temp = "G729"; freq = 8000; nCh = 1; break;} + case 25: {temp = "CELB"; freq = 90000; nCh = 1; break;} + case 26: {temp = "JPEG"; freq = 90000; nCh = 1; break;} + case 28: {temp = "NV"; freq = 90000; nCh = 1; break;} + case 31: {temp = "H261"; freq = 90000; nCh = 1; break;} + case 32: {temp = "MPV"; freq = 90000; nCh = 1; break;} + case 33: {temp = "MP2T"; freq = 90000; nCh = 1; break;} + case 34: {temp = "H263"; freq = 90000; nCh = 1; break;} + }; + + return strDup(temp); +} + +unsigned MediaSession::guessRTPTimestampFrequency(char const* mediumName, + char const* codecName) { + // By default, we assume that audio sessions use a frequency of 8000, + // video sessions use a frequency of 90000, + // and text sessions use a frequency of 1000. + // Begin by checking for known exceptions to this rule + // (where the frequency is known unambiguously (e.g., not like "DVI4")) + if (strcmp(codecName, "L16") == 0) return 44100; + if (strcmp(codecName, "MPA") == 0 + || strcmp(codecName, "MPA-ROBUST") == 0 + || strcmp(codecName, "X-MP3-DRAFT-00") == 0) return 90000; + + // Now, guess default values: + if (strcmp(mediumName, "video") == 0) return 90000; + else if (strcmp(mediumName, "text") == 0) return 1000; + return 8000; // for "audio", and any other medium +} + +Boolean MediaSession +::initiateByMediaType(char const* mimeType, + MediaSubsession*& resultSubsession, + int useSpecialRTPoffset) { + // Look through this session's subsessions for media that match "mimeType" + resultSubsession = NULL; + MediaSubsessionIterator iter(*this); + MediaSubsession* subsession; + while ((subsession = iter.next()) != NULL) { + Boolean wasAlreadyInitiated = subsession->readSource() != NULL; + if (!wasAlreadyInitiated) { + // Try to create a source for this subsession: + if (!subsession->initiate(useSpecialRTPoffset)) return False; + } + + // Make sure the source's MIME type is one that we handle: + if (strcmp(subsession->readSource()->MIMEtype(), mimeType) != 0) { + if (!wasAlreadyInitiated) subsession->deInitiate(); + continue; + } + + resultSubsession = subsession; + break; // use this + } + + if (resultSubsession == NULL) { + envir().setResultMsg("Session has no usable media subsession"); + return False; + } + + return True; +} + + +////////// MediaSubsessionIterator ////////// + +MediaSubsessionIterator::MediaSubsessionIterator(MediaSession const& session) + : fOurSession(session) { + reset(); +} + +MediaSubsessionIterator::~MediaSubsessionIterator() { +} + +MediaSubsession* MediaSubsessionIterator::next() { + MediaSubsession* result = fNextPtr; + + if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext; + + return result; +} + +void MediaSubsessionIterator::reset() { + fNextPtr = fOurSession.fSubsessionsHead; +} + +////////// MediaSubsession ////////// + +MediaSubsession::MediaSubsession(MediaSession& parent) + : sessionId(NULL), serverPortNum(0), sink(NULL), miscPtr(NULL), + fParent(parent), fNext(NULL), + fConnectionEndpointName(NULL), + fClientPortNum(0), fRTPPayloadFormat(0xFF), + fSavedSDPLines(NULL), fMediumName(NULL), fCodecName(NULL), fProtocolName(NULL), + fRTPTimestampFrequency(0), fControlPath(NULL), + fSourceFilterAddr(parent.sourceFilterAddr()), fBandwidth(0), + fAuxiliarydatasizelength(0), fConstantduration(0), fConstantsize(0), + fCRC(0), fCtsdeltalength(0), fDe_interleavebuffersize(0), fDtsdeltalength(0), + fIndexdeltalength(0), fIndexlength(0), fInterleaving(0), fMaxdisplacement(0), + fObjecttype(0), fOctetalign(0), fProfile_level_id(0), fRobustsorting(0), + fSizelength(0), fStreamstateindication(0), fStreamtype(0), + fCpresent(False), fRandomaccessindication(False), + fConfig(NULL), fMode(NULL), fSpropParameterSets(NULL), + fPlayStartTime(0.0), fPlayEndTime(0.0), + fVideoWidth(0), fVideoHeight(0), fVideoFPS(0), fNumChannels(1), fScale(1.0f), fNPT_PTS_Offset(0.0f), + fRTPSocket(NULL), fRTCPSocket(NULL), + fRTPSource(NULL), fRTCPInstance(NULL), fReadSource(NULL), + rtpChannelId(0), rtcpChannelId(0) +{ + rtpInfo.seqNum = 0; rtpInfo.timestamp = 0; rtpInfo.infoIsNew = False; +#ifdef SUPPORT_REAL_RTSP + RealInitSDPAttributes(this); +#endif +} + +MediaSubsession::~MediaSubsession() { + deInitiate(); + + delete[] fConnectionEndpointName; delete[] fSavedSDPLines; + delete[] fMediumName; delete[] fCodecName; delete[] fProtocolName; + delete[] fControlPath; delete[] fConfig; delete[] fMode; delete[] fSpropParameterSets; + + delete fNext; +#ifdef SUPPORT_REAL_RTSP + RealReclaimSDPAttributes(this); +#endif +} + +double MediaSubsession::playStartTime() const { + if (fPlayStartTime > 0) return fPlayStartTime; + + return fParent.playStartTime(); +} + +double MediaSubsession::playEndTime() const { + if (fPlayEndTime > 0) return fPlayEndTime; + + return fParent.playEndTime(); +} + +Boolean MediaSubsession::initiate(int useSpecialRTPoffset) { + if (fReadSource != NULL) return True; // has already been initiated + + do { + if (fCodecName == NULL) { + env().setResultMsg("Codec is unspecified"); + break; + } + + // Create RTP and RTCP 'Groupsocks' on which to receive incoming data. + // (Groupsocks will work even for unicast addresses) + struct in_addr tempAddr; + tempAddr.s_addr = connectionEndpointAddress(); + // This could get changed later, as a result of a RTSP "SETUP" + + if (fClientPortNum != 0) { + // The sockets' port numbers were specified for us. Use these: + fClientPortNum = fClientPortNum&~1; // even + if (isSSM()) { + fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, fClientPortNum); + } else { + fRTPSocket = new Groupsock(env(), tempAddr, fClientPortNum, 255); + } + if (fRTPSocket == NULL) { + env().setResultMsg("Failed to create RTP socket"); + break; + } + + // Set our RTCP port to be the RTP port +1 + portNumBits const rtcpPortNum = fClientPortNum|1; + if (isSSM()) { + fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum); + } else { + fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255); + } + if (fRTCPSocket == NULL) { + char tmpBuf[100]; + sprintf(tmpBuf, "Failed to create RTCP socket (port %d)", rtcpPortNum); + env().setResultMsg(tmpBuf); + break; + } + } else { + // Port numbers were not specified in advance, so we use ephemeral port numbers. + // Create sockets until we get a port-number pair (even: RTP; even+1: RTCP). + // We need to make sure that we don't keep trying to use the same bad port numbers over and over again. + // so we store bad sockets in a table, and delete them all when we're done. + HashTable* socketHashTable = HashTable::create(ONE_WORD_HASH_KEYS); + if (socketHashTable == NULL) break; + Boolean success = False; + NoReuse dummy; // ensures that our new ephemeral port number won't be one that's already in use + + while (1) { + // Create a new socket: + if (isSSM()) { + fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, 0); + } else { + fRTPSocket = new Groupsock(env(), tempAddr, 0, 255); + } + if (fRTPSocket == NULL) { + env().setResultMsg("MediaSession::initiate(): unable to create RTP and RTCP sockets"); + break; + } + + // Get the client port number, and check whether it's even (for RTP): + Port clientPort(0); + if (!getSourcePort(env(), fRTPSocket->socketNum(), clientPort)) { + break; + } + fClientPortNum = ntohs(clientPort.num()); + if ((fClientPortNum&1) != 0) { // it's odd + // Record this socket in our table, and keep trying: + unsigned key = (unsigned)fClientPortNum; + Groupsock* existing = (Groupsock*)socketHashTable->Add((char const*)key, fRTPSocket); + delete existing; // in case it wasn't NULL + continue; + } + + // Make sure we can use the next (i.e., odd) port number, for RTCP: + portNumBits rtcpPortNum = fClientPortNum|1; + if (isSSM()) { + fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum); + } else { + fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255); + } + if (fRTCPSocket != NULL) { + // Success! Use these two sockets. + success = True; + break; + } else { + // We couldn't create the RTCP socket (perhaps that port number's already in use elsewhere?). + // Record the first socket in our table, and keep trying: + unsigned key = (unsigned)fClientPortNum; + Groupsock* existing = (Groupsock*)socketHashTable->Add((char const*)key, fRTPSocket); + delete existing; // in case it wasn't NULL + continue; + } + } + + // Clean up the socket hash table (and contents): + Groupsock* oldGS; + while ((oldGS = (Groupsock*)socketHashTable->RemoveNext()) != NULL) { + delete oldGS; + } + delete socketHashTable; + + if (!success) break; // a fatal error occurred trying to create the RTP and RTCP sockets; we can't continue + } + + // Try to use a big receive buffer for RTP - at least 0.1 second of + // specified bandwidth and at least 50 KB + unsigned rtpBufSize = fBandwidth * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes + if (rtpBufSize < 50 * 1024) + rtpBufSize = 50 * 1024; + increaseReceiveBufferTo(env(), fRTPSocket->socketNum(), rtpBufSize); + + // ASSERT: fRTPSocket != NULL && fRTCPSocket != NULL + if (isSSM()) { + // Special case for RTCP SSM: Send RTCP packets back to the source via unicast: + fRTCPSocket->changeDestinationParameters(fSourceFilterAddr,0,~0); + } + + // Check "fProtocolName" + if (strcmp(fProtocolName, "UDP") == 0) { + // A UDP-packetized stream (*not* a RTP stream) + fReadSource = BasicUDPSource::createNew(env(), fRTPSocket); + fRTPSource = NULL; // Note! + + if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream + fReadSource = MPEG2TransportStreamFramer::createNew(env(), fReadSource); + // this sets "durationInMicroseconds" correctly, based on the PCR values + } + } + else { + // Check "fCodecName" against the set of codecs that we support, + // and create our RTP source accordingly + // (Later make this code more efficient, as this set grows #####) + // (Also, add more fmts that can be implemented by SimpleRTPSource#####) +#if 0 + Boolean createSimpleRTPSource = False; // by default; can be changed below + Boolean doNormalMBitRule = False; // default behavior if "createSimpleRTPSource" is True + if (strcmp(fCodecName, "QCELP") == 0) { // QCELP audio + fReadSource = QCELPAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource, + fRTPPayloadFormat, + fRTPTimestampFrequency); + // Note that fReadSource will differ from fRTPSource in this case + } else if (strcmp(fCodecName, "AMR") == 0) { // AMR audio (narrowband) + fReadSource = AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource, + fRTPPayloadFormat, 0 /*isWideband*/, + fNumChannels, fOctetalign, fInterleaving, + fRobustsorting, fCRC); + // Note that fReadSource will differ from fRTPSource in this case + } else if (strcmp(fCodecName, "AMR-WB") == 0) { // AMR audio (wideband) + fReadSource = AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource, + fRTPPayloadFormat, 1 /*isWideband*/, + fNumChannels, fOctetalign, fInterleaving, + fRobustsorting, fCRC); + // Note that fReadSource will differ from fRTPSource in this case + } else if (strcmp(fCodecName, "MPA") == 0) { // MPEG-1 or 2 audio + fReadSource = fRTPSource = MPEG1or2AudioRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else if (strcmp(fCodecName, "MPA-ROBUST") == 0) { // robust MP3 audio + fRTPSource = MP3ADURTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat, + fRTPTimestampFrequency); + if (fRTPSource == NULL) break; + + // Add a filter that deinterleaves the ADUs after depacketizing them: + MP3ADUdeinterleaver* deinterleaver = MP3ADUdeinterleaver::createNew(env(), fRTPSource); + if (deinterleaver == NULL) break; + + // Add another filter that converts these ADUs to MP3 frames: + fReadSource = MP3FromADUSource::createNew(env(), deinterleaver); + } else if (strcmp(fCodecName, "X-MP3-DRAFT-00") == 0) { + // a non-standard variant of "MPA-ROBUST" used by RealNetworks + // (one 'ADU'ized MP3 frame per packet; no headers) + fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat, + fRTPTimestampFrequency, + "audio/MPA-ROBUST" /*hack*/); + if (fRTPSource == NULL) break; + + // Add a filter that converts these ADUs to MP3 frames: + fReadSource = MP3FromADUSource::createNew(env(), fRTPSource, False /*no ADU header*/); + } else if (strcmp(fCodecName, "MP4A-LATM") == 0) { // MPEG-4 LATM audio + fReadSource = fRTPSource = MPEG4LATMAudioRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else if (strcmp(fCodecName, "AC3") == 0) { // AC3 audio + fReadSource = fRTPSource = AC3AudioRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else if (strcmp(fCodecName, "MP4V-ES") == 0) { // MPEG-4 Elem Str vid + fReadSource = fRTPSource + = MPEG4ESVideoRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else if (strcmp(fCodecName, "MPEG4-GENERIC") == 0) { + fReadSource = fRTPSource = MPEG4GenericRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency, + fMediumName, fMode, + fSizelength, fIndexlength, + fIndexdeltalength); + } else if (strcmp(fCodecName, "MPV") == 0) { // MPEG-1 or 2 video + fReadSource = fRTPSource = MPEG1or2VideoRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else +#endif // 0 + if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream + fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat, + fRTPTimestampFrequency, "video/MP2T", + 0, False); + fReadSource = MPEG2TransportStreamFramer::createNew(env(), fRTPSource); + // this sets "durationInMicroseconds" correctly, based on the PCR values + } +#if 0 + else if (strcmp(fCodecName, "H261") == 0) { // H.261 + fReadSource = fRTPSource = H261VideoRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else if (strcmp(fCodecName, "H263-1998") == 0 || strcmp(fCodecName, "H263-2000") == 0) { // H.263+ + fReadSource = fRTPSource = H263plusVideoRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else if (strcmp(fCodecName, "H264") == 0) { + fReadSource = fRTPSource = H264VideoRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else if (strcmp(fCodecName, "DV") == 0) { + fReadSource = fRTPSource = DVVideoRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency); + } else if (strcmp(fCodecName, "JPEG") == 0) { // motion JPEG + fReadSource = fRTPSource = JPEGVideoRTPSource::createNew(env(), fRTPSocket, + fRTPPayloadFormat, + fRTPTimestampFrequency, + videoWidth(), + videoHeight()); + //} else if (strcmp(fCodecName, "X-QT") == 0 || strcmp(fCodecName, "X-QUICKTIME") == 0) { + // // Generic QuickTime streams, as defined in + // // + // char* mimeType = new char[strlen(mediumName()) + strlen(codecName()) + 2] ; + // sprintf(mimeType, "%s/%s", mediumName(), codecName()); + // fReadSource = fRTPSource = QuickTimeGenericRTPSource::createNew(env(), fRTPSocket, + // fRTPPayloadFormat, + // fRTPTimestampFrequency, + // mimeType); + // delete[] mimeType; +#ifdef SUPPORT_REAL_RTSP + } else if (strcmp(fCodecName, "X-PN-REALAUDIO") == 0 || + strcmp(fCodecName, "X-PN-MULTIRATE-REALAUDIO-LIVE") == 0 || + strcmp(fCodecName, "X-PN-REALVIDEO") == 0 || + strcmp(fCodecName, "X-PN-MULTIRATE-REALVIDEO-LIVE") == 0) { + // A RealNetworks 'RDT' stream (*not* a RTP stream) + fReadSource = RealRDTSource::createNew(env()); + fRTPSource = NULL; // Note! + parentSession().isRealNetworksRDT = True; +#endif + } else if ( strcmp(fCodecName, "PCMU") == 0 // PCM u-law audio + || strcmp(fCodecName, "GSM") == 0 // GSM audio + || strcmp(fCodecName, "PCMA") == 0 // PCM a-law audio + || strcmp(fCodecName, "L16") == 0 // 16-bit linear audio + || strcmp(fCodecName, "MP1S") == 0 // MPEG-1 System Stream + || strcmp(fCodecName, "MP2P") == 0 // MPEG-2 Program Stream + || strcmp(fCodecName, "L8") == 0 // 8-bit linear audio + || strcmp(fCodecName, "G726-16") == 0 // G.726, 16 kbps + || strcmp(fCodecName, "G726-24") == 0 // G.726, 24 kbps + || strcmp(fCodecName, "G726-32") == 0 // G.726, 32 kbps + || strcmp(fCodecName, "G726-40") == 0 // G.726, 40 kbps + || strcmp(fCodecName, "SPEEX") == 0 // SPEEX audio + || strcmp(fCodecName, "T140") == 0 // T.140 text (RFC 4103) + ) { + createSimpleRTPSource = True; + useSpecialRTPoffset = 0; + } else if (useSpecialRTPoffset >= 0) { + // We don't know this RTP payload format, but try to receive + // it using a 'SimpleRTPSource' with the specified header offset: + createSimpleRTPSource = True; + } else { + env().setResultMsg("RTP payload format unknown or not supported"); + break; + } + + if (createSimpleRTPSource) { + char* mimeType = new char[strlen(mediumName()) + strlen(codecName()) + 2] ; + sprintf(mimeType, "%s/%s", mediumName(), codecName()); + fReadSource = fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat, + fRTPTimestampFrequency, mimeType, + (unsigned)useSpecialRTPoffset, + doNormalMBitRule); + delete[] mimeType; + } +#endif // 0 + } + + if (fReadSource == NULL) { + env().setResultMsg("Failed to create read source"); + break; + } + + // Finally, create our RTCP instance. (It starts running automatically) + if (fRTPSource != NULL) { + // If bandwidth is specified, use it and add 5% for RTCP overhead. + // Otherwise make a guess at 500 kbps. + unsigned totSessionBandwidth = fBandwidth ? fBandwidth + fBandwidth / 20 : 500; + fRTCPInstance = RTCPInstance::createNew(env(), fRTCPSocket, + totSessionBandwidth, + (unsigned char const*) + fParent.CNAME(), + NULL /* we're a client */, + fRTPSource); + if (fRTCPInstance == NULL) { + env().setResultMsg("Failed to create RTCP instance"); + break; + } + } + + return True; + } while (0); + + delete fRTPSocket; fRTPSocket = NULL; + delete fRTCPSocket; fRTCPSocket = NULL; + Medium::close(fRTCPInstance); fRTCPInstance = NULL; + Medium::close(fReadSource); fReadSource = fRTPSource = NULL; + fClientPortNum = 0; + return False; +} + +void MediaSubsession::deInitiate() { + Medium::close(fRTCPInstance); + fRTCPInstance = NULL; + + Medium::close(fReadSource); // this is assumed to also close fRTPSource + fReadSource = NULL; fRTPSource = NULL; + + delete fRTCPSocket; delete fRTPSocket; + fRTCPSocket = fRTPSocket = NULL; +} + +Boolean MediaSubsession::setClientPortNum(unsigned short portNum) { + if (fReadSource != NULL) { + env().setResultMsg("A read source has already been created"); + return False; + } + + fClientPortNum = portNum; + return True; +} + +netAddressBits MediaSubsession::connectionEndpointAddress() const { + do { + // Get the endpoint name from with us, or our parent session: + char const* endpointString = connectionEndpointName(); + if (endpointString == NULL) { + endpointString = parentSession().connectionEndpointName(); + } + if (endpointString == NULL) break; + + // Now, convert this name to an address, if we can: + NetAddressList addresses(endpointString); + if (addresses.numAddresses() == 0) break; + + return *(netAddressBits*)(addresses.firstAddress()->data()); + } while (0); + + // No address known: + return 0; +} + +void MediaSubsession::setDestinations(netAddressBits defaultDestAddress) { + // Get the destination address from the connection endpoint name + // (This will be 0 if it's not known, in which case we use the default) + netAddressBits destAddress = connectionEndpointAddress(); + if (destAddress == 0) destAddress = defaultDestAddress; + struct in_addr destAddr; destAddr.s_addr = destAddress; + + // The destination TTL remains unchanged: + int destTTL = ~0; // means: don't change + + if (fRTPSocket != NULL) { + Port destPort(serverPortNum); + fRTPSocket->changeDestinationParameters(destAddr, destPort, destTTL); + } + if (fRTCPSocket != NULL && !isSSM()) { + // Note: For SSM sessions, the dest address for RTCP was already set. + Port destPort(serverPortNum+1); + fRTCPSocket-> + changeDestinationParameters(destAddr, destPort, destTTL); + } +} + +double MediaSubsession::getNormalPlayTime(struct timeval const& presentationTime) { + // First, check whether our "RTPSource" object has already been synchronized using RTCP. + // If it hasn't, then - as a special case - we need to use the RTP timestamp to compute the NPT. + if (rtpSource() == NULL || rtpSource()->timestampFrequency() == 0) return 0.0; // no RTP source, or bad freq! + + if (!rtpSource()->hasBeenSynchronizedUsingRTCP()) { + if (!rtpInfo.infoIsNew) return 0.0; // the "rtpInfo" structure has not been filled in + u_int32_t timestampOffset = rtpSource()->curPacketRTPTimestamp() - rtpInfo.timestamp; + double nptOffset = (timestampOffset/(double)(rtpSource()->timestampFrequency()))*scale(); + double npt = playStartTime() + nptOffset; + + return npt; + } else { + // Common case: We have been synchronized using RTCP. This means that the "presentationTime" parameter + // will be accurate, and so we should use this to compute the NPT. + double ptsDouble = (double)(presentationTime.tv_sec + presentationTime.tv_usec/1000000.0); + + if (rtpInfo.infoIsNew) { + // This is the first time we've been called with a synchronized presentation time since the "rtpInfo" + // structure was last filled in. Use this "presentationTime" to compute "fNPT_PTS_Offset": + u_int32_t timestampOffset = rtpSource()->curPacketRTPTimestamp() - rtpInfo.timestamp; + double nptOffset = (timestampOffset/(double)(rtpSource()->timestampFrequency()))*scale(); + double npt = playStartTime() + nptOffset; + fNPT_PTS_Offset = npt - ptsDouble*scale(); + rtpInfo.infoIsNew = False; // for next time + + return npt; + } else { + // Use the precomputed "fNPT_PTS_Offset" to compute the NPT from the PTS: + if (fNPT_PTS_Offset == 0.0) return 0.0; // error: The "rtpInfo" structure was apparently never filled in + return (double)(ptsDouble*scale() + fNPT_PTS_Offset); + } + } +} + +Boolean MediaSubsession::parseSDPLine_c(char const* sdpLine) { + // Check for "c=IN IP4 " + // or "c=IN IP4 /" + // (Later, do something with also #####) + char* connectionEndpointName = parseCLine(sdpLine); + if (connectionEndpointName != NULL) { + delete[] fConnectionEndpointName; + fConnectionEndpointName = connectionEndpointName; + return True; + } + + return False; +} + +Boolean MediaSubsession::parseSDPLine_b(char const* sdpLine) { + // Check for "b=:" line + // RTP applications are expected to use bwtype="AS" + return sscanf(sdpLine, "b=AS:%u", &fBandwidth) == 1; +} + +Boolean MediaSubsession::parseSDPAttribute_rtpmap(char const* sdpLine) { + // Check for a "a=rtpmap: /" line: + // (Also check without the "/"; RealNetworks omits this) + // Also check for a trailing "/". + Boolean parseSuccess = False; + + unsigned rtpmapPayloadFormat; + char* codecName = strDupSize(sdpLine); // ensures we have enough space + unsigned rtpTimestampFrequency = 0; + unsigned numChannels = 1; + if (sscanf(sdpLine, "a=rtpmap: %u %[^/]/%u/%u", + &rtpmapPayloadFormat, codecName, &rtpTimestampFrequency, + &numChannels) == 4 + || sscanf(sdpLine, "a=rtpmap: %u %[^/]/%u", + &rtpmapPayloadFormat, codecName, &rtpTimestampFrequency) == 3 + || sscanf(sdpLine, "a=rtpmap: %u %s", + &rtpmapPayloadFormat, codecName) == 2) { + parseSuccess = True; + if (rtpmapPayloadFormat == fRTPPayloadFormat) { + // This "rtpmap" matches our payload format, so set our + // codec name and timestamp frequency: + // (First, make sure the codec name is upper case) + { + Locale l("POSIX"); + for (char* p = codecName; *p != '\0'; ++p) *p = toupper(*p); + } + delete[] fCodecName; fCodecName = strDup(codecName); + fRTPTimestampFrequency = rtpTimestampFrequency; + fNumChannels = numChannels; + } + } + delete[] codecName; + + return parseSuccess; +} + +Boolean MediaSubsession::parseSDPAttribute_control(char const* sdpLine) { + // Check for a "a=control:" line: + Boolean parseSuccess = False; + + char* controlPath = strDupSize(sdpLine); // ensures we have enough space + if (sscanf(sdpLine, "a=control: %s", controlPath) == 1) { + parseSuccess = True; + delete[] fControlPath; fControlPath = strDup(controlPath); + } + delete[] controlPath; + + return parseSuccess; +} + +Boolean MediaSubsession::parseSDPAttribute_range(char const* sdpLine) { + // Check for a "a=range:npt=-" line: + // (Later handle other kinds of "a=range" attributes also???#####) + Boolean parseSuccess = False; + + double playStartTime; + double playEndTime; + if (parseRangeAttribute(sdpLine, playStartTime, playEndTime)) { + parseSuccess = True; + if (playStartTime > fPlayStartTime) { + fPlayStartTime = playStartTime; + if (playStartTime > fParent.playStartTime()) { + fParent.playStartTime() = playStartTime; + } + } + if (playEndTime > fPlayEndTime) { + fPlayEndTime = playEndTime; + if (playEndTime > fParent.playEndTime()) { + fParent.playEndTime() = playEndTime; + } + } + } + + return parseSuccess; +} + +Boolean MediaSubsession::parseSDPAttribute_fmtp(char const* sdpLine) { + // Check for a "a=fmtp:" line: + // TEMP: We check only for a handful of expected parameter names ##### + // Later: (i) check that payload format number matches; ##### + // (ii) look for other parameters also (generalize?) ##### + do { + if (strncmp(sdpLine, "a=fmtp:", 7) != 0) break; sdpLine += 7; + while (isdigit(*sdpLine)) ++sdpLine; + + // The remaining "sdpLine" should be a sequence of + // =; + // parameter assignments. Look at each of these. + // First, convert the line to lower-case, to ease comparison: + char* const lineCopy = strDup(sdpLine); char* line = lineCopy; + { + Locale l("POSIX"); + for (char* c = line; *c != '\0'; ++c) *c = tolower(*c); + } + while (*line != '\0' && *line != '\r' && *line != '\n') { + unsigned u; + char* valueStr = strDupSize(line); + if (sscanf(line, " auxiliarydatasizelength = %u", &u) == 1) { + fAuxiliarydatasizelength = u; + } else if (sscanf(line, " constantduration = %u", &u) == 1) { + fConstantduration = u; + } else if (sscanf(line, " constantsize; = %u", &u) == 1) { + fConstantsize = u; + } else if (sscanf(line, " crc = %u", &u) == 1) { + fCRC = u; + } else if (sscanf(line, " ctsdeltalength = %u", &u) == 1) { + fCtsdeltalength = u; + } else if (sscanf(line, " de-interleavebuffersize = %u", &u) == 1) { + fDe_interleavebuffersize = u; + } else if (sscanf(line, " dtsdeltalength = %u", &u) == 1) { + fDtsdeltalength = u; + } else if (sscanf(line, " indexdeltalength = %u", &u) == 1) { + fIndexdeltalength = u; + } else if (sscanf(line, " indexlength = %u", &u) == 1) { + fIndexlength = u; + } else if (sscanf(line, " interleaving = %u", &u) == 1) { + fInterleaving = u; + } else if (sscanf(line, " maxdisplacement = %u", &u) == 1) { + fMaxdisplacement = u; + } else if (sscanf(line, " objecttype = %u", &u) == 1) { + fObjecttype = u; + } else if (sscanf(line, " octet-align = %u", &u) == 1) { + fOctetalign = u; + } else if (sscanf(line, " profile-level-id = %x", &u) == 1) { + // Note that the "profile-level-id" parameter is assumed to be hexadecimal + fProfile_level_id = u; + } else if (sscanf(line, " robust-sorting = %u", &u) == 1) { + fRobustsorting = u; + } else if (sscanf(line, " sizelength = %u", &u) == 1) { + fSizelength = u; + } else if (sscanf(line, " streamstateindication = %u", &u) == 1) { + fStreamstateindication = u; + } else if (sscanf(line, " streamtype = %u", &u) == 1) { + fStreamtype = u; + } else if (sscanf(line, " cpresent = %u", &u) == 1) { + fCpresent = u != 0; + } else if (sscanf(line, " randomaccessindication = %u", &u) == 1) { + fRandomaccessindication = u != 0; + } else if (sscanf(line, " config = %[^; \t\r\n]", valueStr) == 1) { + delete[] fConfig; fConfig = strDup(valueStr); + } else if (sscanf(line, " mode = %[^; \t\r\n]", valueStr) == 1) { + delete[] fMode; fMode = strDup(valueStr); + } else if (sscanf(sdpLine, " sprop-parameter-sets = %[^; \t\r\n]", valueStr) == 1) { + // Note: We used "sdpLine" here, because the value is case-sensitive. + delete[] fSpropParameterSets; fSpropParameterSets = strDup(valueStr); + } else { + // Some of the above parameters are Boolean. Check whether the parameter + // names appear alone, without a "= 1" at the end: + if (sscanf(line, " %[^; \t\r\n]", valueStr) == 1) { + if (strcmp(valueStr, "octet-align") == 0) { + fOctetalign = 1; + } else if (strcmp(valueStr, "cpresent") == 0) { + fCpresent = True; + } else if (strcmp(valueStr, "crc") == 0) { + fCRC = 1; + } else if (strcmp(valueStr, "robust-sorting") == 0) { + fRobustsorting = 1; + } else if (strcmp(valueStr, "randomaccessindication") == 0) { + fRandomaccessindication = True; + } + } + } + delete[] valueStr; + + // Move to the next parameter assignment string: + while (*line != '\0' && *line != '\r' && *line != '\n' + && *line != ';') ++line; + while (*line == ';') ++line; + + // Do the same with sdpLine; needed for finding case sensitive values: + while (*sdpLine != '\0' && *sdpLine != '\r' && *sdpLine != '\n' + && *sdpLine != ';') ++sdpLine; + while (*sdpLine == ';') ++sdpLine; + } + delete[] lineCopy; + return True; + } while (0); + + return False; +} + +Boolean MediaSubsession +::parseSDPAttribute_source_filter(char const* sdpLine) { + return parseSourceFilterAttribute(sdpLine, fSourceFilterAddr); +} + +Boolean MediaSubsession::parseSDPAttribute_x_dimensions(char const* sdpLine) { + // Check for a "a=x-dimensions:," line: + Boolean parseSuccess = False; + + int width, height; + if (sscanf(sdpLine, "a=x-dimensions:%d,%d", &width, &height) == 2) { + parseSuccess = True; + fVideoWidth = (unsigned short)width; + fVideoHeight = (unsigned short)height; + } + + return parseSuccess; +} + +Boolean MediaSubsession::parseSDPAttribute_framerate(char const* sdpLine) { + // Check for a "a=framerate: " or "a=x-framerate: " line: + Boolean parseSuccess = False; + + float frate; + int rate; + if (sscanf(sdpLine, "a=framerate: %f", &frate) == 1 || sscanf(sdpLine, "a=framerate:%f", &frate) == 1) { + parseSuccess = True; + fVideoFPS = (unsigned)frate; + } else if (sscanf(sdpLine, "a=x-framerate: %d", &rate) == 1) { + parseSuccess = True; + fVideoFPS = (unsigned)rate; + } + + return parseSuccess; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MediaSink.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MediaSink.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MediaSink.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MediaSink.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,222 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Media Sinks +// Implementation + +#include "MediaSink.hh" +#include "GroupsockHelper.hh" +#include + +////////// MediaSink ////////// + +MediaSink::MediaSink(UsageEnvironment& env) + : Medium(env), fSource(NULL), + fAfterFunc(NULL), fAfterClientData(NULL) +{ +} + +MediaSink::~MediaSink() { + stopPlaying(); +} + +Boolean MediaSink::isSink() const { + return True; +} + +Boolean MediaSink::lookupByName(UsageEnvironment& env, char const* sinkName, + MediaSink*& resultSink) { + resultSink = NULL; // unless we succeed + + Medium* medium; + if (!Medium::lookupByName(env, sinkName, medium)) return False; + + if (!medium->isSink()) { + env.setResultMsg(sinkName, " is not a media sink"); + return False; + } + + resultSink = (MediaSink*)medium; + return True; +} + +Boolean MediaSink::sourceIsCompatibleWithUs(MediaSource& source) { + // We currently support only framed sources. + return source.isFramedSource(); +} + +Boolean MediaSink::startPlaying(MediaSource& source, + afterPlayingFunc* afterFunc, + void* afterClientData) { + // Make sure we're not already being played: + if (fSource != NULL) { + envir().setResultMsg("This sink is already being played"); + return False; + } + + // Make sure our source is compatible: + if (!sourceIsCompatibleWithUs(source)) { + envir().setResultMsg("MediaSink::startPlaying(): source is not compatible!"); + return False; + } + fSource = (FramedSource*)&source; + + fAfterFunc = afterFunc; + fAfterClientData = afterClientData; + return continuePlaying(); +} + +void MediaSink::stopPlaying() { + // First, tell the source that we're no longer interested: + if (fSource != NULL) fSource->stopGettingFrames(); + + // Cancel any pending tasks: + envir().taskScheduler().unscheduleDelayedTask(nextTask()); + nextTask() = NULL; + + fSource = NULL; // indicates that we can be played again + fAfterFunc = NULL; +} + +void MediaSink::onSourceClosure(void* clientData) { + MediaSink* sink = (MediaSink*)clientData; + sink->fSource = NULL; // indicates that we can be played again + if (sink->fAfterFunc != NULL) { + (*(sink->fAfterFunc))(sink->fAfterClientData); + } +} + +Boolean MediaSink::isRTPSink() const { + return False; // default implementation +} + +////////// OutPacketBuffer ////////// + +unsigned OutPacketBuffer::maxSize = 60000; // by default + +OutPacketBuffer::OutPacketBuffer(unsigned preferredPacketSize, + unsigned maxPacketSize) + : fPreferred(preferredPacketSize), fMax(maxPacketSize), + fOverflowDataSize(0), + fOverflowDurationInMicroseconds(0) +{ + unsigned maxNumPackets = (maxSize + (maxPacketSize-1))/maxPacketSize; + fLimit = maxNumPackets*maxPacketSize; + fBuf = new unsigned char[fLimit]; + resetPacketStart(); + resetOffset(); + resetOverflowData(); +} + +OutPacketBuffer::~OutPacketBuffer() { + delete[] fBuf; +} + +void OutPacketBuffer::enqueue(unsigned char const* from, unsigned numBytes) { + if (numBytes > totalBytesAvailable()) { +#ifdef DEBUG + fprintf(stderr, "OutPacketBuffer::enqueue() warning: %d > %d\n", numBytes, totalBytesAvailable()); +#endif + numBytes = totalBytesAvailable(); + } + + if (curPtr() != from) memmove(curPtr(), from, numBytes); + increment(numBytes); +} + +void OutPacketBuffer::enqueueWord(u_int32_t word) { + u_int32_t nWord = htonl(word); + enqueue((unsigned char*)&nWord, 4); +} + +void OutPacketBuffer::insert(unsigned char const* from, unsigned numBytes, + unsigned toPosition) { + unsigned realToPosition = fPacketStart + toPosition; + if (realToPosition + numBytes > fLimit) { + if (realToPosition > fLimit) return; // we can't do this + numBytes = fLimit - realToPosition; + } + + memmove(&fBuf[realToPosition], from, numBytes); + if (toPosition + numBytes > fCurOffset) { + fCurOffset = toPosition + numBytes; + } +} + +void OutPacketBuffer::insertWord(u_int32_t word, unsigned toPosition) { + u_int32_t nWord = htonl(word); + insert((unsigned char*)&nWord, 4, toPosition); +} + +void OutPacketBuffer::extract(unsigned char* to, unsigned numBytes, + unsigned fromPosition) { + unsigned realFromPosition = fPacketStart + fromPosition; + if (realFromPosition + numBytes > fLimit) { // sanity check + if (realFromPosition > fLimit) return; // we can't do this + numBytes = fLimit - realFromPosition; + } + + memmove(to, &fBuf[realFromPosition], numBytes); +} + +u_int32_t OutPacketBuffer::extractWord(unsigned fromPosition) { + u_int32_t nWord; + extract((unsigned char*)&nWord, 4, fromPosition); + return ntohl(nWord); +} + +void OutPacketBuffer::skipBytes(unsigned numBytes) { + if (numBytes > totalBytesAvailable()) { + numBytes = totalBytesAvailable(); + } + + increment(numBytes); +} + +void OutPacketBuffer +::setOverflowData(unsigned overflowDataOffset, + unsigned overflowDataSize, + struct timeval const& presentationTime, + unsigned durationInMicroseconds) { + fOverflowDataOffset = overflowDataOffset; + fOverflowDataSize = overflowDataSize; + fOverflowPresentationTime = presentationTime; + fOverflowDurationInMicroseconds = durationInMicroseconds; +} + +void OutPacketBuffer::useOverflowData() { + enqueue(&fBuf[fPacketStart + fOverflowDataOffset], fOverflowDataSize); + fCurOffset -= fOverflowDataSize; // undoes increment performed by "enqueue" + resetOverflowData(); +} + +void OutPacketBuffer::adjustPacketStart(unsigned numBytes) { + fPacketStart += numBytes; + if (fOverflowDataOffset >= numBytes) { + fOverflowDataOffset -= numBytes; + } else { + fOverflowDataOffset = 0; + fOverflowDataSize = 0; // an error otherwise + } +} + +void OutPacketBuffer::resetPacketStart() { + if (fOverflowDataSize > 0) { + fOverflowDataOffset += fPacketStart; + } + fPacketStart = 0; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MediaSource.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MediaSource.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MediaSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MediaSource.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,88 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Media Sources +// Implementation + +#include "MediaSource.hh" + +////////// MediaSource ////////// + +MediaSource::MediaSource(UsageEnvironment& env) + : Medium(env) { +} + +MediaSource::~MediaSource() { +} + +Boolean MediaSource::isSource() const { + return True; +} + +char const* MediaSource::MIMEtype() const { + return "application/OCTET-STREAM"; // default type +} + +Boolean MediaSource::isFramedSource() const { + return False; // default implementation +} +Boolean MediaSource::isRTPSource() const { + return False; // default implementation +} +Boolean MediaSource::isMPEG1or2VideoStreamFramer() const { + return False; // default implementation +} +Boolean MediaSource::isMPEG4VideoStreamFramer() const { + return False; // default implementation +} +Boolean MediaSource::isH264VideoStreamFramer() const { + return False; // default implementation +} +Boolean MediaSource::isH265VideoStreamFramer() const { + return False; // default implementation +} +Boolean MediaSource::isDVVideoStreamFramer() const { + return False; // default implementation +} +Boolean MediaSource::isJPEGVideoSource() const { + return False; // default implementation +} +Boolean MediaSource::isAMRAudioSource() const { + return False; // default implementation +} + +Boolean MediaSource::lookupByName(UsageEnvironment& env, + char const* sourceName, + MediaSource*& resultSource) { + resultSource = NULL; // unless we succeed + + Medium* medium; + if (!Medium::lookupByName(env, sourceName, medium)) return False; + + if (!medium->isSource()) { + env.setResultMsg(sourceName, " is not a media source"); + return False; + } + + resultSource = (MediaSource*)medium; + return True; +} + +void MediaSource::getAttributes() const { + // Default implementation + envir().setResultMsg(""); +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MPEG2TransportStreamFramer.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MPEG2TransportStreamFramer.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MPEG2TransportStreamFramer.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MPEG2TransportStreamFramer.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,238 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// A filter that passes through (unchanged) chunks that contain an integral number +// of MPEG-2 Transport Stream packets, but returning (in "fDurationInMicroseconds") +// an updated estimate of the time gap between chunks. +// Implementation + +#include "MPEG2TransportStreamFramer.hh" +#include // for "gettimeofday()" + +#define TRANSPORT_PACKET_SIZE 188 +#define NEW_DURATION_WEIGHT 0.5 + // How much weight to give to the latest duration measurement (must be <= 1) +#define TIME_ADJUSTMENT_FACTOR 0.8 + // A factor by which to adjust the duration estimate to ensure that the overall + // packet transmission times remains matched with the PCR times (which will be the + // times that we expect receivers to play the incoming packets). + // (must be <= 1) +#define MAX_PLAYOUT_BUFFER_DURATION 0.1 // (seconds) +#define PCR_PERIOD_VARIATION_RATIO 0.5 + +////////// PIDStatus ////////// + +class PIDStatus { +public: + PIDStatus(double _firstClock, double _firstRealTime) + : firstClock(_firstClock), lastClock(_firstClock), + firstRealTime(_firstRealTime), lastRealTime(_firstRealTime), + lastPacketNum(0) { + } + + double firstClock, lastClock, firstRealTime, lastRealTime; + unsigned lastPacketNum; +}; + + +////////// MPEG2TransportStreamFramer ////////// + +MPEG2TransportStreamFramer* MPEG2TransportStreamFramer +::createNew(UsageEnvironment& env, FramedSource* inputSource) { + return new MPEG2TransportStreamFramer(env, inputSource); +} + +MPEG2TransportStreamFramer +::MPEG2TransportStreamFramer(UsageEnvironment& env, FramedSource* inputSource) + : FramedFilter(env, inputSource), + fTSPacketCount(0), fTSPacketDurationEstimate(0.0), fTSPCRCount(0) { + fPIDStatusTable = HashTable::create(ONE_WORD_HASH_KEYS); +} + +MPEG2TransportStreamFramer::~MPEG2TransportStreamFramer() { + clearPIDStatusTable(); + delete fPIDStatusTable; +} + +void MPEG2TransportStreamFramer::clearPIDStatusTable() { + PIDStatus* pidStatus; + while ((pidStatus = (PIDStatus*)fPIDStatusTable->RemoveNext()) != NULL) { + delete pidStatus; + } +} + +void MPEG2TransportStreamFramer::doGetNextFrame() { + // Read directly from our input source into our client's buffer: + fFrameSize = 0; + fInputSource->getNextFrame(fTo, fMaxSize, + afterGettingFrame, this, + FramedSource::handleClosure, this); +} + +void MPEG2TransportStreamFramer::doStopGettingFrames() { + FramedFilter::doStopGettingFrames(); + fTSPacketCount = 0; + fTSPCRCount = 0; + + clearPIDStatusTable(); +} + +void MPEG2TransportStreamFramer +::afterGettingFrame(void* clientData, unsigned frameSize, + unsigned /*numTruncatedBytes*/, + struct timeval presentationTime, + unsigned /*durationInMicroseconds*/) { + MPEG2TransportStreamFramer* framer = (MPEG2TransportStreamFramer*)clientData; + framer->afterGettingFrame1(frameSize, presentationTime); +} + +#define TRANSPORT_SYNC_BYTE 0x47 + +void MPEG2TransportStreamFramer::afterGettingFrame1(unsigned frameSize, + struct timeval presentationTime) { + fFrameSize += frameSize; + unsigned const numTSPackets = fFrameSize/TRANSPORT_PACKET_SIZE; + fFrameSize = numTSPackets*TRANSPORT_PACKET_SIZE; // an integral # of TS packets + if (fFrameSize == 0) { + // We didn't read a complete TS packet; assume that the input source has closed. + handleClosure(this); + return; + } + + // Make sure the data begins with a sync byte: + unsigned syncBytePosition; + for (syncBytePosition = 0; syncBytePosition < fFrameSize; ++syncBytePosition) { + if (fTo[syncBytePosition] == TRANSPORT_SYNC_BYTE) break; + } + if (syncBytePosition == fFrameSize) { + envir() << "No Transport Stream sync byte in data."; + handleClosure(this); + return; + } else if (syncBytePosition > 0) { + // There's a sync byte, but not at the start of the data. Move the good data + // to the start of the buffer, then read more to fill it up again: + memmove(fTo, &fTo[syncBytePosition], fFrameSize - syncBytePosition); + fFrameSize -= syncBytePosition; + fInputSource->getNextFrame(&fTo[fFrameSize], syncBytePosition, + afterGettingFrame, this, + FramedSource::handleClosure, this); + return; + } // else normal case: the data begins with a sync byte + + fPresentationTime = presentationTime; + + // Scan through the TS packets that we read, and update our estimate of + // the duration of each packet: + struct timeval tvNow; + gettimeofday(&tvNow, NULL); + double timeNow = tvNow.tv_sec + tvNow.tv_usec/1000000.0; + for (unsigned i = 0; i < numTSPackets; ++i) { + updateTSPacketDurationEstimate(&fTo[i*TRANSPORT_PACKET_SIZE], timeNow); + } + + fDurationInMicroseconds + = numTSPackets * (unsigned)(fTSPacketDurationEstimate*1000000); + + // Complete the delivery to our client: + afterGetting(this); +} + +void MPEG2TransportStreamFramer +::updateTSPacketDurationEstimate(unsigned char* pkt, double timeNow) { + // Sanity check: Make sure we start with the sync byte: + if (pkt[0] != TRANSPORT_SYNC_BYTE) { + envir() << "Missing sync byte!\n"; + return; + } + + ++fTSPacketCount; + + // If this packet doesn't contain a PCR, then we're not interested in it: + u_int8_t const adaptation_field_control = (pkt[3]&0x30)>>4; + if (adaptation_field_control != 2 && adaptation_field_control != 3) return; + // there's no adaptation_field + + u_int8_t const adaptation_field_length = pkt[4]; + if (adaptation_field_length == 0) return; + + u_int8_t const discontinuity_indicator = pkt[5]&0x80; + u_int8_t const pcrFlag = pkt[5]&0x10; + if (pcrFlag == 0) return; // no PCR + + // There's a PCR. Get it, and the PID: + ++fTSPCRCount; + u_int32_t pcrBaseHigh = (pkt[6]<<24)|(pkt[7]<<16)|(pkt[8]<<8)|pkt[9]; + double clock = pcrBaseHigh/45000.0; + if ((pkt[10]&0x80) != 0) clock += 1/90000.0; // add in low-bit (if set) + unsigned short pcrExt = ((pkt[10]&0x01)<<8) | pkt[11]; + clock += pcrExt/27000000.0; + + unsigned pid = ((pkt[1]&0x1F)<<8) | pkt[2]; + + // Check whether we already have a record of a PCR for this PID: + PIDStatus* pidStatus = (PIDStatus*)(fPIDStatusTable->Lookup((char*)pid)); + + if (pidStatus == NULL) { + // We're seeing this PID's PCR for the first time: + pidStatus = new PIDStatus(clock, timeNow); + fPIDStatusTable->Add((char*)pid, pidStatus); +#ifdef DEBUG_PCR + fprintf(stderr, "PID 0x%x, FIRST PCR 0x%08x+%d:%03x == %f @ %f, pkt #%lu\n", pid, pcrBaseHigh, pkt[10]>>7, pcrExt, clock, timeNow, fTSPacketCount); +#endif + } else { + // We've seen this PID's PCR before; update our per-packet duration estimate: + double durationPerPacket + = (clock - pidStatus->lastClock)/(fTSPacketCount - pidStatus->lastPacketNum); + + // Hack (suggested by "Romain"): Don't update our estimate if this PCR appeared unusually quickly. + // (This can produce more accurate estimates for wildly VBR streams.) + if (fTSPCRCount > 0) { + double meanPCRPeriod=(double)fTSPacketCount/fTSPCRCount; + if (fTSPacketCount - pidStatus->lastPacketNum < meanPCRPeriod*PCR_PERIOD_VARIATION_RATIO) return; + } + + if (fTSPacketDurationEstimate == 0.0) { // we've just started + fTSPacketDurationEstimate = durationPerPacket; + } else if (discontinuity_indicator == 0 && durationPerPacket >= 0.0) { + fTSPacketDurationEstimate + = durationPerPacket*NEW_DURATION_WEIGHT + + fTSPacketDurationEstimate*(1-NEW_DURATION_WEIGHT); + + // Also adjust the duration estimate to try to ensure that the transmission + // rate matches the playout rate: + double transmitDuration = timeNow - pidStatus->firstRealTime; + double playoutDuration = clock - pidStatus->firstClock; + if (transmitDuration > playoutDuration) { + fTSPacketDurationEstimate *= TIME_ADJUSTMENT_FACTOR; // reduce estimate + } else if (transmitDuration + MAX_PLAYOUT_BUFFER_DURATION < playoutDuration) { + fTSPacketDurationEstimate /= TIME_ADJUSTMENT_FACTOR; // increase estimate + } + } else { + // the PCR has a discontinuity from its previous value; don't use it now, + // but reset our PCR and real-time values to compensate: + pidStatus->firstClock = clock; + pidStatus->firstRealTime = timeNow; + } +#ifdef DEBUG_PCR + fprintf(stderr, "PID 0x%x, PCR 0x%08x+%d:%03x == %f @ %f (diffs %f @ %f), pkt #%lu, discon %d => this duration %f, new estimate %f, mean PCR period=%f\n", pid, pcrBaseHigh, pkt[10]>>7, pcrExt, clock, timeNow, clock - pidStatus->firstClock, timeNow - pidStatus->firstRealTime, fTSPacketCount, discontinuity_indicator != 0, durationPerPacket, fTSPacketDurationEstimate, meanPCRPeriod ); +#endif + } + + pidStatus->lastClock = clock; + pidStatus->lastRealTime = timeNow; + pidStatus->lastPacketNum = fTSPacketCount; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MultiFramedRTPSink.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MultiFramedRTPSink.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MultiFramedRTPSink.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MultiFramedRTPSink.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,423 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTP sink for a common kind of payload format: Those which pack multiple, +// complete codec frames (as many as possible) into each RTP packet. +// Implementation + +#include "MultiFramedRTPSink.hh" +#include "GroupsockHelper.hh" + +////////// MultiFramedRTPSink ////////// + +void MultiFramedRTPSink::setPacketSizes(unsigned preferredPacketSize, + unsigned maxPacketSize) { + if (preferredPacketSize > maxPacketSize || preferredPacketSize == 0) return; + // sanity check + + delete fOutBuf; + fOutBuf = new OutPacketBuffer(preferredPacketSize, maxPacketSize); + fOurMaxPacketSize = maxPacketSize; // save value, in case subclasses need it +} + +MultiFramedRTPSink::MultiFramedRTPSink(UsageEnvironment& env, + Groupsock* rtpGS, + unsigned char rtpPayloadType, + unsigned rtpTimestampFrequency, + char const* rtpPayloadFormatName, + unsigned numChannels) + : RTPSink(env, rtpGS, rtpPayloadType, rtpTimestampFrequency, + rtpPayloadFormatName, numChannels), + fOutBuf(NULL), fCurFragmentationOffset(0), fPreviousFrameEndedFragmentation(False), + fNoFramesLeft(False), fNumFramesUsedSoFar(0), fIsFirstPacket(True), fTimestampPosition(0), + fSpecialHeaderPosition(0), fSpecialHeaderSize(0), fCurFrameSpecificHeaderPosition(0), + fCurFrameSpecificHeaderSize(0), fTotalFrameSpecificHeaderSizes(0) +{ + memset(&fNextSendTime, 0, sizeof(fNextSendTime)); + setPacketSizes(1000, 1448); + // Default max packet size (1500, minus allowance for IP, UDP, UMTP headers) + // (Also, make it a multiple of 4 bytes, just in case that matters.) +} + +MultiFramedRTPSink::~MultiFramedRTPSink() { + delete fOutBuf; +} + +void MultiFramedRTPSink +::doSpecialFrameHandling(unsigned /*fragmentationOffset*/, + unsigned char* /*frameStart*/, + unsigned /*numBytesInFrame*/, + struct timeval frameTimestamp, + unsigned /*numRemainingBytes*/) { + // default implementation: If this is the first frame in the packet, + // use its timestamp for the RTP timestamp: + if (isFirstFrameInPacket()) { + setTimestamp(frameTimestamp); + } +} + +Boolean MultiFramedRTPSink::allowFragmentationAfterStart() const { + return False; // by default +} + +Boolean MultiFramedRTPSink::allowOtherFramesAfterLastFragment() const { + return False; // by default +} + +Boolean MultiFramedRTPSink +::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/, + unsigned /*numBytesInFrame*/) const { + return True; // by default +} + +unsigned MultiFramedRTPSink::specialHeaderSize() const { + // default implementation: Assume no special header: + return 0; +} + +unsigned MultiFramedRTPSink::frameSpecificHeaderSize() const { + // default implementation: Assume no frame-specific header: + return 0; +} + +unsigned MultiFramedRTPSink::computeOverflowForNewFrame(unsigned newFrameSize) const { + // default implementation: Just call numOverflowBytes() + return fOutBuf->numOverflowBytes(newFrameSize); +} + +void MultiFramedRTPSink::setMarkerBit() { + unsigned rtpHdr = fOutBuf->extractWord(0); + rtpHdr |= 0x00800000; + fOutBuf->insertWord(rtpHdr, 0); +} + +void MultiFramedRTPSink::setTimestamp(struct timeval timestamp) { + // First, convert the timestamp to a 32-bit RTP timestamp: + fCurrentTimestamp = convertToRTPTimestamp(timestamp); + + // Then, insert it into the RTP packet: + fOutBuf->insertWord(fCurrentTimestamp, fTimestampPosition); +} + +void MultiFramedRTPSink::setSpecialHeaderWord(unsigned word, + unsigned wordPosition) { + fOutBuf->insertWord(word, fSpecialHeaderPosition + 4*wordPosition); +} + +void MultiFramedRTPSink::setSpecialHeaderBytes(unsigned char const* bytes, + unsigned numBytes, + unsigned bytePosition) { + fOutBuf->insert(bytes, numBytes, fSpecialHeaderPosition + bytePosition); +} + +void MultiFramedRTPSink::setFrameSpecificHeaderWord(unsigned word, + unsigned wordPosition) { + fOutBuf->insertWord(word, fCurFrameSpecificHeaderPosition + 4*wordPosition); +} + +void MultiFramedRTPSink::setFrameSpecificHeaderBytes(unsigned char const* bytes, + unsigned numBytes, + unsigned bytePosition) { + fOutBuf->insert(bytes, numBytes, fCurFrameSpecificHeaderPosition + bytePosition); +} + +void MultiFramedRTPSink::setFramePadding(unsigned numPaddingBytes) { + if (numPaddingBytes > 0) { + // Add the padding bytes (with the last one being the padding size): + unsigned char paddingBuffer[255]; //max padding + memset(paddingBuffer, 0, numPaddingBytes); + paddingBuffer[numPaddingBytes-1] = numPaddingBytes; + fOutBuf->enqueue(paddingBuffer, numPaddingBytes); + + // Set the RTP padding bit: + unsigned rtpHdr = fOutBuf->extractWord(0); + rtpHdr |= 0x20000000; + fOutBuf->insertWord(rtpHdr, 0); + } +} + +Boolean MultiFramedRTPSink::continuePlaying() { + // Send the first packet. + // (This will also schedule any future sends.) + buildAndSendPacket(True); + return True; +} + +void MultiFramedRTPSink::stopPlaying() { + fOutBuf->resetPacketStart(); + fOutBuf->resetOffset(); + fOutBuf->resetOverflowData(); + + // Then call the default "stopPlaying()" function: + MediaSink::stopPlaying(); +} + +void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket) { + fIsFirstPacket = isFirstPacket; + + // Set up the RTP header: + unsigned rtpHdr = 0x80000000; // RTP version 2 + rtpHdr |= (fRTPPayloadType<<16); + rtpHdr |= fSeqNo; // sequence number + fOutBuf->enqueueWord(rtpHdr); + + // Note where the RTP timestamp will go. + // (We can't fill this in until we start packing payload frames.) + fTimestampPosition = fOutBuf->curPacketSize(); + fOutBuf->skipBytes(4); // leave a hole for the timestamp + + fOutBuf->enqueueWord(SSRC()); + + // Allow for a special, payload-format-specific header following the + // RTP header: + fSpecialHeaderPosition = fOutBuf->curPacketSize(); + fSpecialHeaderSize = specialHeaderSize(); + fOutBuf->skipBytes(fSpecialHeaderSize); + + // Begin packing as many (complete) frames into the packet as we can: + fTotalFrameSpecificHeaderSizes = 0; + fNoFramesLeft = False; + fNumFramesUsedSoFar = 0; + packFrame(); +} + +void MultiFramedRTPSink::packFrame() { + // Get the next frame. + + // First, see if we have an overflow frame that was too big for the last pkt + if (fOutBuf->haveOverflowData()) { + // Use this frame before reading a new one from the source + unsigned frameSize = fOutBuf->overflowDataSize(); + struct timeval presentationTime = fOutBuf->overflowPresentationTime(); + unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds(); + fOutBuf->useOverflowData(); + + afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds); + } else { + // Normal case: we need to read a new frame from the source + if (fSource == NULL) return; + + fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize(); + fCurFrameSpecificHeaderSize = frameSpecificHeaderSize(); + fOutBuf->skipBytes(fCurFrameSpecificHeaderSize); + fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize; + + fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(), + afterGettingFrame, this, ourHandleClosure, this); + } +} + +void MultiFramedRTPSink +::afterGettingFrame(void* clientData, unsigned numBytesRead, + unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds) { + MultiFramedRTPSink* sink = (MultiFramedRTPSink*)clientData; + sink->afterGettingFrame1(numBytesRead, numTruncatedBytes, + presentationTime, durationInMicroseconds); +} + +void MultiFramedRTPSink +::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds) { + if (fIsFirstPacket) { + // Record the fact that we're starting to play now: + gettimeofday(&fNextSendTime, NULL); + } + + if (numTruncatedBytes > 0) { + unsigned const bufferSize = fOutBuf->totalBytesAvailable(); + unsigned newMaxSize = frameSize + numTruncatedBytes; + envir() << "MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size (" + << bufferSize << "). " + << numTruncatedBytes << " bytes of trailing data was dropped! Correct this by increasing \"OutPacketBuffer::maxSize\" to at least " + << newMaxSize << ", *before* creating this 'RTPSink'. (Current value is " + << OutPacketBuffer::maxSize << ".)\n"; + } + unsigned curFragmentationOffset = fCurFragmentationOffset; + unsigned numFrameBytesToUse = frameSize; + unsigned overflowBytes = 0; + + // If we have already packed one or more frames into this packet, + // check whether this new frame is eligible to be packed after them. + // (This is independent of whether the packet has enough room for this + // new frame; that check comes later.) + if (fNumFramesUsedSoFar > 0) { + if ((fPreviousFrameEndedFragmentation + && !allowOtherFramesAfterLastFragment()) + || !frameCanAppearAfterPacketStart(fOutBuf->curPtr(), frameSize)) { + // Save away this frame for next time: + numFrameBytesToUse = 0; + fOutBuf->setOverflowData(fOutBuf->curPacketSize(), frameSize, + presentationTime, durationInMicroseconds); + } + } + fPreviousFrameEndedFragmentation = False; + + if (numFrameBytesToUse > 0) { + // Check whether this frame overflows the packet + if (fOutBuf->wouldOverflow(frameSize)) { + // Don't use this frame now; instead, save it as overflow data, and + // send it in the next packet instead. However, if the frame is too + // big to fit in a packet by itself, then we need to fragment it (and + // use some of it in this packet, if the payload format permits this.) + if (isTooBigForAPacket(frameSize) + && (fNumFramesUsedSoFar == 0 || allowFragmentationAfterStart())) { + // We need to fragment this frame, and use some of it now: + overflowBytes = computeOverflowForNewFrame(frameSize); + numFrameBytesToUse -= overflowBytes; + fCurFragmentationOffset += numFrameBytesToUse; + } else { + // We don't use any of this frame now: + overflowBytes = frameSize; + numFrameBytesToUse = 0; + } + fOutBuf->setOverflowData(fOutBuf->curPacketSize() + numFrameBytesToUse, + overflowBytes, presentationTime, durationInMicroseconds); + } else if (fCurFragmentationOffset > 0) { + // This is the last fragment of a frame that was fragmented over + // more than one packet. Do any special handling for this case: + fCurFragmentationOffset = 0; + fPreviousFrameEndedFragmentation = True; + } + } + + if (numFrameBytesToUse == 0) { + // Send our packet now, because we have filled it up: + sendPacketIfNecessary(); + } else { + // Use this frame in our outgoing packet: + unsigned char* frameStart = fOutBuf->curPtr(); + fOutBuf->increment(numFrameBytesToUse); + // do this now, in case "doSpecialFrameHandling()" calls "setFramePadding()" to append padding bytes + + // Here's where any payload format specific processing gets done: + doSpecialFrameHandling(curFragmentationOffset, frameStart, + numFrameBytesToUse, presentationTime, + overflowBytes); + + ++fNumFramesUsedSoFar; + + // Update the time at which the next packet should be sent, based + // on the duration of the frame that we just packed into it. + // However, if this frame has overflow data remaining, then don't + // count its duration yet. + if (overflowBytes == 0) { + fNextSendTime.tv_usec += durationInMicroseconds; + fNextSendTime.tv_sec += fNextSendTime.tv_usec/1000000; + fNextSendTime.tv_usec %= 1000000; + } + + // Send our packet now if (i) it's already at our preferred size, or + // (ii) (heuristic) another frame of the same size as the one we just + // read would overflow the packet, or + // (iii) it contains the last fragment of a fragmented frame, and we + // don't allow anything else to follow this or + // (iv) one frame per packet is allowed: + if (fOutBuf->isPreferredSize() + || fOutBuf->wouldOverflow(numFrameBytesToUse) + || (fPreviousFrameEndedFragmentation && + !allowOtherFramesAfterLastFragment()) + || !frameCanAppearAfterPacketStart(fOutBuf->curPtr() - frameSize, + frameSize) ) { + // The packet is ready to be sent now + sendPacketIfNecessary(); + } else { + // There's room for more frames; try getting another: + packFrame(); + } + } +} + +static unsigned const rtpHeaderSize = 12; + +Boolean MultiFramedRTPSink::isTooBigForAPacket(unsigned numBytes) const { + // Check whether a 'numBytes'-byte frame - together with a RTP header and + // (possible) special headers - would be too big for an output packet: + // (Later allow for RTP extension header!) ##### + numBytes += rtpHeaderSize + specialHeaderSize() + frameSpecificHeaderSize(); + return fOutBuf->isTooBigForAPacket(numBytes); +} + +void MultiFramedRTPSink::sendPacketIfNecessary() { + if (fNumFramesUsedSoFar > 0) { + // Send the packet: +#ifdef TEST_LOSS + if ((our_random()%10) != 0) // simulate 10% packet loss ##### +#endif + fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize()); + ++fPacketCount; + fTotalOctetCount += fOutBuf->curPacketSize(); + fOctetCount += fOutBuf->curPacketSize() + - rtpHeaderSize - fSpecialHeaderSize - fTotalFrameSpecificHeaderSizes; + + ++fSeqNo; // for next time + } + + if (fOutBuf->haveOverflowData() + && fOutBuf->totalBytesAvailable() > fOutBuf->totalBufferSize()/2) { + // Efficiency hack: Reset the packet start pointer to just in front of + // the overflow data (allowing for the RTP header and special headers), + // so that we probably don't have to "memmove()" the overflow data + // into place when building the next packet: + unsigned newPacketStart = fOutBuf->curPacketSize() + - (rtpHeaderSize + fSpecialHeaderSize + frameSpecificHeaderSize()); + fOutBuf->adjustPacketStart(newPacketStart); + } else { + // Normal case: Reset the packet start pointer back to the start: + fOutBuf->resetPacketStart(); + } + fOutBuf->resetOffset(); + fNumFramesUsedSoFar = 0; + + if (fNoFramesLeft) { + // We're done: + onSourceClosure(this); + } else { + // We have more frames left to send. Figure out when the next frame + // is due to start playing, then make sure that we wait this long before + // sending the next packet. + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + int uSecondsToGo; + if (fNextSendTime.tv_sec < timeNow.tv_sec + || (fNextSendTime.tv_sec == timeNow.tv_sec && fNextSendTime.tv_usec < timeNow.tv_usec)) { + uSecondsToGo = 0; // prevents integer underflow if too far behind + } else { + uSecondsToGo = (fNextSendTime.tv_sec - timeNow.tv_sec)*1000000 + (fNextSendTime.tv_usec - timeNow.tv_usec); + } + + // Delay this amount of time: + nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, + (TaskFunc*)sendNext, this); + } +} + +// The following is called after each delay between packet sends: +void MultiFramedRTPSink::sendNext(void* firstArg) { + MultiFramedRTPSink* sink = (MultiFramedRTPSink*)firstArg; + sink->buildAndSendPacket(False); +} + +void MultiFramedRTPSink::ourHandleClosure(void* clientData) { + MultiFramedRTPSink* sink = (MultiFramedRTPSink*)clientData; + // There are no frames left, but we may have a partially built packet + // to send + sink->fNoFramesLeft = True; + sink->sendPacketIfNecessary(); +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MultiFramedRTPSource.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MultiFramedRTPSource.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/MultiFramedRTPSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/MultiFramedRTPSource.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,564 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTP source for a common kind of payload format: Those that pack multiple, +// complete codec frames (as many as possible) into each RTP packet. +// Implementation + +#include "MultiFramedRTPSource.hh" +#include "GroupsockHelper.hh" +#include + +////////// ReorderingPacketBuffer definition ////////// + +class ReorderingPacketBuffer { +public: + ReorderingPacketBuffer(BufferedPacketFactory* packetFactory); + virtual ~ReorderingPacketBuffer(); + void reset(); + + BufferedPacket* getFreePacket(MultiFramedRTPSource* ourSource); + Boolean storePacket(BufferedPacket* bPacket); + BufferedPacket* getNextCompletedPacket(Boolean& packetLossPreceded); + void releaseUsedPacket(BufferedPacket* packet); + void freePacket(BufferedPacket* packet) { + if (packet != fSavedPacket) { + delete packet; + } else { + fSavedPacketFree = True; + } + } + Boolean isEmpty() const { return fHeadPacket == NULL; } + + void setThresholdTime(unsigned uSeconds) { fThresholdTime = uSeconds; } + +private: + BufferedPacketFactory* fPacketFactory; + unsigned fThresholdTime; // uSeconds + Boolean fHaveSeenFirstPacket; // used to set initial "fNextExpectedSeqNo" + unsigned short fNextExpectedSeqNo; + BufferedPacket* fHeadPacket; + BufferedPacket* fSavedPacket; + // to avoid calling new/free in the common case + Boolean fSavedPacketFree; +}; + + +////////// MultiFramedRTPSource implementation ////////// + +MultiFramedRTPSource +::MultiFramedRTPSource(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + BufferedPacketFactory* packetFactory) + : RTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency) { + reset(); + fReorderingBuffer = new ReorderingPacketBuffer(packetFactory); + + // Try to use a big receive buffer for RTP: + increaseReceiveBufferTo(env, RTPgs->socketNum(), 50*1024); +} + +void MultiFramedRTPSource::reset() { + fCurrentPacketBeginsFrame = True; // by default + fCurrentPacketCompletesFrame = True; // by default + fAreDoingNetworkReads = False; + fNeedDelivery = False; + fPacketLossInFragmentedFrame = False; + fSavedTo = NULL; + fSavedMaxSize = 0; +} + +MultiFramedRTPSource::~MultiFramedRTPSource() { + fRTPInterface.stopNetworkReading(); + delete fReorderingBuffer; +} + +Boolean MultiFramedRTPSource +::processSpecialHeader(BufferedPacket* /*packet*/, + unsigned& resultSpecialHeaderSize) { + // Default implementation: Assume no special header: + resultSpecialHeaderSize = 0; + return True; +} + +Boolean MultiFramedRTPSource +::packetIsUsableInJitterCalculation(unsigned char* /*packet*/, + unsigned /*packetSize*/) { + // Default implementation: + return True; +} + +void MultiFramedRTPSource::doStopGettingFrames() { + fRTPInterface.stopNetworkReading(); + fReorderingBuffer->reset(); + reset(); +} + +void MultiFramedRTPSource::doGetNextFrame() { + if (!fAreDoingNetworkReads) { + // Turn on background read handling of incoming packets: + fAreDoingNetworkReads = True; + TaskScheduler::BackgroundHandlerProc* handler + = (TaskScheduler::BackgroundHandlerProc*)&networkReadHandler; + fRTPInterface.startNetworkReading(handler); + } + + fSavedTo = fTo; + fSavedMaxSize = fMaxSize; + fFrameSize = 0; // for now + fNeedDelivery = True; + doGetNextFrame1(); +} + +void MultiFramedRTPSource::doGetNextFrame1() { + while (fNeedDelivery) { + // If we already have packet data available, then deliver it now. + Boolean packetLossPrecededThis; + BufferedPacket* nextPacket + = fReorderingBuffer->getNextCompletedPacket(packetLossPrecededThis); + if (nextPacket == NULL) break; + + fNeedDelivery = False; + + if (nextPacket->useCount() == 0) { + // Before using the packet, check whether it has a special header + // that needs to be processed: + unsigned specialHeaderSize; + if (!processSpecialHeader(nextPacket, specialHeaderSize)) { + // Something's wrong with the header; reject the packet: + fReorderingBuffer->releaseUsedPacket(nextPacket); + fNeedDelivery = True; + break; + } + nextPacket->skip(specialHeaderSize); + } + + // Check whether we're part of a multi-packet frame, and whether + // there was packet loss that would render this packet unusable: + if (fCurrentPacketBeginsFrame) { + if (packetLossPrecededThis || fPacketLossInFragmentedFrame) { + // We didn't get all of the previous frame. + // Forget any data that we used from it: + fTo = fSavedTo; fMaxSize = fSavedMaxSize; + fFrameSize = 0; + } + fPacketLossInFragmentedFrame = False; + } else if (packetLossPrecededThis) { + // We're in a multi-packet frame, with preceding packet loss + fPacketLossInFragmentedFrame = True; + } + if (fPacketLossInFragmentedFrame) { + // This packet is unusable; reject it: + fReorderingBuffer->releaseUsedPacket(nextPacket); + fNeedDelivery = True; + break; + } + + // The packet is usable. Deliver all or part of it to our caller: + unsigned frameSize; + nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes, + fCurPacketRTPSeqNum, fCurPacketRTPTimestamp, + fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP, + fCurPacketMarkerBit); + fFrameSize += frameSize; + + if (!nextPacket->hasUsableData()) { + // We're completely done with this packet now + fReorderingBuffer->releaseUsedPacket(nextPacket); + } + + if (fCurrentPacketCompletesFrame || fNumTruncatedBytes > 0) { + // We have all the data that the client wants. + if (fNumTruncatedBytes > 0) { + envir() << "MultiFramedRTPSource::doGetNextFrame1(): The total received frame size exceeds the client's buffer size (" + << fSavedMaxSize << "). " + << fNumTruncatedBytes << " bytes of trailing data will be dropped!\n"; + } + // Call our own 'after getting' function, so that the downstream object can consume the data: + if (fReorderingBuffer->isEmpty()) { + // Common case optimization: There are no more queued incoming packets, so this code will not get + // executed again without having first returned to the event loop. Call our 'after getting' function + // directly, because there's no risk of a long chain of recursion (and thus stack overflow): + afterGetting(this); + } else { + // Special case: Call our 'after getting' function via the event loop. + nextTask() = envir().taskScheduler().scheduleDelayedTask(0, + (TaskFunc*)FramedSource::afterGetting, this); + } + } else { + // This packet contained fragmented data, and does not complete + // the data that the client wants. Keep getting data: + fTo += frameSize; fMaxSize -= frameSize; + fNeedDelivery = True; + } + } +} + +void MultiFramedRTPSource +::setPacketReorderingThresholdTime(unsigned uSeconds) { + fReorderingBuffer->setThresholdTime(uSeconds); +} + +#define ADVANCE(n) do { bPacket->skip(n); } while (0) + +void MultiFramedRTPSource::networkReadHandler(MultiFramedRTPSource* source, + int /*mask*/) { + // Get a free BufferedPacket descriptor to hold the new network packet: + BufferedPacket* bPacket + = source->fReorderingBuffer->getFreePacket(source); + + // Read the network packet, and perform sanity checks on the RTP header: + Boolean readSuccess = False; + do { + if (!bPacket->fillInData(source->fRTPInterface)) break; +#ifdef TEST_LOSS + source->setPacketReorderingThresholdTime(0); + // don't wait for 'lost' packets to arrive out-of-order later + if ((our_random()%10) == 0) break; // simulate 10% packet loss +#endif + + // Check for the 12-byte RTP header: + if (bPacket->dataSize() < 12) break; + unsigned rtpHdr = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4); + Boolean rtpMarkerBit = (rtpHdr&0x00800000) != 0; + unsigned rtpTimestamp = ntohl(*(u_int32_t*)(bPacket->data()));ADVANCE(4); + unsigned rtpSSRC = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4); + + // Check the RTP version number (it should be 2): + if ((rtpHdr&0xC0000000) != 0x80000000) break; + + // Skip over any CSRC identifiers in the header: + unsigned cc = (rtpHdr>>24)&0xF; + if (bPacket->dataSize() < cc) break; + ADVANCE(cc*4); + + // Check for (& ignore) any RTP header extension + if (rtpHdr&0x10000000) { + if (bPacket->dataSize() < 4) break; + unsigned extHdr = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4); + unsigned remExtSize = 4*(extHdr&0xFFFF); + if (bPacket->dataSize() < remExtSize) break; + ADVANCE(remExtSize); + } + + // Discard any padding bytes: + if (rtpHdr&0x20000000) { + if (bPacket->dataSize() == 0) break; + unsigned numPaddingBytes + = (unsigned)(bPacket->data())[bPacket->dataSize()-1]; + if (bPacket->dataSize() < numPaddingBytes) break; + bPacket->removePadding(numPaddingBytes); + } + // Check the Payload Type. + if ((unsigned char)((rtpHdr&0x007F0000)>>16) + != source->rtpPayloadFormat()) { + break; + } + + // The rest of the packet is the usable data. Record and save it: + source->fLastReceivedSSRC = rtpSSRC; + unsigned short rtpSeqNo = (unsigned short)(rtpHdr&0xFFFF); + Boolean usableInJitterCalculation + = source->packetIsUsableInJitterCalculation((bPacket->data()), + bPacket->dataSize()); + struct timeval presentationTime; // computed by: + Boolean hasBeenSyncedUsingRTCP; // computed by: + source->receptionStatsDB() + .noteIncomingPacket(rtpSSRC, rtpSeqNo, rtpTimestamp, + source->timestampFrequency(), + usableInJitterCalculation, presentationTime, + hasBeenSyncedUsingRTCP, bPacket->dataSize()); + + // Fill in the rest of the packet descriptor, and store it: + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + bPacket->assignMiscParams(rtpSeqNo, rtpTimestamp, presentationTime, + hasBeenSyncedUsingRTCP, rtpMarkerBit, + timeNow); + if (!source->fReorderingBuffer->storePacket(bPacket)) break; + + readSuccess = True; + } while (0); + if (!readSuccess) source->fReorderingBuffer->freePacket(bPacket); + + source->doGetNextFrame1(); + // If we didn't get proper data this time, we'll get another chance +} + + +////////// BufferedPacket and BufferedPacketFactory implementation ///// + +#define MAX_PACKET_SIZE 10000 + +BufferedPacket::BufferedPacket() + : fPacketSize(MAX_PACKET_SIZE), + fBuf(new unsigned char[MAX_PACKET_SIZE]), + fNextPacket(NULL), + fHead(0), fTail(0), fUseCount(0), fRTPSeqNo(0), fRTPTimestamp(0), + fHasBeenSyncedUsingRTCP(False), fRTPMarkerBit(False), fIsFirstPacket(True) +{ + memset(&fPresentationTime, 0, sizeof(fPresentationTime)); + memset(&fTimeReceived, 0, sizeof(fTimeReceived)); +} + +BufferedPacket::~BufferedPacket() { + delete fNextPacket; + delete[] fBuf; +} + +void BufferedPacket::reset() { + fHead = fTail = 0; + fUseCount = 0; + fIsFirstPacket = False; // by default +} + +// The following function has been deprecated: +unsigned BufferedPacket +::nextEnclosedFrameSize(unsigned char*& /*framePtr*/, unsigned dataSize) { + // By default, use the entire buffered data, even though it may consist + // of more than one frame, on the assumption that the client doesn't + // care. (This is more efficient than delivering a frame at a time) + return dataSize; +} + +void BufferedPacket +::getNextEnclosedFrameParameters(unsigned char*& framePtr, unsigned dataSize, + unsigned& frameSize, + unsigned& frameDurationInMicroseconds) { + // By default, use the entire buffered data, even though it may consist + // of more than one frame, on the assumption that the client doesn't + // care. (This is more efficient than delivering a frame at a time) + + // For backwards-compatibility with existing uses of (the now deprecated) + // "nextEnclosedFrameSize()", call that function to implement this one: + frameSize = nextEnclosedFrameSize(framePtr, dataSize); + + frameDurationInMicroseconds = 0; // by default. Subclasses should correct this. +} + +Boolean BufferedPacket::fillInData(RTPInterface& rtpInterface) { + reset(); + + unsigned numBytesRead; + struct sockaddr_in fromAddress; + if (!rtpInterface.handleRead(&fBuf[fTail], fPacketSize-fTail, numBytesRead, + fromAddress)) { + return False; + } + fTail += numBytesRead; + return True; +} + +void BufferedPacket +::assignMiscParams(unsigned short rtpSeqNo, unsigned rtpTimestamp, + struct timeval presentationTime, + Boolean hasBeenSyncedUsingRTCP, Boolean rtpMarkerBit, + struct timeval timeReceived) { + fRTPSeqNo = rtpSeqNo; + fRTPTimestamp = rtpTimestamp; + fPresentationTime = presentationTime; + fHasBeenSyncedUsingRTCP = hasBeenSyncedUsingRTCP; + fRTPMarkerBit = rtpMarkerBit; + fTimeReceived = timeReceived; +} + +void BufferedPacket::skip(unsigned numBytes) { + fHead += numBytes; + if (fHead > fTail) fHead = fTail; +} + +void BufferedPacket::removePadding(unsigned numBytes) { + if (numBytes > fTail-fHead) numBytes = fTail-fHead; + fTail -= numBytes; +} + +void BufferedPacket::appendData(unsigned char* newData, unsigned numBytes) { + if (numBytes > fPacketSize-fTail) numBytes = fPacketSize - fTail; + memmove(&fBuf[fTail], newData, numBytes); + fTail += numBytes; +} + +void BufferedPacket::use(unsigned char* to, unsigned toSize, + unsigned& bytesUsed, unsigned& bytesTruncated, + unsigned short& rtpSeqNo, unsigned& rtpTimestamp, + struct timeval& presentationTime, + Boolean& hasBeenSyncedUsingRTCP, + Boolean& rtpMarkerBit) { + unsigned char* origFramePtr = &fBuf[fHead]; + unsigned char* newFramePtr = origFramePtr; // may change in the call below + unsigned frameSize, frameDurationInMicroseconds; + getNextEnclosedFrameParameters(newFramePtr, fTail - fHead, + frameSize, frameDurationInMicroseconds); + if (frameSize > toSize) { + bytesTruncated = frameSize - toSize; + bytesUsed = toSize; + } else { + bytesTruncated = 0; + bytesUsed = frameSize; + } + + memmove(to, newFramePtr, bytesUsed); + fHead += (newFramePtr - origFramePtr) + frameSize; + ++fUseCount; + + rtpSeqNo = fRTPSeqNo; + rtpTimestamp = fRTPTimestamp; + presentationTime = fPresentationTime; + hasBeenSyncedUsingRTCP = fHasBeenSyncedUsingRTCP; + rtpMarkerBit = fRTPMarkerBit; + + // Update "fPresentationTime" for the next enclosed frame (if any): + fPresentationTime.tv_usec += frameDurationInMicroseconds; + if (fPresentationTime.tv_usec >= 1000000) { + fPresentationTime.tv_sec += fPresentationTime.tv_usec/1000000; + fPresentationTime.tv_usec = fPresentationTime.tv_usec%1000000; + } +} + +BufferedPacketFactory::BufferedPacketFactory() { +} + +BufferedPacketFactory::~BufferedPacketFactory() { +} + +BufferedPacket* BufferedPacketFactory +::createNewPacket(MultiFramedRTPSource* /*ourSource*/) { + return new BufferedPacket; +} + + +////////// ReorderingPacketBuffer implementation ////////// + +ReorderingPacketBuffer +::ReorderingPacketBuffer(BufferedPacketFactory* packetFactory) + : fThresholdTime(100000) /* default reordering threshold: 100 ms */, + fHaveSeenFirstPacket(False), fHeadPacket(NULL), fSavedPacket(NULL), fSavedPacketFree(True), fNextExpectedSeqNo(0) +{ + fPacketFactory = (packetFactory == NULL) + ? (new BufferedPacketFactory) + : packetFactory; +} + +ReorderingPacketBuffer::~ReorderingPacketBuffer() { + reset(); + delete fPacketFactory; +} + +void ReorderingPacketBuffer::reset() { + if (fSavedPacketFree) delete fSavedPacket; // because fSavedPacket is not in the list + delete fHeadPacket; // will also delete fSavedPacket if it's in the list + fHaveSeenFirstPacket = False; + fHeadPacket = NULL; + fSavedPacket = NULL; +} + +BufferedPacket* ReorderingPacketBuffer +::getFreePacket(MultiFramedRTPSource* ourSource) { + if (fSavedPacket == NULL) { // we're being called for the first time + fSavedPacket = fPacketFactory->createNewPacket(ourSource); + fSavedPacketFree = True; + } + + if (fSavedPacketFree == True) { + fSavedPacketFree = False; + return fSavedPacket; + } else { + return fPacketFactory->createNewPacket(ourSource); + } +} + +Boolean ReorderingPacketBuffer::storePacket(BufferedPacket* bPacket) { + unsigned short rtpSeqNo = bPacket->rtpSeqNo(); + + if (!fHaveSeenFirstPacket) { + fNextExpectedSeqNo = rtpSeqNo; // initialization + bPacket->isFirstPacket() = True; + fHaveSeenFirstPacket = True; + } + + // Ignore this packet if its sequence number is less than the one + // that we're looking for (in this case, it's been excessively delayed). + if (seqNumLT(rtpSeqNo, fNextExpectedSeqNo)) return False; + + // Figure out where the new packet will be stored in the queue: + BufferedPacket* beforePtr = NULL; + BufferedPacket* afterPtr = fHeadPacket; + while (afterPtr != NULL) { + if (seqNumLT(rtpSeqNo, afterPtr->rtpSeqNo())) break; // it comes here + if (rtpSeqNo == afterPtr->rtpSeqNo()) { + // This is a duplicate packet - ignore it + return False; + } + + beforePtr = afterPtr; + afterPtr = afterPtr->nextPacket(); + } + + // Link our new packet between "beforePtr" and "afterPtr": + bPacket->nextPacket() = afterPtr; + if (beforePtr == NULL) { + fHeadPacket = bPacket; + } else { + beforePtr->nextPacket() = bPacket; + } + + return True; +} + +void ReorderingPacketBuffer::releaseUsedPacket(BufferedPacket* packet) { + // ASSERT: packet == fHeadPacket + // ASSERT: fNextExpectedSeqNo == packet->rtpSeqNo() + ++fNextExpectedSeqNo; // because we're finished with this packet now + + fHeadPacket = fHeadPacket->nextPacket(); + packet->nextPacket() = NULL; + + freePacket(packet); +} + +BufferedPacket* ReorderingPacketBuffer +::getNextCompletedPacket(Boolean& packetLossPreceded) { + if (fHeadPacket == NULL) return NULL; + + // Check whether the next packet we want is already at the head + // of the queue: + // ASSERT: fHeadPacket->rtpSeqNo() >= fNextExpectedSeqNo + if (fHeadPacket->rtpSeqNo() == fNextExpectedSeqNo) { + packetLossPreceded = fHeadPacket->isFirstPacket(); + // (The very first packet is treated as if there was packet loss beforehand.) + return fHeadPacket; + } + + // We're still waiting for our desired packet to arrive. However, if + // our time threshold has been exceeded, then forget it, and return + // the head packet instead: + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + unsigned uSecondsSinceReceived + = (timeNow.tv_sec - fHeadPacket->timeReceived().tv_sec)*1000000 + + (timeNow.tv_usec - fHeadPacket->timeReceived().tv_usec); + if (uSecondsSinceReceived > fThresholdTime) { + fNextExpectedSeqNo = fHeadPacket->rtpSeqNo(); + // we've given up on earlier packets now + packetLossPreceded = True; + return fHeadPacket; + } + + // Otherwise, keep waiting for our desired packet to arrive: + return NULL; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/our_md5.c kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/our_md5.c --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/our_md5.c 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/our_md5.c 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,319 @@ +/* + * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights + * reserved. + * + * License to copy and use this software is granted provided that it is + * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" + * in all material mentioning or referencing this software or this function. + * + * License is also granted to make and use derivative works provided that such + * works are identified as "derived from the RSA Data Security, Inc. MD5 + * Message-Digest Algorithm" in all material mentioning or referencing the + * derived work. + * + * RSA Data Security, Inc. makes no representations concerning either the + * merchantability of this software or the suitability of this software for + * any particular purpose. It is provided "as is" without express or implied + * warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +#include "our_md5.h" +#include + +/* + * Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* + * ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* + * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is + * separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UNSIGNED32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UNSIGNED32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UNSIGNED32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UNSIGNED32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* + * Encodes input (UNSIGNED32) into output (unsigned char). Assumes len is a + * multiple of 4. + */ +static void +Encode(unsigned char *output, UNSIGNED32 * input, unsigned int len) +{ + unsigned int i, j; + +#if 0 + assert((len % 4) == 0); +#endif + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char) (input[i] & 0xff); + output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff); + output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff); + output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff); + } +} + +/* + * Decodes input (unsigned char) into output (UNSIGNED32). Assumes len is a + * multiple of 4. + */ +static void +Decode(UNSIGNED32 * output, unsigned char const *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UNSIGNED32) input[j]) | (((UNSIGNED32) input[j + 1]) << 8) | + (((UNSIGNED32) input[j + 2]) << 16) | (((UNSIGNED32) input[j + 3]) << 24); +} + +/* + * MD5 basic transformation. Transforms state based on block. + */ +static void +MD5Transform(UNSIGNED32 state[4], const unsigned char block[64]) +{ + UNSIGNED32 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* + * Zeroize sensitive information. + */ + memset((unsigned char *) x, 0, sizeof(x)); +} + +/** + * our_MD5Init: + * @context: MD5 context to be initialized. + * + * Initializes MD5 context for the start of message digest computation. + **/ +void +our_MD5Init(MD5_CTX * context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/** + * ourMD5Update: + * @context: MD5 context to be updated. + * @input: pointer to data to be fed into MD5 algorithm. + * @inputLen: size of @input data in bytes. + * + * MD5 block update operation. Continues an MD5 message-digest operation, + * processing another message block, and updating the context. + **/ + +void +ourMD5Update(MD5_CTX * context, const unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UNSIGNED32) inputLen << 3)) < ((UNSIGNED32) inputLen << 3)) { + context->count[1]++; + } + context->count[1] += ((UNSIGNED32) inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + memcpy((unsigned char *) & context->buffer[index], (unsigned char *) input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) { + MD5Transform(context->state, &input[i]); + } + index = 0; + } else { + i = 0; + } + /* Buffer remaining input */ + if ((inputLen - i) != 0) { + memcpy((unsigned char *) & context->buffer[index], (unsigned char *) & input[i], inputLen - i); + } +} + +/** + * our_MD5Final: + * @digest: 16-byte buffer to write MD5 checksum. + * @context: MD5 context to be finalized. + * + * Ends an MD5 message-digest operation, writing the the message + * digest and zeroing the context. The context must be initialized + * with our_MD5Init() before being used for other MD5 checksum calculations. + **/ + +void +our_MD5Final(unsigned char digest[16], MD5_CTX * context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode(bits, context->count, 8); + + /* + * Pad out to 56 mod 64. + */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + ourMD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + ourMD5Update(context, bits, 8); + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* + * Zeroize sensitive information. + */ + memset((unsigned char *) context, 0, sizeof(*context)); +} + diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/our_md5.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/our_md5.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/our_md5.h 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/our_md5.h 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,61 @@ +/* Because MD5 may not be implemented (at least, with the same + * interface) on all systems, we have our own copy here. + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#ifndef _SYS_MD5_H_ +#define _SYS_MD5_H_ + +typedef unsigned UNSIGNED32; + +/* Definitions of _ANSI_ARGS and EXTERN that will work in either + C or C++ code: + */ +#undef _ANSI_ARGS_ +#if ((defined(__STDC__) || defined(SABER)) && !defined(NO_PROTOTYPE)) || defined(__cplusplus) || defined(USE_PROTOTYPE) +# define _ANSI_ARGS_(x) x +#else +# define _ANSI_ARGS_(x) () +#endif +#ifdef __cplusplus +# define EXTERN extern "C" +#else +# define EXTERN extern +#endif + +/* MD5 context. */ +typedef struct MD5Context { + UNSIGNED32 state[4]; /* state (ABCD) */ + UNSIGNED32 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +EXTERN void our_MD5Init (MD5_CTX *); +EXTERN void ourMD5Update (MD5_CTX *, const unsigned char *, unsigned int); +EXTERN void our_MD5Pad (MD5_CTX *); +EXTERN void our_MD5Final (unsigned char [16], MD5_CTX *); +EXTERN char * our_MD5End(MD5_CTX *, char *); +EXTERN char * our_MD5File(const char *, char *); +EXTERN char * our_MD5Data(const unsigned char *, unsigned int, char *); +#endif /* _SYS_MD5_H_ */ diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/our_md5hl.c kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/our_md5hl.c --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/our_md5hl.c 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/our_md5hl.c 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,68 @@ +#define LENGTH 16 +/* md5hl.c + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + */ + +#include +#include +#include "our_md5.h" +#include "NetCommon.h" + +#ifndef BUFSIZ //pocket pc +#define BUFSIZ 255 +#endif + + +char * +our_MD5End(MD5_CTX *ctx, char *buf) +{ + int i; + unsigned char digest[LENGTH]; + static const char hex[]="0123456789abcdef"; + + if (!buf) + buf = (char*)malloc(2*LENGTH + 1); + if (!buf) + return 0; + our_MD5Final(digest, ctx); + for (i = 0; i < LENGTH; i++) { + buf[i+i] = hex[digest[i] >> 4]; + buf[i+i+1] = hex[digest[i] & 0x0f]; + } + buf[i+i] = '\0'; + return buf; +} + +char * +our_MD5File(const char *filename, char *buf) +{ + unsigned char buffer[BUFSIZ]; + MD5_CTX ctx; + int i; + FILE* f; + + our_MD5Init(&ctx); + f = fopen(filename, "r"); + if (f == NULL) return 0; + while ((i = fread(buffer,1,sizeof buffer,f)) > 0) { + ourMD5Update(&ctx,buffer,i); + } + fclose(f); + if (i < 0) return 0; + return our_MD5End(&ctx, buf); +} + +char * +our_MD5Data (const unsigned char *data, unsigned int len, char *buf) +{ + MD5_CTX ctx; + + our_MD5Init(&ctx); + ourMD5Update(&ctx,data,len); + return our_MD5End(&ctx, buf); +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTCP.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTCP.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTCP.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTCP.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,1000 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTCP +// Implementation + +#include "RTCP.hh" +#include "GroupsockHelper.hh" +#include "rtcp_from_spec.h" + +////////// RTCPMemberDatabase ////////// + +class RTCPMemberDatabase { +public: + RTCPMemberDatabase(RTCPInstance& ourRTCPInstance) + : fOurRTCPInstance(ourRTCPInstance), fNumMembers(1 /*ourself*/), + fTable(HashTable::create(ONE_WORD_HASH_KEYS)) { + } + + virtual ~RTCPMemberDatabase() { + delete fTable; + } + + Boolean isMember(unsigned ssrc) const { + return fTable->Lookup((char*)(long)ssrc) != NULL; + } + + Boolean noteMembership(unsigned ssrc, unsigned curTimeCount) { + Boolean isNew = !isMember(ssrc); + + if (isNew) { + ++fNumMembers; + } + + // Record the current time, so we can age stale members + fTable->Add((char*)(long)ssrc, (void*)(long)curTimeCount); + + return isNew; + } + + Boolean remove(unsigned ssrc) { + Boolean wasPresent = fTable->Remove((char*)(long)ssrc); + if (wasPresent) { + --fNumMembers; + } + return wasPresent; + } + + unsigned numMembers() const { + return fNumMembers; + } + + void reapOldMembers(unsigned threshold); + +private: + RTCPInstance& fOurRTCPInstance; + unsigned fNumMembers; + HashTable* fTable; +}; + +void RTCPMemberDatabase::reapOldMembers(unsigned threshold) { + Boolean foundOldMember; + unsigned oldSSRC = 0; + + do { + foundOldMember = False; + + HashTable::Iterator* iter + = HashTable::Iterator::create(*fTable); + unsigned long timeCount; + char const* key; + while ((timeCount = (unsigned long)(iter->next(key))) != 0) { +#ifdef DEBUG + fprintf(stderr, "reap: checking SSRC 0x%lx: %ld (threshold %d)\n", (unsigned long)key, timeCount, threshold); +#endif + if (timeCount < (unsigned long)threshold) { // this SSRC is old + unsigned long ssrc = (unsigned long)key; + oldSSRC = (unsigned)ssrc; + foundOldMember = True; + } + } + delete iter; + + if (foundOldMember) { +#ifdef DEBUG + fprintf(stderr, "reap: removing SSRC 0x%x\n", oldSSRC); +#endif + fOurRTCPInstance.removeSSRC(oldSSRC, True); + } + } while (foundOldMember); +} + + +////////// RTCPInstance ////////// + +static double dTimeNow() { + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + return (double) (timeNow.tv_sec + timeNow.tv_usec/1000000.0); +} + +static unsigned const maxPacketSize = 1450; + // bytes (1500, minus some allowance for IP, UDP, UMTP headers) +static unsigned const preferredPacketSize = 1000; // bytes + +RTCPInstance::RTCPInstance(UsageEnvironment& env, Groupsock* RTCPgs, + unsigned totSessionBW, + unsigned char const* cname, + RTPSink* sink, RTPSource const* source, + Boolean isSSMSource) + : Medium(env), fRTCPInterface(this, RTCPgs), fTotSessionBW(totSessionBW), + fSink(sink), fSource(source), fIsSSMSource(isSSMSource), + fCNAME(RTCP_SDES_CNAME, cname), fOutgoingReportCount(1), + fAveRTCPSize(0), fIsInitial(1), fPrevNumMembers(0), + fLastSentSize(0), fLastReceivedSize(0), fLastReceivedSSRC(0), + fTypeOfEvent(EVENT_UNKNOWN), fTypeOfPacket(PACKET_UNKNOWN_TYPE), + fHaveJustSentPacket(False), fLastPacketSentSize(0), + fByeHandlerTask(NULL), fByeHandlerClientData(NULL), + fSRHandlerTask(NULL), fSRHandlerClientData(NULL), + fRRHandlerTask(NULL), fRRHandlerClientData(NULL), + fSpecificRRHandlerTable(NULL) { +#ifdef DEBUG + fprintf(stderr, "RTCPInstance[%p]::RTCPInstance()\n", this); +#endif + if (fTotSessionBW == 0) { // not allowed! + env << "RTCPInstance::RTCPInstance error: totSessionBW parameter should not be zero!\n"; + fTotSessionBW = 1; + } + + if (isSSMSource) RTCPgs->multicastSendOnly(); // don't receive multicast + + double timeNow = dTimeNow(); + fPrevReportTime = fNextReportTime = timeNow; + + fKnownMembers = new RTCPMemberDatabase(*this); + fInBuf = new unsigned char[maxPacketSize]; + if (fKnownMembers == NULL || fInBuf == NULL) return; + + // A hack to save buffer space, because RTCP packets are always small: + unsigned savedMaxSize = OutPacketBuffer::maxSize; + OutPacketBuffer::maxSize = maxPacketSize; + fOutBuf = new OutPacketBuffer(preferredPacketSize, maxPacketSize); + OutPacketBuffer::maxSize = savedMaxSize; + if (fOutBuf == NULL) return; + + // Arrange to handle incoming reports from others: + TaskScheduler::BackgroundHandlerProc* handler + = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler; + fRTCPInterface.startNetworkReading(handler); + + // Send our first report. + fTypeOfEvent = EVENT_REPORT; + onExpire(this); +} + +struct RRHandlerRecord { + TaskFunc* rrHandlerTask; + void* rrHandlerClientData; +}; + +RTCPInstance::~RTCPInstance() { +#ifdef DEBUG + fprintf(stderr, "RTCPInstance[%p]::~RTCPInstance()\n", this); +#endif + // Turn off background read handling: + fRTCPInterface.stopNetworkReading(); + + // Begin by sending a BYE. We have to do this immediately, without + // 'reconsideration', because "this" is going away. + fTypeOfEvent = EVENT_BYE; // not used, but... + sendBYE(); + + if (fSpecificRRHandlerTable != NULL) { + AddressPortLookupTable::Iterator iter(*fSpecificRRHandlerTable); + RRHandlerRecord* rrHandler; + while ((rrHandler = (RRHandlerRecord*)iter.next()) != NULL) { + delete rrHandler; + } + delete fSpecificRRHandlerTable; + } + + delete fKnownMembers; + delete fOutBuf; + delete[] fInBuf; +} + +RTCPInstance* RTCPInstance::createNew(UsageEnvironment& env, Groupsock* RTCPgs, + unsigned totSessionBW, + unsigned char const* cname, + RTPSink* sink, RTPSource const* source, + Boolean isSSMSource) { + return new RTCPInstance(env, RTCPgs, totSessionBW, cname, sink, source, + isSSMSource); +} + +Boolean RTCPInstance::lookupByName(UsageEnvironment& env, + char const* instanceName, + RTCPInstance*& resultInstance) { + resultInstance = NULL; // unless we succeed + + Medium* medium; + if (!Medium::lookupByName(env, instanceName, medium)) return False; + + if (!medium->isRTCPInstance()) { + env.setResultMsg(instanceName, " is not a RTCP instance"); + return False; + } + + resultInstance = (RTCPInstance*)medium; + return True; +} + +Boolean RTCPInstance::isRTCPInstance() const { + return True; +} + +unsigned RTCPInstance::numMembers() const { + if (fKnownMembers == NULL) return 0; + + return fKnownMembers->numMembers(); +} + +void RTCPInstance::setByeHandler(TaskFunc* handlerTask, void* clientData, + Boolean handleActiveParticipantsOnly) { + fByeHandlerTask = handlerTask; + fByeHandlerClientData = clientData; + fByeHandleActiveParticipantsOnly = handleActiveParticipantsOnly; +} + +void RTCPInstance::setSRHandler(TaskFunc* handlerTask, void* clientData) { + fSRHandlerTask = handlerTask; + fSRHandlerClientData = clientData; +} + +void RTCPInstance::setRRHandler(TaskFunc* handlerTask, void* clientData) { + fRRHandlerTask = handlerTask; + fRRHandlerClientData = clientData; +} + +void RTCPInstance +::setSpecificRRHandler(netAddressBits fromAddress, Port fromPort, + TaskFunc* handlerTask, void* clientData) { + if (handlerTask == NULL && clientData == NULL) { + unsetSpecificRRHandler(fromAddress, fromPort); + return; + } + + RRHandlerRecord* rrHandler = new RRHandlerRecord; + rrHandler->rrHandlerTask = handlerTask; + rrHandler->rrHandlerClientData = clientData; + if (fSpecificRRHandlerTable == NULL) { + fSpecificRRHandlerTable = new AddressPortLookupTable; + } + fSpecificRRHandlerTable->Add(fromAddress, (~0), fromPort, rrHandler); +} + +void RTCPInstance +::unsetSpecificRRHandler(netAddressBits fromAddress, Port fromPort) { + if (fSpecificRRHandlerTable == NULL) return; + + RRHandlerRecord* rrHandler + = (RRHandlerRecord*)(fSpecificRRHandlerTable->Lookup(fromAddress, (~0), fromPort)); + if (rrHandler != NULL) { + fSpecificRRHandlerTable->Remove(fromAddress, (~0), fromPort); + delete rrHandler; + } +} + +void RTCPInstance::setStreamSocket(int sockNum, + unsigned char streamChannelId) { + // Turn off background read handling: + fRTCPInterface.stopNetworkReading(); + + // Switch to RTCP-over-TCP: + fRTCPInterface.setStreamSocket(sockNum, streamChannelId); + + // Turn background reading back on: + TaskScheduler::BackgroundHandlerProc* handler + = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler; + fRTCPInterface.startNetworkReading(handler); +} + +void RTCPInstance::addStreamSocket(int sockNum, + unsigned char streamChannelId) { + // First, turn off background read handling for the default (UDP) socket: + fRTCPInterface.stopNetworkReading(); + + // Add the RTCP-over-TCP interface: + fRTCPInterface.addStreamSocket(sockNum, streamChannelId); + + // Turn on background reading for this socket (in case it's not on already): + TaskScheduler::BackgroundHandlerProc* handler + = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler; + fRTCPInterface.startNetworkReading(handler); +} + +static unsigned const IP_UDP_HDR_SIZE = 28; + // overhead (bytes) of IP and UDP hdrs + +#define ADVANCE(n) pkt += (n); packetSize -= (n) + +void RTCPInstance::incomingReportHandler(RTCPInstance* instance, + int /*mask*/) { + instance->incomingReportHandler1(); +} + +void RTCPInstance::incomingReportHandler1() { + unsigned char* pkt = fInBuf; + unsigned packetSize; + struct sockaddr_in fromAddress; + int typeOfPacket = PACKET_UNKNOWN_TYPE; + + do { + int tcpReadStreamSocketNum = fRTCPInterface.nextTCPReadStreamSocketNum(); + unsigned char tcpReadStreamChannelId = fRTCPInterface.nextTCPReadStreamChannelId(); + if (!fRTCPInterface.handleRead(pkt, maxPacketSize, + packetSize, fromAddress)) { + break; + } + + // Ignore the packet if it was looped-back from ourself: + if (RTCPgs()->wasLoopedBackFromUs(envir(), fromAddress)) { + // However, we still want to handle incoming RTCP packets from + // *other processes* on the same machine. To distinguish this + // case from a true loop-back, check whether we've just sent a + // packet of the same size. (This check isn't perfect, but it seems + // to be the best we can do.) + if (fHaveJustSentPacket && fLastPacketSentSize == packetSize) { + // This is a true loop-back: + fHaveJustSentPacket = False; + break; // ignore this packet + } + } + + if (fIsSSMSource) { + // This packet was received via unicast. 'Reflect' it by resending + // it to the multicast group. + // NOTE: Denial-of-service attacks are possible here. + // Users of this software may wish to add their own, + // application-specific mechanism for 'authenticating' the + // validity of this packet before reflecting it. + fRTCPInterface.sendPacket(pkt, packetSize); + fHaveJustSentPacket = True; + fLastPacketSentSize = packetSize; + } + +#ifdef DEBUG + fprintf(stderr, "[%p]saw incoming RTCP packet (from address %s, port %d)\n", this, our_inet_ntoa(fromAddress.sin_addr), ntohs(fromAddress.sin_port)); + unsigned char* p = pkt; + for (unsigned i = 0; i < packetSize; ++i) { + if (i%4 == 0) fprintf(stderr, " "); + fprintf(stderr, "%02x", p[i]); + } + fprintf(stderr, "\n"); +#endif + int totPacketSize = IP_UDP_HDR_SIZE + packetSize; + + // Check the RTCP packet for validity: + // It must at least contain a header (4 bytes), and this header + // must be version=2, with no padding bit, and a payload type of + // SR (200) or RR (201): + if (packetSize < 4) break; + unsigned rtcpHdr = ntohl(*(unsigned*)pkt); + if ((rtcpHdr & 0xE0FE0000) != (0x80000000 | (RTCP_PT_SR<<16))) { +#ifdef DEBUG + fprintf(stderr, "rejected bad RTCP packet: header 0x%08x\n", rtcpHdr); +#endif + break; + } + + // Process each of the individual RTCP 'subpackets' in (what may be) + // a compound RTCP packet. + unsigned reportSenderSSRC = 0; + Boolean packetOK = False; + while (1) { + unsigned rc = (rtcpHdr>>24)&0x1F; + unsigned pt = (rtcpHdr>>16)&0xFF; + unsigned length = 4*(rtcpHdr&0xFFFF); // doesn't count hdr + ADVANCE(4); // skip over the header + if (length > packetSize) break; + + // Assume that each RTCP subpacket begins with a 4-byte SSRC: + if (length < 4) break; length -= 4; + reportSenderSSRC = ntohl(*(unsigned*)pkt); ADVANCE(4); + + Boolean subPacketOK = False; + switch (pt) { + case RTCP_PT_SR: { +#ifdef DEBUG + fprintf(stderr, "SR\n"); +#endif + if (length < 20) break; length -= 20; + + // Extract the NTP timestamp, and note this: + unsigned NTPmsw = ntohl(*(unsigned*)pkt); ADVANCE(4); + unsigned NTPlsw = ntohl(*(unsigned*)pkt); ADVANCE(4); + unsigned rtpTimestamp = ntohl(*(unsigned*)pkt); ADVANCE(4); + if (fSource != NULL) { + RTPReceptionStatsDB& receptionStats + = fSource->receptionStatsDB(); + receptionStats.noteIncomingSR(reportSenderSSRC, + NTPmsw, NTPlsw, rtpTimestamp); + } + ADVANCE(8); // skip over packet count, octet count + + // If a 'SR handler' was set, call it now: + if (fSRHandlerTask != NULL) (*fSRHandlerTask)(fSRHandlerClientData); + + // The rest of the SR is handled like a RR (so, no "break;" here) + } + case RTCP_PT_RR: { +#ifdef DEBUG + fprintf(stderr, "RR\n"); +#endif + unsigned reportBlocksSize = rc*(6*4); + if (length < reportBlocksSize) break; + length -= reportBlocksSize; + + if (fSink != NULL) { + // Use this information to update stats about our transmissions: + RTPTransmissionStatsDB& transmissionStats = fSink->transmissionStatsDB(); + for (unsigned i = 0; i < rc; ++i) { + unsigned senderSSRC = ntohl(*(unsigned*)pkt); ADVANCE(4); + // We care only about reports about our own transmission, not others' + if (senderSSRC == fSink->SSRC()) { + unsigned lossStats = ntohl(*(unsigned*)pkt); ADVANCE(4); + unsigned highestReceived = ntohl(*(unsigned*)pkt); ADVANCE(4); + unsigned jitter = ntohl(*(unsigned*)pkt); ADVANCE(4); + unsigned timeLastSR = ntohl(*(unsigned*)pkt); ADVANCE(4); + unsigned timeSinceLastSR = ntohl(*(unsigned*)pkt); ADVANCE(4); + transmissionStats.noteIncomingRR(reportSenderSSRC, fromAddress, + lossStats, + highestReceived, jitter, + timeLastSR, timeSinceLastSR); + } else { + ADVANCE(4*5); + } + } + } else { + ADVANCE(reportBlocksSize); + } + + if (pt == RTCP_PT_RR) { // i.e., we didn't fall through from 'SR' + // If a 'RR handler' was set, call it now: + + // Specific RR handler: + if (fSpecificRRHandlerTable != NULL) { + netAddressBits fromAddr; + portNumBits fromPortNum; + if (tcpReadStreamSocketNum < 0) { + // Normal case: We read the RTCP packet over UDP + fromAddr = fromAddress.sin_addr.s_addr; + fromPortNum = ntohs(fromAddress.sin_port); + } else { + // Special case: We read the RTCP packet over TCP (interleaved) + // Hack: Use the TCP socket and channel id to look up the handler + fromAddr = tcpReadStreamSocketNum; + fromPortNum = tcpReadStreamChannelId; + } + Port fromPort(fromPortNum); + RRHandlerRecord* rrHandler + = (RRHandlerRecord*)(fSpecificRRHandlerTable->Lookup(fromAddr, (~0), fromPort)); + if (rrHandler != NULL) { + if (rrHandler->rrHandlerTask != NULL) { + (*(rrHandler->rrHandlerTask))(rrHandler->rrHandlerClientData); + } + } + } + + // General RR handler: + if (fRRHandlerTask != NULL) (*fRRHandlerTask)(fRRHandlerClientData); + } + + subPacketOK = True; + typeOfPacket = PACKET_RTCP_REPORT; + break; + } + case RTCP_PT_BYE: { +#ifdef DEBUG + fprintf(stderr, "BYE\n"); +#endif + // If a 'BYE handler' was set, call it now: + TaskFunc* byeHandler = fByeHandlerTask; + if (byeHandler != NULL + && (!fByeHandleActiveParticipantsOnly + || (fSource != NULL + && fSource->receptionStatsDB().lookup(reportSenderSSRC) != NULL) + || (fSink != NULL + && fSink->transmissionStatsDB().lookup(reportSenderSSRC) != NULL))) { + fByeHandlerTask = NULL; + // we call this only once by default + (*byeHandler)(fByeHandlerClientData); + } + + // We should really check for & handle >1 SSRCs being present ##### + + subPacketOK = True; + typeOfPacket = PACKET_BYE; + break; + } + // Later handle SDES, APP, and compound RTCP packets ##### + default: +#ifdef DEBUG + fprintf(stderr, "UNSUPPORTED TYPE(0x%x)\n", pt); +#endif + subPacketOK = True; + break; + } + if (!subPacketOK) break; + + // need to check for (& handle) SSRC collision! ##### + +#ifdef DEBUG + fprintf(stderr, "validated RTCP subpacket (type %d): %d, %d, %d, 0x%08x\n", typeOfPacket, rc, pt, length, reportSenderSSRC); +#endif + + // Skip over any remaining bytes in this subpacket: + ADVANCE(length); + + // Check whether another RTCP 'subpacket' follows: + if (packetSize == 0) { + packetOK = True; + break; + } else if (packetSize < 4) { +#ifdef DEBUG + fprintf(stderr, "extraneous %d bytes at end of RTCP packet!\n", packetSize); +#endif + break; + } + rtcpHdr = ntohl(*(unsigned*)pkt); + if ((rtcpHdr & 0xC0000000) != 0x80000000) { +#ifdef DEBUG + fprintf(stderr, "bad RTCP subpacket: header 0x%08x\n", rtcpHdr); +#endif + break; + } + } + + if (!packetOK) { +#ifdef DEBUG + fprintf(stderr, "rejected bad RTCP subpacket: header 0x%08x\n", rtcpHdr); +#endif + break; + } else { +#ifdef DEBUG + fprintf(stderr, "validated entire RTCP packet\n"); +#endif + } + + onReceive(typeOfPacket, totPacketSize, reportSenderSSRC); + } while (0); +} + +void RTCPInstance::onReceive(int typeOfPacket, int totPacketSize, + unsigned ssrc) { + fTypeOfPacket = typeOfPacket; + fLastReceivedSize = totPacketSize; + fLastReceivedSSRC = ssrc; + + int members = (int)numMembers(); + int senders = (fSink != NULL) ? 1 : 0; + + OnReceive(this, // p + this, // e + &members, // members + &fPrevNumMembers, // pmembers + &senders, // senders + &fAveRTCPSize, // avg_rtcp_size + &fPrevReportTime, // tp + dTimeNow(), // tc + fNextReportTime); +} + +void RTCPInstance::sendReport() { + // Hack: Don't send a SR during those (brief) times when the timestamp of the + // next outgoing RTP packet has been preset, to ensure that that timestamp gets + // used for that outgoing packet. (David Bertrand, 2006.07.18) + if (fSink != NULL && fSink->nextTimestampHasBeenPreset()) return; + +#ifdef DEBUG + fprintf(stderr, "sending REPORT\n"); +#endif + // Begin by including a SR and/or RR report: + addReport(); + + // Then, include a SDES: + addSDES(); + + // Send the report: + sendBuiltPacket(); + + // Periodically clean out old members from our SSRC membership database: + const unsigned membershipReapPeriod = 5; + if ((++fOutgoingReportCount) % membershipReapPeriod == 0) { + unsigned threshold = fOutgoingReportCount - membershipReapPeriod; + fKnownMembers->reapOldMembers(threshold); + } +} + +void RTCPInstance::sendBYE() { +#ifdef DEBUG + fprintf(stderr, "sending BYE\n"); +#endif + // The packet must begin with a SR and/or RR report: + addReport(); + + addBYE(); + sendBuiltPacket(); +} + +void RTCPInstance::sendBuiltPacket() { +#ifdef DEBUG + fprintf(stderr, "sending RTCP packet\n"); + unsigned char* p = fOutBuf->packet(); + for (unsigned i = 0; i < fOutBuf->curPacketSize(); ++i) { + if (i%4 == 0) fprintf(stderr," "); + fprintf(stderr, "%02x", p[i]); + } + fprintf(stderr, "\n"); +#endif + unsigned reportSize = fOutBuf->curPacketSize(); + fRTCPInterface.sendPacket(fOutBuf->packet(), reportSize); + fOutBuf->resetOffset(); + + fLastSentSize = IP_UDP_HDR_SIZE + reportSize; + fHaveJustSentPacket = True; + fLastPacketSentSize = reportSize; +} + +int RTCPInstance::checkNewSSRC() { + return fKnownMembers->noteMembership(fLastReceivedSSRC, + fOutgoingReportCount); +} + +void RTCPInstance::removeLastReceivedSSRC() { + removeSSRC(fLastReceivedSSRC, False/*keep stats around*/); +} + +void RTCPInstance::removeSSRC(u_int32_t ssrc, Boolean alsoRemoveStats) { + fKnownMembers->remove(ssrc); + + if (alsoRemoveStats) { + // Also, remove records of this SSRC from any reception or transmission stats + if (fSource != NULL) fSource->receptionStatsDB().removeRecord(ssrc); + if (fSink != NULL) fSink->transmissionStatsDB().removeRecord(ssrc); + } +} + +void RTCPInstance::onExpire(RTCPInstance* instance) { + instance->onExpire1(); +} + +// Member functions to build specific kinds of report: + +void RTCPInstance::addReport() { + // Include a SR or a RR, depending on whether we + // have an associated sink or source: + if (fSink != NULL) { + addSR(); + } else if (fSource != NULL) { + addRR(); + } +} + +void RTCPInstance::addSR() { + // ASSERT: fSink != NULL + + enqueueCommonReportPrefix(RTCP_PT_SR, fSink->SSRC(), + 5 /* extra words in a SR */); + + // Now, add the 'sender info' for our sink + + // Insert the NTP and RTP timestamps for the 'wallclock time': + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + fOutBuf->enqueueWord(timeNow.tv_sec + 0x83AA7E80); + // NTP timestamp most-significant word (1970 epoch -> 1900 epoch) + double fractionalPart = (timeNow.tv_usec/15625.0)*0x04000000; // 2^32/10^6 + fOutBuf->enqueueWord((unsigned)(fractionalPart+0.5)); + // NTP timestamp least-significant word + unsigned rtpTimestamp = fSink->convertToRTPTimestamp(timeNow); + fOutBuf->enqueueWord(rtpTimestamp); // RTP ts + + // Insert the packet and byte counts: + fOutBuf->enqueueWord(fSink->packetCount()); + fOutBuf->enqueueWord(fSink->octetCount()); + + enqueueCommonReportSuffix(); +} + +void RTCPInstance::addRR() { + // ASSERT: fSource != NULL + + enqueueCommonReportPrefix(RTCP_PT_RR, fSource->SSRC()); + enqueueCommonReportSuffix(); +} + +void RTCPInstance::enqueueCommonReportPrefix(unsigned char packetType, + unsigned SSRC, + unsigned numExtraWords) { + unsigned numReportingSources; + if (fSource == NULL) { + numReportingSources = 0; // we don't receive anything + } else { + RTPReceptionStatsDB& allReceptionStats + = fSource->receptionStatsDB(); + numReportingSources = allReceptionStats.numActiveSourcesSinceLastReset(); + // This must be <32, to fit in 5 bits: + if (numReportingSources >= 32) { numReportingSources = 32; } + // Later: support adding more reports to handle >32 sources (unlikely)##### + } + + unsigned rtcpHdr = 0x80000000; // version 2, no padding + rtcpHdr |= (numReportingSources<<24); + rtcpHdr |= (packetType<<16); + rtcpHdr |= (1 + numExtraWords + 6*numReportingSources); + // each report block is 6 32-bit words long + fOutBuf->enqueueWord(rtcpHdr); + + fOutBuf->enqueueWord(SSRC); +} + +void RTCPInstance::enqueueCommonReportSuffix() { + // Output the report blocks for each source: + if (fSource != NULL) { + RTPReceptionStatsDB& allReceptionStats + = fSource->receptionStatsDB(); + + RTPReceptionStatsDB::Iterator iterator(allReceptionStats); + while (1) { + RTPReceptionStats* receptionStats = iterator.next(); + if (receptionStats == NULL) break; + enqueueReportBlock(receptionStats); + } + + allReceptionStats.reset(); // because we have just generated a report + } +} + +void +RTCPInstance::enqueueReportBlock(RTPReceptionStats* stats) { + fOutBuf->enqueueWord(stats->SSRC()); + + unsigned highestExtSeqNumReceived = stats->highestExtSeqNumReceived(); + + unsigned totNumExpected + = highestExtSeqNumReceived - stats->baseExtSeqNumReceived(); + int totNumLost = totNumExpected - stats->totNumPacketsReceived(); + // 'Clamp' this loss number to a 24-bit signed value: + if (totNumLost > 0x007FFFFF) { + totNumLost = 0x007FFFFF; + } else if (totNumLost < 0) { + if (totNumLost < -0x00800000) totNumLost = 0x00800000; // unlikely, but... + totNumLost &= 0x00FFFFFF; + } + + unsigned numExpectedSinceLastReset + = highestExtSeqNumReceived - stats->lastResetExtSeqNumReceived(); + int numLostSinceLastReset + = numExpectedSinceLastReset - stats->numPacketsReceivedSinceLastReset(); + unsigned char lossFraction; + if (numExpectedSinceLastReset == 0 || numLostSinceLastReset < 0) { + lossFraction = 0; + } else { + lossFraction = (unsigned char) + ((numLostSinceLastReset << 8) / numExpectedSinceLastReset); + } + + fOutBuf->enqueueWord((lossFraction<<24) | totNumLost); + fOutBuf->enqueueWord(highestExtSeqNumReceived); + + fOutBuf->enqueueWord(stats->jitter()); + + unsigned NTPmsw = stats->lastReceivedSR_NTPmsw(); + unsigned NTPlsw = stats->lastReceivedSR_NTPlsw(); + unsigned LSR = ((NTPmsw&0xFFFF)<<16)|(NTPlsw>>16); // middle 32 bits + fOutBuf->enqueueWord(LSR); + + // Figure out how long has elapsed since the last SR rcvd from this src: + struct timeval const& LSRtime = stats->lastReceivedSR_time(); // "last SR" + struct timeval timeNow, timeSinceLSR; + gettimeofday(&timeNow, NULL); + if (timeNow.tv_usec < LSRtime.tv_usec) { + timeNow.tv_usec += 1000000; + timeNow.tv_sec -= 1; + } + timeSinceLSR.tv_sec = timeNow.tv_sec - LSRtime.tv_sec; + timeSinceLSR.tv_usec = timeNow.tv_usec - LSRtime.tv_usec; + // The enqueued time is in units of 1/65536 seconds. + // (Note that 65536/1000000 == 1024/15625) + unsigned DLSR; + if (LSR == 0) { + DLSR = 0; + } else { + DLSR = (timeSinceLSR.tv_sec<<16) + | ( (((timeSinceLSR.tv_usec<<11)+15625)/31250) & 0xFFFF); + } + fOutBuf->enqueueWord(DLSR); +} + +void RTCPInstance::addSDES() { + // For now we support only the CNAME item; later support more ##### + + // Begin by figuring out the size of the entire SDES report: + unsigned numBytes = 4; + // counts the SSRC, but not the header; it'll get subtracted out + numBytes += fCNAME.totalSize(); // includes id and length + numBytes += 1; // the special END item + + unsigned num4ByteWords = (numBytes + 3)/4; + + unsigned rtcpHdr = 0x81000000; // version 2, no padding, 1 SSRC chunk + rtcpHdr |= (RTCP_PT_SDES<<16); + rtcpHdr |= num4ByteWords; + fOutBuf->enqueueWord(rtcpHdr); + + if (fSource != NULL) { + fOutBuf->enqueueWord(fSource->SSRC()); + } else if (fSink != NULL) { + fOutBuf->enqueueWord(fSink->SSRC()); + } + + // Add the CNAME: + fOutBuf->enqueue(fCNAME.data(), fCNAME.totalSize()); + + // Add the 'END' item (i.e., a zero byte), plus any more needed to pad: + unsigned numPaddingBytesNeeded = 4 - (fOutBuf->curPacketSize() % 4); + unsigned char const zero = '\0'; + while (numPaddingBytesNeeded-- > 0) fOutBuf->enqueue(&zero, 1); +} + +void RTCPInstance::addBYE() { + unsigned rtcpHdr = 0x81000000; // version 2, no padding, 1 SSRC + rtcpHdr |= (RTCP_PT_BYE<<16); + rtcpHdr |= 1; // 2 32-bit words total (i.e., with 1 SSRC) + fOutBuf->enqueueWord(rtcpHdr); + + if (fSource != NULL) { + fOutBuf->enqueueWord(fSource->SSRC()); + } else if (fSink != NULL) { + fOutBuf->enqueueWord(fSink->SSRC()); + } +} + +void RTCPInstance::schedule(double nextTime) { + fNextReportTime = nextTime; + + double secondsToDelay = nextTime - dTimeNow(); +#ifdef DEBUG + fprintf(stderr, "schedule(%f->%f)\n", secondsToDelay, nextTime); +#endif + int usToGo = (int)(secondsToDelay * 1000000); + nextTask() = envir().taskScheduler().scheduleDelayedTask(usToGo, + (TaskFunc*)RTCPInstance::onExpire, this); +} + +void RTCPInstance::reschedule(double nextTime) { + envir().taskScheduler().unscheduleDelayedTask(nextTask()); + schedule(nextTime); +} + +void RTCPInstance::onExpire1() { + // Note: fTotSessionBW is kbits per second + double rtcpBW = 0.05*fTotSessionBW*1024/8; // -> bytes per second + + OnExpire(this, // event + numMembers(), // members + (fSink != NULL) ? 1 : 0, // senders + rtcpBW, // rtcp_bw + (fSink != NULL) ? 1 : 0, // we_sent + &fAveRTCPSize, // ave_rtcp_size + &fIsInitial, // initial + dTimeNow(), // tc + &fPrevReportTime, // tp + &fPrevNumMembers // pmembers + ); +} + +////////// SDESItem ////////// + +SDESItem::SDESItem(unsigned char tag, unsigned char const* value) { + unsigned length = strlen((char const*)value); + if (length > 0xFF) length = 0xFF; // maximum data length for a SDES item + + fData[0] = tag; + fData[1] = (unsigned char)length; + memmove(&fData[2], value, length); +} + +unsigned SDESItem::totalSize() const { + return 2 + (unsigned)fData[1]; +} + + +////////// Implementation of routines imported by the "rtcp_from_spec" C code + +extern "C" void Schedule(double nextTime, event e) { + RTCPInstance* instance = (RTCPInstance*)e; + if (instance == NULL) return; + + instance->schedule(nextTime); +} + +extern "C" void Reschedule(double nextTime, event e) { + RTCPInstance* instance = (RTCPInstance*)e; + if (instance == NULL) return; + + instance->reschedule(nextTime); +} + +extern "C" void SendRTCPReport(event e) { + RTCPInstance* instance = (RTCPInstance*)e; + if (instance == NULL) return; + + instance->sendReport(); +} + +extern "C" void SendBYEPacket(event e) { + RTCPInstance* instance = (RTCPInstance*)e; + if (instance == NULL) return; + + instance->sendBYE(); +} + +extern "C" int TypeOfEvent(event e) { + RTCPInstance* instance = (RTCPInstance*)e; + if (instance == NULL) return EVENT_UNKNOWN; + + return instance->typeOfEvent(); +} + +extern "C" int SentPacketSize(event e) { + RTCPInstance* instance = (RTCPInstance*)e; + if (instance == NULL) return 0; + + return instance->sentPacketSize(); +} + +extern "C" int PacketType(packet p) { + RTCPInstance* instance = (RTCPInstance*)p; + if (instance == NULL) return PACKET_UNKNOWN_TYPE; + + return instance->packetType(); +} + +extern "C" int ReceivedPacketSize(packet p) { + RTCPInstance* instance = (RTCPInstance*)p; + if (instance == NULL) return 0; + + return instance->receivedPacketSize(); +} + +extern "C" int NewMember(packet p) { + RTCPInstance* instance = (RTCPInstance*)p; + if (instance == NULL) return 0; + + return instance->checkNewSSRC(); +} + +extern "C" int NewSender(packet /*p*/) { + return 0; // we don't yet recognize senders other than ourselves ##### +} + +extern "C" void AddMember(packet /*p*/) { + // Do nothing; all of the real work was done when NewMember() was called +} + +extern "C" void AddSender(packet /*p*/) { + // we don't yet recognize senders other than ourselves ##### +} + +extern "C" void RemoveMember(packet p) { + RTCPInstance* instance = (RTCPInstance*)p; + if (instance == NULL) return; + + instance->removeLastReceivedSSRC(); +} + +extern "C" void RemoveSender(packet /*p*/) { + // we don't yet recognize senders other than ourselves ##### +} + +extern "C" double drand30() { + unsigned tmp = our_random()&0x3FFFFFFF; // a random 30-bit integer + return tmp/(double)(1024*1024*1024); +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/rtcp_from_spec.c kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/rtcp_from_spec.c --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/rtcp_from_spec.c 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/rtcp_from_spec.c 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,289 @@ +/* RTCP code taken directly from the most recent RTP specification: + * RFC 3550 + * Implementation + */ + +#include "rtcp_from_spec.h" + +/***** + +A.7 Computing the RTCP Transmission Interval + + The following functions implement the RTCP transmission and reception + rules described in Section 6.2. These rules are coded in several + functions: + + o rtcp_interval() computes the deterministic calculated + interval, measured in seconds. The parameters are defined in + Section 6.3. + + o OnExpire() is called when the RTCP transmission timer expires. + + o OnReceive() is called whenever an RTCP packet is received. + + Both OnExpire() and OnReceive() have event e as an argument. This is + the next scheduled event for that participant, either an RTCP report + or a BYE packet. It is assumed that the following functions are + available: + + o Schedule(time t, event e) schedules an event e to occur at + time t. When time t arrives, the function OnExpire is called + with e as an argument. + + o Reschedule(time t, event e) reschedules a previously scheduled + event e for time t. + + o SendRTCPReport(event e) sends an RTCP report. + + o SendBYEPacket(event e) sends a BYE packet. + + o TypeOfEvent(event e) returns EVENT_BYE if the event being + processed is for a BYE packet to be sent, else it returns + EVENT_REPORT. + + o PacketType(p) returns PACKET_RTCP_REPORT if packet p is an + RTCP report (not BYE), PACKET_BYE if its a BYE RTCP packet, + and PACKET_RTP if its a regular RTP data packet. + + o ReceivedPacketSize() and SentPacketSize() return the size of + the referenced packet in octets. + + o NewMember(p) returns a 1 if the participant who sent packet p + is not currently in the member list, 0 otherwise. Note this + function is not sufficient for a complete implementation + because each CSRC identifier in an RTP packet and each SSRC in + a BYE packet should be processed. + + o NewSender(p) returns a 1 if the participant who sent packet p + is not currently in the sender sublist of the member list, 0 + otherwise. + + o AddMember() and RemoveMember() to add and remove participants + from the member list. + + o AddSender() and RemoveSender() to add and remove participants + from the sender sublist of the member list. +*****/ + + + double rtcp_interval(int members, + int senders, + double rtcp_bw, + int we_sent, + double avg_rtcp_size, + int initial) + { + /* + * Minimum average time between RTCP packets from this site (in + * seconds). This time prevents the reports from `clumping' when + * sessions are small and the law of large numbers isn't helping + * to smooth out the traffic. It also keeps the report interval + * from becoming ridiculously small during transient outages like + * a network partition. + */ + double const RTCP_MIN_TIME = 5.; + /* + * Fraction of the RTCP bandwidth to be shared among active + * senders. (This fraction was chosen so that in a typical + * session with one or two active senders, the computed report + * time would be roughly equal to the minimum report time so that + * we don't unnecessarily slow down receiver reports.) The + * receiver fraction must be 1 - the sender fraction. + */ + double const RTCP_SENDER_BW_FRACTION = 0.25; + double const RTCP_RCVR_BW_FRACTION = (1-RTCP_SENDER_BW_FRACTION); + /* + * To compensate for "unconditional reconsideration" converging to a + * value below the intended average. + */ + double const COMPENSATION = 2.71828 - 1.5; + + double t; /* interval */ + double rtcp_min_time = RTCP_MIN_TIME; + int n; /* no. of members for computation */ + + /* + * Very first call at application start-up uses half the min + * delay for quicker notification while still allowing some time + * before reporting for randomization and to learn about other + * sources so the report interval will converge to the correct + * interval more quickly. + */ + if (initial) { + rtcp_min_time /= 2; + } + + /* + * If there were active senders, give them at least a minimum + * share of the RTCP bandwidth. Otherwise all participants share + * the RTCP bandwidth equally. + */ + n = members; + if (senders > 0 && senders < members * RTCP_SENDER_BW_FRACTION) { + if (we_sent) { + rtcp_bw *= RTCP_SENDER_BW_FRACTION; + n = senders; + } else { + rtcp_bw *= RTCP_RCVR_BW_FRACTION; + n -= senders; + } + } + + /* + * The effective number of sites times the average packet size is + * the total number of octets sent when each site sends a report. + * Dividing this by the effective bandwidth gives the time + * interval over which those packets must be sent in order to + * meet the bandwidth target, with a minimum enforced. In that + * time interval we send one report so this time is also our + * average time between reports. + */ + t = avg_rtcp_size * n / rtcp_bw; + if (t < rtcp_min_time) t = rtcp_min_time; + + /* + * To avoid traffic bursts from unintended synchronization with + * other sites, we then pick our actual next report interval as a + * random number uniformly distributed between 0.5*t and 1.5*t. + */ + t = t * (drand48() + 0.5); + t = t / COMPENSATION; + return t; + } + + void OnExpire(event e, + int members, + int senders, + double rtcp_bw, + int we_sent, + double *avg_rtcp_size, + int *initial, + time_tp tc, + time_tp *tp, + int *pmembers) + { + /* This function is responsible for deciding whether to send + * an RTCP report or BYE packet now, or to reschedule transmission. + * It is also responsible for updating the pmembers, initial, tp, + * and avg_rtcp_size state variables. This function should be called + * upon expiration of the event timer used by Schedule(). */ + + double t; /* Interval */ + double tn; /* Next transmit time */ + + /* In the case of a BYE, we use "unconditional reconsideration" to + * reschedule the transmission of the BYE if necessary */ + + if (TypeOfEvent(e) == EVENT_BYE) { + t = rtcp_interval(members, + senders, + rtcp_bw, + we_sent, + *avg_rtcp_size, + *initial); + tn = *tp + t; + if (tn <= tc) { + SendBYEPacket(e); + exit(1); + } else { + Schedule(tn, e); + } + + } else if (TypeOfEvent(e) == EVENT_REPORT) { + t = rtcp_interval(members, + senders, + rtcp_bw, + we_sent, + *avg_rtcp_size, + *initial); + tn = *tp + t; + + if (tn <= tc) { + SendRTCPReport(e); + *avg_rtcp_size = (1./16.)*SentPacketSize(e) + + (15./16.)*(*avg_rtcp_size); + *tp = tc; + + /* We must redraw the interval. Don't reuse the + one computed above, since its not actually + distributed the same, as we are conditioned + on it being small enough to cause a packet to + be sent */ + + t = rtcp_interval(members, + senders, + rtcp_bw, + we_sent, + *avg_rtcp_size, + *initial); + + Schedule(t+tc,e); + *initial = 0; + } else { + Schedule(tn, e); + } + *pmembers = members; + } + } + + + void OnReceive(packet p, + event e, + int *members, + int *pmembers, + int *senders, + double *avg_rtcp_size, + double *tp, + double tc, + double tn) + { + /* What we do depends on whether we have left the group, and + * are waiting to send a BYE (TypeOfEvent(e) == EVENT_BYE) or + * an RTCP report. p represents the packet that was just received. */ + + if (PacketType(p) == PACKET_RTCP_REPORT) { + if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) { + AddMember(p); + *members += 1; + } + *avg_rtcp_size = (1./16.)*ReceivedPacketSize(p) + + (15./16.)*(*avg_rtcp_size); + } else if (PacketType(p) == PACKET_RTP) { + if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) { + AddMember(p); + *members += 1; + } + if (NewSender(p) && (TypeOfEvent(e) == EVENT_REPORT)) { + AddSender(p); + *senders += 1; + } + } else if (PacketType(p) == PACKET_BYE) { + *avg_rtcp_size = (1./16.)*ReceivedPacketSize(p) + + (15./16.)*(*avg_rtcp_size); + + if (TypeOfEvent(e) == EVENT_REPORT) { + if (NewSender(p) == FALSE) { + RemoveSender(p); + *senders -= 1; + } + + if (NewMember(p) == FALSE) { + RemoveMember(p); + *members -= 1; + } + + if(*members < *pmembers) { + tn = tc + (((double) *members)/(*pmembers))*(tn - tc); + *tp = tc - (((double) *members)/(*pmembers))*(tc - *tp); + + /* Reschedule the next report for time tn */ + + Reschedule(tn, e); + *pmembers = *members; + } + + } else if (TypeOfEvent(e) == EVENT_BYE) { + *members += 1; + } + } + } diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/rtcp_from_spec.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/rtcp_from_spec.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/rtcp_from_spec.h 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/rtcp_from_spec.h 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,82 @@ +/* RTCP code taken directly from the most recent RTP specification: + * draft-ietf-avt-rtp-new-11.txt + * C header + */ + +#ifndef _RTCP_FROM_SPEC_H +#define _RTCP_FROM_SPEC_H + +#include + +/* Definitions of _ANSI_ARGS and EXTERN that will work in either + C or C++ code: + */ +#undef _ANSI_ARGS_ +#if ((defined(__STDC__) || defined(SABER)) && !defined(NO_PROTOTYPE)) || defined(__cplusplus) || defined(USE_PROTOTYPE) +# define _ANSI_ARGS_(x) x +#else +# define _ANSI_ARGS_(x) () +#endif +#ifdef __cplusplus +# define EXTERN extern "C" +#else +# define EXTERN extern +#endif + +/* The code from the spec assumes a type "event"; make this a void*: */ +typedef void* event; + +#define EVENT_UNKNOWN 0 +#define EVENT_REPORT 1 +#define EVENT_BYE 2 + +/* The code from the spec assumes a type "time_tp"; make this a double: */ +typedef double time_tp; + +/* The code from the spec assumes a type "packet"; make this a void*: */ +typedef void* packet; + +#define PACKET_UNKNOWN_TYPE 0 +#define PACKET_RTP 1 +#define PACKET_RTCP_REPORT 2 +#define PACKET_BYE 3 +#define PACKET_RTCP_APP 4 + +/* The code from the spec calls drand48(), but we have drand30() instead */ +#define drand48 drand30 + +/* The code calls "exit()", but we don't want to exit, so make it a noop: */ +#define exit(n) do {} while (0) + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +/* EXPORTS: */ + +EXTERN void OnExpire _ANSI_ARGS_((event, int, int, double, int, double*, int*, time_tp, time_tp*, int*)); + +EXTERN void OnReceive _ANSI_ARGS_((packet, event, int*, int*, int*, double*, double*, double, double)); + +/* IMPORTS: */ + +EXTERN void Schedule _ANSI_ARGS_((double,event)); +EXTERN void Reschedule _ANSI_ARGS_((double,event)); +EXTERN void SendRTCPReport _ANSI_ARGS_((event)); +EXTERN void SendBYEPacket _ANSI_ARGS_((event)); +EXTERN int TypeOfEvent _ANSI_ARGS_((event)); +EXTERN int SentPacketSize _ANSI_ARGS_((event)); +EXTERN int PacketType _ANSI_ARGS_((packet)); +EXTERN int ReceivedPacketSize _ANSI_ARGS_((packet)); +EXTERN int NewMember _ANSI_ARGS_((packet)); +EXTERN int NewSender _ANSI_ARGS_((packet)); +EXTERN void AddMember _ANSI_ARGS_((packet)); +EXTERN void AddSender _ANSI_ARGS_((packet)); +EXTERN void RemoveMember _ANSI_ARGS_((packet)); +EXTERN void RemoveSender _ANSI_ARGS_((packet)); +EXTERN double drand30 _ANSI_ARGS_((void)); + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTPInterface.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTPInterface.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTPInterface.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTPInterface.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,401 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// An abstraction of a network interface used for RTP (or RTCP). +// (This allows the RTP-over-TCP hack (RFC 2326, section 10.12) to +// be implemented transparently.) +// Implementation + +#include "RTPInterface.hh" +#include +#include + +////////// Helper Functions - Definition ////////// + +// Helper routines and data structures, used to implement +// sending/receiving RTP/RTCP over a TCP socket: + +static void sendRTPOverTCP(unsigned char* packet, unsigned packetSize, + int socketNum, unsigned char streamChannelId); + +// Reading RTP-over-TCP is implemented using two levels of hash tables. +// The top-level hash table maps TCP socket numbers to a +// "SocketDescriptor" that contains a hash table for each of the +// sub-channels that are reading from this socket. + +static HashTable* socketHashTable(UsageEnvironment& env) { + _Tables* ourTables = _Tables::getOurTables(env); + if (ourTables->socketTable == NULL) { + // Create a new socket number -> SocketDescriptor mapping table: + ourTables->socketTable = HashTable::create(ONE_WORD_HASH_KEYS); + } + return (HashTable*)(ourTables->socketTable); +} + +class SocketDescriptor { +public: + SocketDescriptor(UsageEnvironment& env, int socketNum); + virtual ~SocketDescriptor(); + + void registerRTPInterface(unsigned char streamChannelId, + RTPInterface* rtpInterface); + RTPInterface* lookupRTPInterface(unsigned char streamChannelId); + void deregisterRTPInterface(unsigned char streamChannelId); + + void setServerRequestAlternativeByteHandler(ServerRequestAlternativeByteHandler* handler, void* clientData) { + fServerRequestAlternativeByteHandler = handler; + fServerRequestAlternativeByteHandlerClientData = clientData; + } + +private: + static void tcpReadHandler(SocketDescriptor*, int mask); + +private: + UsageEnvironment& fEnv; + int fOurSocketNum; + HashTable* fSubChannelHashTable; + ServerRequestAlternativeByteHandler* fServerRequestAlternativeByteHandler; + void* fServerRequestAlternativeByteHandlerClientData; +}; + +static SocketDescriptor* lookupSocketDescriptor(UsageEnvironment& env, int sockNum, Boolean createIfNotFound = True) { + char const* key = (char const*)(long)sockNum; + SocketDescriptor* socketDescriptor = (SocketDescriptor*)(socketHashTable(env)->Lookup(key)); + if (socketDescriptor == NULL && createIfNotFound) { + socketDescriptor = new SocketDescriptor(env, sockNum); + socketHashTable(env)->Add((char const*)(long)(sockNum), socketDescriptor); + } + + return socketDescriptor; +} + +static void removeSocketDescription(UsageEnvironment& env, int sockNum) { + char const* key = (char const*)(long)sockNum; + HashTable* table = socketHashTable(env); + table->Remove(key); + + if (table->IsEmpty()) { + // We can also delete the table (to reclaim space): + _Tables* ourTables = _Tables::getOurTables(env); + delete table; + ourTables->socketTable = NULL; + ourTables->reclaimIfPossible(); + } +} + + +////////// RTPInterface - Implementation ////////// + +RTPInterface::RTPInterface(Medium* owner, Groupsock* gs) + : fOwner(owner), fGS(gs), + fTCPStreams(NULL), + fNextTCPReadSize(0), fNextTCPReadStreamSocketNum(-1), + fNextTCPReadStreamChannelId(0xFF), fReadHandlerProc(NULL), + fAuxReadHandlerFunc(NULL), fAuxReadHandlerClientData(NULL) { + // Make the socket non-blocking, even though it will be read from only asynchronously, when packets arrive. + // The reason for this is that, in some OSs, reads on a blocking socket can (allegedly) sometimes block, + // even if the socket was previously reported (e.g., by "select()") as having data available. + // (This can supposedly happen if the UDP checksum fails, for example.) + makeSocketNonBlocking(fGS->socketNum()); + increaseSendBufferTo(envir(), fGS->socketNum(), 50*1024); +} + +RTPInterface::~RTPInterface() { + delete fTCPStreams; +} + +void RTPInterface::setStreamSocket(int sockNum, + unsigned char streamChannelId) { + fGS->removeAllDestinations(); + addStreamSocket(sockNum, streamChannelId); +} + +void RTPInterface::addStreamSocket(int sockNum, + unsigned char streamChannelId) { + if (sockNum < 0) return; + + for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; + streams = streams->fNext) { + if (streams->fStreamSocketNum == sockNum + && streams->fStreamChannelId == streamChannelId) { + return; // we already have it + } + } + + fTCPStreams = new tcpStreamRecord(sockNum, streamChannelId, fTCPStreams); +} + +static void deregisterSocket(UsageEnvironment& env, int sockNum, unsigned char streamChannelId) { + SocketDescriptor* socketDescriptor = lookupSocketDescriptor(env, sockNum, False); + if (socketDescriptor != NULL) { + socketDescriptor->deregisterRTPInterface(streamChannelId); + // Note: This may delete "socketDescriptor", + // if no more interfaces are using this socket + } +} + +void RTPInterface::removeStreamSocket(int sockNum, + unsigned char streamChannelId) { + for (tcpStreamRecord** streamsPtr = &fTCPStreams; *streamsPtr != NULL; + streamsPtr = &((*streamsPtr)->fNext)) { + if ((*streamsPtr)->fStreamSocketNum == sockNum + && (*streamsPtr)->fStreamChannelId == streamChannelId) { + deregisterSocket(envir(), sockNum, streamChannelId); + + // Then remove the record pointed to by *streamsPtr : + tcpStreamRecord* next = (*streamsPtr)->fNext; + (*streamsPtr)->fNext = NULL; + delete (*streamsPtr); + *streamsPtr = next; + return; + } + } +} + +void RTPInterface::setServerRequestAlternativeByteHandler(ServerRequestAlternativeByteHandler* handler, void* clientData) { + for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; + streams = streams->fNext) { + // Get (or create, if necessary) a socket descriptor for "streams->fStreamSocketNum": + SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams->fStreamSocketNum); + + socketDescriptor->setServerRequestAlternativeByteHandler(handler, clientData); + } +} + + +void RTPInterface::sendPacket(unsigned char* packet, unsigned packetSize) { + // Normal case: Send as a UDP packet: + fGS->output(envir(), fGS->ttl(), packet, packetSize); + + // Also, send over each of our TCP sockets: + for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; + streams = streams->fNext) { + sendRTPOverTCP(packet, packetSize, + streams->fStreamSocketNum, streams->fStreamChannelId); + } +} + +void RTPInterface +::startNetworkReading(TaskScheduler::BackgroundHandlerProc* handlerProc) { + // Normal case: Arrange to read UDP packets: + envir().taskScheduler(). + turnOnBackgroundReadHandling(fGS->socketNum(), handlerProc, fOwner); + + // Also, receive RTP over TCP, on each of our TCP connections: + fReadHandlerProc = handlerProc; + for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; + streams = streams->fNext) { + // Get a socket descriptor for "streams->fStreamSocketNum": + SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams->fStreamSocketNum); + + // Tell it about our subChannel: + socketDescriptor->registerRTPInterface(streams->fStreamChannelId, this); + } +} + +Boolean RTPInterface::handleRead(unsigned char* buffer, + unsigned bufferMaxSize, + unsigned& bytesRead, + struct sockaddr_in& fromAddress) { + Boolean readSuccess; + if (fNextTCPReadStreamSocketNum < 0) { + // Normal case: read from the (datagram) 'groupsock': + readSuccess = fGS->handleRead(buffer, bufferMaxSize, bytesRead, fromAddress); + } else { + // Read from the TCP connection: + bytesRead = 0; + unsigned totBytesToRead = fNextTCPReadSize; + if (totBytesToRead > bufferMaxSize) totBytesToRead = bufferMaxSize; + unsigned curBytesToRead = totBytesToRead; + int curBytesRead; + while ((curBytesRead = readSocket(envir(), fNextTCPReadStreamSocketNum, + &buffer[bytesRead], curBytesToRead, + fromAddress)) > 0) { + bytesRead += curBytesRead; + if (bytesRead >= totBytesToRead) break; + curBytesToRead -= curBytesRead; + } + if (curBytesRead <= 0) { + bytesRead = 0; + readSuccess = False; + } else { + readSuccess = True; + } + fNextTCPReadStreamSocketNum = -1; // default, for next time + } + + if (readSuccess && fAuxReadHandlerFunc != NULL) { + // Also pass the newly-read packet data to our auxilliary handler: + (*fAuxReadHandlerFunc)(fAuxReadHandlerClientData, buffer, bytesRead); + } + return readSuccess; +} + +void RTPInterface::stopNetworkReading() { + // Normal case + envir().taskScheduler().turnOffBackgroundReadHandling(fGS->socketNum()); + + // Also turn off read handling on each of our TCP connections: + for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; + streams = streams->fNext) { + deregisterSocket(envir(), streams->fStreamSocketNum, streams->fStreamChannelId); + } +} + + +////////// Helper Functions - Implementation ///////// + +void sendRTPOverTCP(unsigned char* packet, unsigned packetSize, + int socketNum, unsigned char streamChannelId) { +#ifdef DEBUG + fprintf(stderr, "sendRTPOverTCP: %d bytes over channel %d (socket %d)\n", + packetSize, streamChannelId, socketNum); fflush(stderr); +#endif + // Send RTP over TCP, using the encoding defined in + // RFC 2326, section 10.12: + do { + char const dollar = '$'; + if (send(socketNum, &dollar, 1, 0) != 1) break; + if (send(socketNum, (char*)&streamChannelId, 1, 0) != 1) break; + + char netPacketSize[2]; + netPacketSize[0] = (char) ((packetSize&0xFF00)>>8); + netPacketSize[1] = (char) (packetSize&0xFF); + if (send(socketNum, netPacketSize, 2, 0) != 2) break; + + if (send(socketNum, (char*)packet, packetSize, 0) != (int)packetSize) break; + +#ifdef DEBUG + fprintf(stderr, "sendRTPOverTCP: completed\n"); fflush(stderr); +#endif + + return; + } while (0); + +#ifdef DEBUG + fprintf(stderr, "sendRTPOverTCP: failed!\n"); fflush(stderr); +#endif +} + +SocketDescriptor::SocketDescriptor(UsageEnvironment& env, int socketNum) + :fEnv(env), fOurSocketNum(socketNum), + fSubChannelHashTable(HashTable::create(ONE_WORD_HASH_KEYS)), + fServerRequestAlternativeByteHandler(NULL), fServerRequestAlternativeByteHandlerClientData(NULL) { +} + +SocketDescriptor::~SocketDescriptor() { + delete fSubChannelHashTable; +} + +void SocketDescriptor::registerRTPInterface(unsigned char streamChannelId, + RTPInterface* rtpInterface) { + Boolean isFirstRegistration = fSubChannelHashTable->IsEmpty(); + fSubChannelHashTable->Add((char const*)(long)streamChannelId, + rtpInterface); + + if (isFirstRegistration) { + // Arrange to handle reads on this TCP socket: + TaskScheduler::BackgroundHandlerProc* handler + = (TaskScheduler::BackgroundHandlerProc*)&tcpReadHandler; + fEnv.taskScheduler(). + turnOnBackgroundReadHandling(fOurSocketNum, handler, this); + } +} + +RTPInterface* SocketDescriptor +::lookupRTPInterface(unsigned char streamChannelId) { + char const* lookupArg = (char const*)(long)streamChannelId; + return (RTPInterface*)(fSubChannelHashTable->Lookup(lookupArg)); +} + +void SocketDescriptor +::deregisterRTPInterface(unsigned char streamChannelId) { + fSubChannelHashTable->Remove((char const*)(long)streamChannelId); + + if (fSubChannelHashTable->IsEmpty()) { + // No more interfaces are using us, so it's curtains for us now + fEnv.taskScheduler().turnOffBackgroundReadHandling(fOurSocketNum); + removeSocketDescription(fEnv, fOurSocketNum); + delete this; + } +} + +void SocketDescriptor::tcpReadHandler(SocketDescriptor* socketDescriptor, int mask) { + do { + UsageEnvironment& env = socketDescriptor->fEnv; // abbrev + int socketNum = socketDescriptor->fOurSocketNum; + + // Begin by reading any characters that aren't '$'. Any such characters are regular RTSP commands or responses, + // which need to be handled separately. + unsigned char c; + struct sockaddr_in fromAddress; + struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; + while (1) { + int result = readSocket(env, socketNum, &c, 1, fromAddress, &timeout); + if (result != 1) { // error reading TCP socket + if (result < 0) { + env.taskScheduler().turnOffBackgroundReadHandling(socketNum); // stops further calls to us + } + return; + } + + if (c == '$') break; + if (socketDescriptor->fServerRequestAlternativeByteHandler != NULL) { + (*socketDescriptor->fServerRequestAlternativeByteHandler)(socketDescriptor->fServerRequestAlternativeByteHandlerClientData, c); + } + } + + // The next byte is the stream channel id: + unsigned char streamChannelId; + if (readSocket(env, socketNum, &streamChannelId, 1, fromAddress) != 1) break; + RTPInterface* rtpInterface = socketDescriptor->lookupRTPInterface(streamChannelId); + if (rtpInterface == NULL) break; // we're not interested in this channel + + // The next two bytes are the RTP or RTCP packet size (in network order) + unsigned short size; + if (readSocketExact(env, socketNum, (unsigned char*)&size, 2, + fromAddress) != 2) break; + rtpInterface->fNextTCPReadSize = ntohs(size); + rtpInterface->fNextTCPReadStreamSocketNum = socketNum; + rtpInterface->fNextTCPReadStreamChannelId = streamChannelId; +#ifdef DEBUG + fprintf(stderr, "SocketDescriptor::tcpReadHandler() reading %d bytes on channel %d\n", rtpInterface->fNextTCPReadSize, streamChannelId); +#endif + + // Now that we have the data set up, call this subchannel's + // read handler: + if (rtpInterface->fReadHandlerProc != NULL) { + rtpInterface->fReadHandlerProc(rtpInterface->fOwner, mask); + } + + } while (0); +} + + +////////// tcpStreamRecord implementation ////////// + +tcpStreamRecord +::tcpStreamRecord(int streamSocketNum, unsigned char streamChannelId, + tcpStreamRecord* next) + : fNext(next), + fStreamSocketNum(streamSocketNum), fStreamChannelId(streamChannelId) { +} + +tcpStreamRecord::~tcpStreamRecord() { + delete fNext; +} + diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTPSink.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTPSink.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTPSink.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTPSink.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,355 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTP Sinks +// Implementation + +#include "RTPSink.hh" +#include "GroupsockHelper.hh" + +////////// RTPSink ////////// + +Boolean RTPSink::lookupByName(UsageEnvironment& env, char const* sinkName, + RTPSink*& resultSink) { + resultSink = NULL; // unless we succeed + + MediaSink* sink; + if (!MediaSink::lookupByName(env, sinkName, sink)) return False; + + if (!sink->isRTPSink()) { + env.setResultMsg(sinkName, " is not a RTP sink"); + return False; + } + + resultSink = (RTPSink*)sink; + return True; +} + +Boolean RTPSink::isRTPSink() const { + return True; +} + +RTPSink::RTPSink(UsageEnvironment& env, + Groupsock* rtpGS, unsigned char rtpPayloadType, + unsigned rtpTimestampFrequency, + char const* rtpPayloadFormatName, + unsigned numChannels) + : MediaSink(env), fRTPInterface(this, rtpGS), + fRTPPayloadType(rtpPayloadType), + fPacketCount(0), fOctetCount(0), fTotalOctetCount(0), + fTimestampFrequency(rtpTimestampFrequency), fNextTimestampHasBeenPreset(True), + fNumChannels(numChannels), + fCurrentTimestamp(0) +{ + fRTPPayloadFormatName + = strDup(rtpPayloadFormatName == NULL ? "???" : rtpPayloadFormatName); + gettimeofday(&fCreationTime, NULL); + fTotalOctetCountStartTime = fCreationTime; + + fSeqNo = (u_int16_t)our_random(); + fSSRC = our_random32(); + fTimestampBase = our_random32(); + + fTransmissionStatsDB = new RTPTransmissionStatsDB(*this); +} + +RTPSink::~RTPSink() { + delete fTransmissionStatsDB; + delete[] (char*)fRTPPayloadFormatName; +} + +u_int32_t RTPSink::convertToRTPTimestamp(struct timeval tv) { + // Begin by converting from "struct timeval" units to RTP timestamp units: + u_int32_t timestampIncrement = (fTimestampFrequency*tv.tv_sec); + timestampIncrement += (u_int32_t)((2.0*fTimestampFrequency*tv.tv_usec + 1000000.0)/2000000); + // note: rounding + + // Then add this to our 'timestamp base': + if (fNextTimestampHasBeenPreset) { + // Make the returned timestamp the same as the current "fTimestampBase", + // so that timestamps begin with the value that was previously preset: + fTimestampBase -= timestampIncrement; + fNextTimestampHasBeenPreset = False; + } + + u_int32_t const rtpTimestamp = fTimestampBase + timestampIncrement; +#ifdef DEBUG_TIMESTAMPS + fprintf(stderr, "fTimestampBase: 0x%08x, tv: %lu.%06ld\n\t=> RTP timestamp: 0x%08x\n", + fTimestampBase, tv.tv_sec, tv.tv_usec, rtpTimestamp); + fflush(stderr); +#endif + + return rtpTimestamp; +} + +u_int32_t RTPSink::presetNextTimestamp() { + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + + u_int32_t tsNow = convertToRTPTimestamp(timeNow); + fTimestampBase = tsNow; + fNextTimestampHasBeenPreset = True; + + return tsNow; +} + +void RTPSink::getTotalBitrate(unsigned& outNumBytes, double& outElapsedTime) { + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + + outNumBytes = fTotalOctetCount; + outElapsedTime = (double)(timeNow.tv_sec-fTotalOctetCountStartTime.tv_sec) + + (timeNow.tv_usec-fTotalOctetCountStartTime.tv_usec)/1000000.0; + + fTotalOctetCount = 0; + fTotalOctetCountStartTime = timeNow; +} + +char const* RTPSink::sdpMediaType() const { + return "data"; + // default SDP media (m=) type, unless redefined by subclasses +} + +char* RTPSink::rtpmapLine() const { + if (rtpPayloadType() >= 96) { // the payload format type is dynamic + char* encodingParamsPart; + if (numChannels() != 1) { + encodingParamsPart = new char[1 + 20 /* max int len */]; + sprintf(encodingParamsPart, "/%d", numChannels()); + } else { + encodingParamsPart = strDup(""); + } + char const* const rtpmapFmt = "a=rtpmap:%d %s/%d%s\r\n"; + unsigned rtpmapFmtSize = strlen(rtpmapFmt) + + 3 /* max char len */ + strlen(rtpPayloadFormatName()) + + 20 /* max int len */ + strlen(encodingParamsPart); + char* rtpmapLine = new char[rtpmapFmtSize]; + sprintf(rtpmapLine, rtpmapFmt, + rtpPayloadType(), rtpPayloadFormatName(), + rtpTimestampFrequency(), encodingParamsPart); + delete[] encodingParamsPart; + + return rtpmapLine; + } else { + // The payload format is staic, so there's no "a=rtpmap:" line: + return strDup(""); + } +} + +char const* RTPSink::auxSDPLine() { + return NULL; // by default +} + + +////////// RTPTransmissionStatsDB ////////// + +RTPTransmissionStatsDB::RTPTransmissionStatsDB(RTPSink& rtpSink) + : fOurRTPSink(rtpSink), + fTable(HashTable::create(ONE_WORD_HASH_KEYS)) { + fNumReceivers=0; +} + +RTPTransmissionStatsDB::~RTPTransmissionStatsDB() { + // First, remove and delete all stats records from the table: + RTPTransmissionStats* stats; + while ((stats = (RTPTransmissionStats*)fTable->RemoveNext()) != NULL) { + delete stats; + } + + // Then, delete the table itself: + delete fTable; +} + +void RTPTransmissionStatsDB +::noteIncomingRR(u_int32_t SSRC, struct sockaddr_in const& lastFromAddress, + unsigned lossStats, unsigned lastPacketNumReceived, + unsigned jitter, unsigned lastSRTime, unsigned diffSR_RRTime) { + RTPTransmissionStats* stats = lookup(SSRC); + if (stats == NULL) { + // This is the first time we've heard of this SSRC. + // Create a new record for it: + stats = new RTPTransmissionStats(fOurRTPSink, SSRC); + if (stats == NULL) return; + add(SSRC, stats); +#ifdef DEBUG_RR + fprintf(stderr, "Adding new entry for SSRC %x in RTPTransmissionStatsDB\n", SSRC); +#endif + } + + stats->noteIncomingRR(lastFromAddress, + lossStats, lastPacketNumReceived, jitter, + lastSRTime, diffSR_RRTime); +} + +void RTPTransmissionStatsDB::removeRecord(u_int32_t SSRC) { + RTPTransmissionStats* stats = lookup(SSRC); + if (stats != NULL) { + long SSRC_long = (long)SSRC; + fTable->Remove((char const*)SSRC_long); + --fNumReceivers; + delete stats; + } +} + +RTPTransmissionStatsDB::Iterator +::Iterator(RTPTransmissionStatsDB& receptionStatsDB) + : fIter(HashTable::Iterator::create(*(receptionStatsDB.fTable))) { +} + +RTPTransmissionStatsDB::Iterator::~Iterator() { + delete fIter; +} + +RTPTransmissionStats* +RTPTransmissionStatsDB::Iterator::next() { + char const* key; // dummy + + return (RTPTransmissionStats*)(fIter->next(key)); +} + +RTPTransmissionStats* RTPTransmissionStatsDB::lookup(u_int32_t SSRC) const { + long SSRC_long = (long)SSRC; + return (RTPTransmissionStats*)(fTable->Lookup((char const*)SSRC_long)); +} + +void RTPTransmissionStatsDB::add(u_int32_t SSRC, RTPTransmissionStats* stats) { + long SSRC_long = (long)SSRC; + fTable->Add((char const*)SSRC_long, stats); + ++fNumReceivers; +} + + +////////// RTPTransmissionStats ////////// + +RTPTransmissionStats::RTPTransmissionStats(RTPSink& rtpSink, u_int32_t SSRC) + : fOurRTPSink(rtpSink), fSSRC(SSRC), fLastPacketNumReceived(0), + fPacketLossRatio(0), fTotNumPacketsLost(0), fJitter(0), + fLastSRTime(0), fDiffSR_RRTime(0), fFirstPacket(True), + fTotalOctetCount_hi(0), fTotalOctetCount_lo(0), + fTotalPacketCount_hi(0), fTotalPacketCount_lo(0), + fOldValid(False), fOldLastPacketNumReceived(0), fOldTotNumPacketsLost(0), + fFirstPacketNumReported(0) +{ + gettimeofday(&fTimeCreated, NULL); + + fLastOctetCount = rtpSink.octetCount(); + fLastPacketCount = rtpSink.packetCount(); + memset(&fLastFromAddress, 0, sizeof(sockaddr_in)); + fTimeReceived.tv_sec = 0; + fTimeReceived.tv_usec = 0; +} + +RTPTransmissionStats::~RTPTransmissionStats() {} + +void RTPTransmissionStats +::noteIncomingRR(struct sockaddr_in const& lastFromAddress, + unsigned lossStats, unsigned lastPacketNumReceived, + unsigned jitter, unsigned lastSRTime, + unsigned diffSR_RRTime) { + if (fFirstPacket) { + fFirstPacket = False; + fFirstPacketNumReported = lastPacketNumReceived; + } else { + fOldValid = True; + fOldLastPacketNumReceived = fLastPacketNumReceived; + fOldTotNumPacketsLost = fTotNumPacketsLost; + } + gettimeofday(&fTimeReceived, NULL); + + fLastFromAddress = lastFromAddress; + fPacketLossRatio = lossStats>>24; + fTotNumPacketsLost = lossStats&0xFFFFFF; + fLastPacketNumReceived = lastPacketNumReceived; + fJitter = jitter; + fLastSRTime = lastSRTime; + fDiffSR_RRTime = diffSR_RRTime; +#ifdef DEBUG_RR + fprintf(stderr, "RTCP RR data (received at %lu.%06ld): lossStats 0x%08x, lastPacketNumReceived 0x%08x, jitter 0x%08x, lastSRTime 0x%08x, diffSR_RRTime 0x%08x\n", + fTimeReceived.tv_sec, fTimeReceived.tv_usec, lossStats, lastPacketNumReceived, jitter, lastSRTime, diffSR_RRTime); + unsigned rtd = roundTripDelay(); + fprintf(stderr, "=> round-trip delay: 0x%04x (== %f seconds)\n", rtd, rtd/65536.0); +#endif + + // Update our counts of the total number of octets and packets sent towards + // this receiver: + u_int32_t newOctetCount = fOurRTPSink.octetCount(); + u_int32_t octetCountDiff = newOctetCount - fLastOctetCount; + fLastOctetCount = newOctetCount; + u_int32_t prevTotalOctetCount_lo = fTotalOctetCount_lo; + fTotalOctetCount_lo += octetCountDiff; + if (fTotalOctetCount_lo < prevTotalOctetCount_lo) { // wrap around + ++fTotalOctetCount_hi; + } + + u_int32_t newPacketCount = fOurRTPSink.packetCount(); + u_int32_t packetCountDiff = newPacketCount - fLastPacketCount; + fLastPacketCount = newPacketCount; + u_int32_t prevTotalPacketCount_lo = fTotalPacketCount_lo; + fTotalPacketCount_lo += packetCountDiff; + if (fTotalPacketCount_lo < prevTotalPacketCount_lo) { // wrap around + ++fTotalPacketCount_hi; + } +} + +unsigned RTPTransmissionStats::roundTripDelay() const { + // Compute the round-trip delay that was indicated by the most recently-received + // RTCP RR packet. Use the method noted in the RTP/RTCP specification (RFC 3350). + + if (fLastSRTime == 0) { + // Either no RTCP RR packet has been received yet, or else the + // reporting receiver has not yet received any RTCP SR packets from us: + return 0; + } + + // First, convert the time that we received the last RTCP RR packet to NTP format, + // in units of 1/65536 (2^-16) seconds: + unsigned lastReceivedTimeNTP_high + = fTimeReceived.tv_sec + 0x83AA7E80; // 1970 epoch -> 1900 epoch + double fractionalPart = (fTimeReceived.tv_usec*0x0400)/15625.0; // 2^16/10^6 + unsigned lastReceivedTimeNTP + = (unsigned)((lastReceivedTimeNTP_high<<16) + fractionalPart + 0.5); + + int rawResult = lastReceivedTimeNTP - fLastSRTime - fDiffSR_RRTime; + if (rawResult < 0) { + // This can happen if there's clock drift between the sender and receiver, + // and if the round-trip time was very small. + rawResult = 0; + } + return (unsigned)rawResult; +} + +void RTPTransmissionStats::getTotalOctetCount(u_int32_t& hi, u_int32_t& lo) { + hi = fTotalOctetCount_hi; + lo = fTotalOctetCount_lo; +} + +void RTPTransmissionStats::getTotalPacketCount(u_int32_t& hi, u_int32_t& lo) { + hi = fTotalPacketCount_hi; + lo = fTotalPacketCount_lo; +} + +unsigned RTPTransmissionStats::packetsReceivedSinceLastRR() const { + if (!fOldValid) return 0; + + return fLastPacketNumReceived-fOldLastPacketNumReceived; +} + +int RTPTransmissionStats::packetsLostBetweenRR() const { + if (!fOldValid) return 0; + + return fTotNumPacketsLost - fOldTotNumPacketsLost; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTPSource.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTPSource.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTPSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTPSource.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,401 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// RTP Sources +// Implementation + +#include "RTPSource.hh" +#include "GroupsockHelper.hh" + +////////// RTPSource ////////// + +Boolean RTPSource::lookupByName(UsageEnvironment& env, + char const* sourceName, + RTPSource*& resultSource) { + resultSource = NULL; // unless we succeed + + MediaSource* source; + if (!MediaSource::lookupByName(env, sourceName, source)) return False; + + if (!source->isRTPSource()) { + env.setResultMsg(sourceName, " is not a RTP source"); + return False; + } + + resultSource = (RTPSource*)source; + return True; +} + +Boolean RTPSource::hasBeenSynchronizedUsingRTCP() { + return fCurPacketHasBeenSynchronizedUsingRTCP; +} + +Boolean RTPSource::isRTPSource() const { + return True; +} + +RTPSource::RTPSource(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + u_int32_t rtpTimestampFrequency) + : FramedSource(env), + fRTPInterface(this, RTPgs), + fCurPacketHasBeenSynchronizedUsingRTCP(False), + fRTPPayloadFormat(rtpPayloadFormat), + fTimestampFrequency(rtpTimestampFrequency), + fSSRC(our_random32()), + fCurPacketRTPSeqNum(0), + fCurPacketRTPTimestamp(0), + fCurPacketMarkerBit(False), + fLastReceivedSSRC(0) +{ + fReceptionStatsDB = new RTPReceptionStatsDB(); +} + +RTPSource::~RTPSource() { + delete fReceptionStatsDB; +} + +void RTPSource::getAttributes() const { + envir().setResultMsg(""); // Fix later to get attributes from header ##### +} + + +////////// RTPReceptionStatsDB ////////// + +RTPReceptionStatsDB::RTPReceptionStatsDB() + : fTable(HashTable::create(ONE_WORD_HASH_KEYS)), fTotNumPacketsReceived(0) { + reset(); +} + +void RTPReceptionStatsDB::reset() { + fNumActiveSourcesSinceLastReset = 0; + + Iterator iter(*this); + RTPReceptionStats* stats; + while ((stats = iter.next()) != NULL) { + stats->reset(); + } +} + +RTPReceptionStatsDB::~RTPReceptionStatsDB() { + // First, remove and delete all stats records from the table: + RTPReceptionStats* stats; + while ((stats = (RTPReceptionStats*)fTable->RemoveNext()) != NULL) { + delete stats; + } + + // Then, delete the table itself: + delete fTable; +} + +void RTPReceptionStatsDB +::noteIncomingPacket(u_int32_t SSRC, u_int16_t seqNum, + u_int32_t rtpTimestamp, unsigned timestampFrequency, + Boolean useForJitterCalculation, + struct timeval& resultPresentationTime, + Boolean& resultHasBeenSyncedUsingRTCP, + unsigned packetSize) { + ++fTotNumPacketsReceived; + RTPReceptionStats* stats = lookup(SSRC); + if (stats == NULL) { + // This is the first time we've heard from this SSRC. + // Create a new record for it: + stats = new RTPReceptionStats(SSRC, seqNum); + if (stats == NULL) return; + add(SSRC, stats); + } + + if (stats->numPacketsReceivedSinceLastReset() == 0) { + ++fNumActiveSourcesSinceLastReset; + } + + stats->noteIncomingPacket(seqNum, rtpTimestamp, timestampFrequency, + useForJitterCalculation, + resultPresentationTime, + resultHasBeenSyncedUsingRTCP, packetSize); +} + +void RTPReceptionStatsDB +::noteIncomingSR(u_int32_t SSRC, + u_int32_t ntpTimestampMSW, u_int32_t ntpTimestampLSW, + u_int32_t rtpTimestamp) { + RTPReceptionStats* stats = lookup(SSRC); + if (stats == NULL) { + // This is the first time we've heard of this SSRC. + // Create a new record for it: + stats = new RTPReceptionStats(SSRC); + if (stats == NULL) return; + add(SSRC, stats); + } + + stats->noteIncomingSR(ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp); +} + +void RTPReceptionStatsDB::removeRecord(u_int32_t SSRC) { + RTPReceptionStats* stats = lookup(SSRC); + if (stats != NULL) { + long SSRC_long = (long)SSRC; + fTable->Remove((char const*)SSRC_long); + delete stats; + } +} + +RTPReceptionStatsDB::Iterator +::Iterator(RTPReceptionStatsDB& receptionStatsDB) + : fIter(HashTable::Iterator::create(*(receptionStatsDB.fTable))) { +} + +RTPReceptionStatsDB::Iterator::~Iterator() { + delete fIter; +} + +RTPReceptionStats* +RTPReceptionStatsDB::Iterator::next(Boolean includeInactiveSources) { + char const* key; // dummy + + // If asked, skip over any sources that haven't been active + // since the last reset: + RTPReceptionStats* stats; + do { + stats = (RTPReceptionStats*)(fIter->next(key)); + } while (stats != NULL && !includeInactiveSources + && stats->numPacketsReceivedSinceLastReset() == 0); + + return stats; +} + +RTPReceptionStats* RTPReceptionStatsDB::lookup(u_int32_t SSRC) const { + long SSRC_long = (long)SSRC; + return (RTPReceptionStats*)(fTable->Lookup((char const*)SSRC_long)); +} + +void RTPReceptionStatsDB::add(u_int32_t SSRC, RTPReceptionStats* stats) { + long SSRC_long = (long)SSRC; + fTable->Add((char const*)SSRC_long, stats); +} + +////////// RTPReceptionStats ////////// + +RTPReceptionStats::RTPReceptionStats(u_int32_t SSRC, u_int16_t initialSeqNum) { + initSeqNum(initialSeqNum); + init(SSRC); +} + +RTPReceptionStats::RTPReceptionStats(u_int32_t SSRC) { + fBaseExtSeqNumReceived = 0; + fHighestExtSeqNumReceived = 0; + init(SSRC); +} + +RTPReceptionStats::~RTPReceptionStats() { +} + +void RTPReceptionStats::init(u_int32_t SSRC) { + fSSRC = SSRC; + fTotNumPacketsReceived = 0; + fTotBytesReceived_hi = fTotBytesReceived_lo = 0; + fHaveSeenInitialSequenceNumber = False; + fLastTransit = ~0; + fPreviousPacketRTPTimestamp = 0; + fJitter = 0.0; + fLastReceivedSR_NTPmsw = fLastReceivedSR_NTPlsw = 0; + fLastReceivedSR_time.tv_sec = fLastReceivedSR_time.tv_usec = 0; + fLastPacketReceptionTime.tv_sec = fLastPacketReceptionTime.tv_usec = 0; + fMinInterPacketGapUS = 0x7FFFFFFF; + fMaxInterPacketGapUS = 0; + fTotalInterPacketGaps.tv_sec = fTotalInterPacketGaps.tv_usec = 0; + fHasBeenSynchronized = False; + fSyncTime.tv_sec = fSyncTime.tv_usec = 0; + fSyncTimestamp = 0; + reset(); +} + +void RTPReceptionStats::initSeqNum(u_int16_t initialSeqNum) { + fBaseExtSeqNumReceived = initialSeqNum-1; + fHighestExtSeqNumReceived = initialSeqNum; + fHaveSeenInitialSequenceNumber = True; +} + +#ifndef MILLION +#define MILLION 1000000 +#endif + +void RTPReceptionStats +::noteIncomingPacket(u_int16_t seqNum, u_int32_t rtpTimestamp, + unsigned timestampFrequency, + Boolean useForJitterCalculation, + struct timeval& resultPresentationTime, + Boolean& resultHasBeenSyncedUsingRTCP, + unsigned packetSize) { + if (!fHaveSeenInitialSequenceNumber) initSeqNum(seqNum); + + ++fNumPacketsReceivedSinceLastReset; + ++fTotNumPacketsReceived; + u_int32_t prevTotBytesReceived_lo = fTotBytesReceived_lo; + fTotBytesReceived_lo += packetSize; + if (fTotBytesReceived_lo < prevTotBytesReceived_lo) { // wrap-around + ++fTotBytesReceived_hi; + } + + // Check whether the new sequence number is the highest yet seen: + unsigned oldSeqNum = (fHighestExtSeqNumReceived&0xFFFF); + if (seqNumLT((u_int16_t)oldSeqNum, seqNum)) { + // This packet was not an old packet received out of order, so check it: + unsigned seqNumCycle = (fHighestExtSeqNumReceived&0xFFFF0000); + unsigned seqNumDifference = (unsigned)((int)seqNum-(int)oldSeqNum); + if (seqNumDifference >= 0x8000) { + // The sequence number wrapped around, so start a new cycle: + seqNumCycle += 0x10000; + } + + unsigned newSeqNum = seqNumCycle|seqNum; + if (newSeqNum > fHighestExtSeqNumReceived) { + fHighestExtSeqNumReceived = newSeqNum; + } + } + + // Record the inter-packet delay + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + if (fLastPacketReceptionTime.tv_sec != 0 + || fLastPacketReceptionTime.tv_usec != 0) { + unsigned gap + = (timeNow.tv_sec - fLastPacketReceptionTime.tv_sec)*MILLION + + timeNow.tv_usec - fLastPacketReceptionTime.tv_usec; + if (gap > fMaxInterPacketGapUS) { + fMaxInterPacketGapUS = gap; + } + if (gap < fMinInterPacketGapUS) { + fMinInterPacketGapUS = gap; + } + fTotalInterPacketGaps.tv_usec += gap; + if (fTotalInterPacketGaps.tv_usec >= MILLION) { + ++fTotalInterPacketGaps.tv_sec; + fTotalInterPacketGaps.tv_usec -= MILLION; + } + } + fLastPacketReceptionTime = timeNow; + + // Compute the current 'jitter' using the received packet's RTP timestamp, + // and the RTP timestamp that would correspond to the current time. + // (Use the code from appendix A.8 in the RTP spec.) + // Note, however, that we don't use this packet if its timestamp is + // the same as that of the previous packet (this indicates a multi-packet + // fragment), or if we've been explicitly told not to use this packet. + if (useForJitterCalculation + && rtpTimestamp != fPreviousPacketRTPTimestamp) { + unsigned arrival = (timestampFrequency*timeNow.tv_sec); + arrival += (unsigned) + ((2.0*timestampFrequency*timeNow.tv_usec + 1000000.0)/2000000); + // note: rounding + int transit = arrival - rtpTimestamp; + if (fLastTransit == (~0)) fLastTransit = transit; // hack for first time + int d = transit - fLastTransit; + fLastTransit = transit; + if (d < 0) d = -d; + fJitter += (1.0/16.0) * ((double)d - fJitter); + } + + // Return the 'presentation time' that corresponds to "rtpTimestamp": + if (fSyncTime.tv_sec == 0 && fSyncTime.tv_usec == 0) { + // This is the first timestamp that we've seen, so use the current + // 'wall clock' time as the synchronization time. (This will be + // corrected later when we receive RTCP SRs.) + fSyncTimestamp = rtpTimestamp; + fSyncTime = timeNow; + } + + int timestampDiff = rtpTimestamp - fSyncTimestamp; + // Note: This works even if the timestamp wraps around + // (as long as "int" is 32 bits) + + // Divide this by the timestamp frequency to get real time: + double timeDiff = timestampDiff/(double)timestampFrequency; + + // Add this to the 'sync time' to get our result: + unsigned const million = 1000000; + unsigned seconds, uSeconds; + if (timeDiff >= 0.0) { + seconds = fSyncTime.tv_sec + (unsigned)(timeDiff); + uSeconds = fSyncTime.tv_usec + + (unsigned)((timeDiff - (unsigned)timeDiff)*million); + if (uSeconds >= million) { + uSeconds -= million; + ++seconds; + } + } else { + timeDiff = -timeDiff; + seconds = fSyncTime.tv_sec - (unsigned)(timeDiff); + uSeconds = fSyncTime.tv_usec + - (unsigned)((timeDiff - (unsigned)timeDiff)*million); + if ((int)uSeconds < 0) { + uSeconds += million; + --seconds; + } + } + resultPresentationTime.tv_sec = seconds; + resultPresentationTime.tv_usec = uSeconds; + resultHasBeenSyncedUsingRTCP = fHasBeenSynchronized; + + // Save these as the new synchronization timestamp & time: + fSyncTimestamp = rtpTimestamp; + fSyncTime = resultPresentationTime; + + fPreviousPacketRTPTimestamp = rtpTimestamp; +} + +void RTPReceptionStats::noteIncomingSR(u_int32_t ntpTimestampMSW, + u_int32_t ntpTimestampLSW, + u_int32_t rtpTimestamp) { + fLastReceivedSR_NTPmsw = ntpTimestampMSW; + fLastReceivedSR_NTPlsw = ntpTimestampLSW; + + gettimeofday(&fLastReceivedSR_time, NULL); + + // Use this SR to update time synchronization information: + fSyncTimestamp = rtpTimestamp; + fSyncTime.tv_sec = ntpTimestampMSW - 0x83AA7E80; // 1/1/1900 -> 1/1/1970 + double microseconds = (ntpTimestampLSW*15625.0)/0x04000000; // 10^6/2^32 + fSyncTime.tv_usec = (unsigned)(microseconds+0.5); + fHasBeenSynchronized = True; +} + +double RTPReceptionStats::totNumKBytesReceived() const { + double const hiMultiplier = 0x20000000/125.0; // == (2^32)/(10^3) + return fTotBytesReceived_hi*hiMultiplier + fTotBytesReceived_lo/1000.0; +} + +unsigned RTPReceptionStats::jitter() const { + return (unsigned)fJitter; +} + +void RTPReceptionStats::reset() { + fNumPacketsReceivedSinceLastReset = 0; + fLastResetExtSeqNumReceived = fHighestExtSeqNumReceived; +} + +Boolean seqNumLT(u_int16_t s1, u_int16_t s2) { + // a 'less-than' on 16-bit sequence numbers + int diff = s2-s1; + if (diff > 0) { + return (diff < 0x8000); + } else if (diff < 0) { + return (diff < -0x8000); + } else { // diff == 0 + return False; + } +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTSPClient.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTSPClient.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTSPClient.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTSPClient.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,2549 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// A generic RTSP client +// Implementation + +#include "RTSPClient.hh" +#include "RTSPCommon.hh" +#include "Base64.hh" +#include "Locale.hh" +#include +#include "our_md5.h" +#ifdef SUPPORT_REAL_RTSP +#include "../RealRTSP/include/RealRTSP.hh" +#endif + +////////// RTSPClient ////////// + +RTSPClient* RTSPClient::createNew(UsageEnvironment& env, + int verbosityLevel, + char const* applicationName, + portNumBits tunnelOverHTTPPortNum) { + return new RTSPClient(env, verbosityLevel, + applicationName, tunnelOverHTTPPortNum); +} + +Boolean RTSPClient::lookupByName(UsageEnvironment& env, + char const* instanceName, + RTSPClient*& resultClient) { + resultClient = NULL; // unless we succeed + + Medium* medium; + if (!Medium::lookupByName(env, instanceName, medium)) return False; + + if (!medium->isRTSPClient()) { + env.setResultMsg(instanceName, " is not a RTSP client"); + return False; + } + + resultClient = (RTSPClient*)medium; + return True; +} + +unsigned RTSPClient::fCSeq = 0; + +RTSPClient::RTSPClient(UsageEnvironment& env, + int verbosityLevel, char const* applicationName, + portNumBits tunnelOverHTTPPortNum) + : Medium(env), + fVerbosityLevel(verbosityLevel), + fTunnelOverHTTPPortNum(tunnelOverHTTPPortNum), + fInputSocketNum(-1), fOutputSocketNum(-1), fServerAddress(0), + fBaseURL(NULL), fTCPStreamIdCount(0), fLastSessionId(NULL), + fSessionTimeoutParameter(0), +#ifdef SUPPORT_REAL_RTSP + fRealChallengeStr(NULL), fRealETagStr(NULL), +#endif + fServerIsKasenna(False), fKasennaContentType(NULL), + fServerIsMicrosoft(False), fDescribeStatusCode(0) +{ + fResponseBufferSize = 20000; + fResponseBuffer = new char[fResponseBufferSize+1]; + + // Set the "User-Agent:" header to use in each request: + char const* const libName = "LIVE555 Streaming Media v"; + char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING; + char const* libPrefix; char const* libSuffix; + if (applicationName == NULL || applicationName[0] == '\0') { + applicationName = libPrefix = libSuffix = ""; + } else { + libPrefix = " ("; + libSuffix = ")"; + } + char const* const formatStr = "User-Agent: %s%s%s%s%s\r\n"; + size_t headerSize + = strlen(formatStr) + strlen(applicationName) + strlen(libPrefix) + + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix) + 1; + fUserAgentHeaderStr = new char[headerSize]; + snprintf(fUserAgentHeaderStr, (headerSize-1), formatStr, + applicationName, libPrefix, libName, libVersionStr, libSuffix); + fUserAgentHeaderStr[headerSize - 1] = '\0'; + fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr); +} + +void RTSPClient::setUserAgentString(char const* userAgentStr) { + if (userAgentStr == NULL) return; + + // Change the existing user agent header string: + char const* const formatStr = "User-Agent: %s\r\n"; + size_t headerSize = strlen(formatStr) + strlen(userAgentStr) + 1; + delete[] fUserAgentHeaderStr; + fUserAgentHeaderStr = new char[headerSize]; + sprintf(fUserAgentHeaderStr, formatStr, userAgentStr); + fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr); +} + +RTSPClient::~RTSPClient() { + envir().taskScheduler().turnOffBackgroundReadHandling(fInputSocketNum); // must be called before: + reset(); + + delete[] fResponseBuffer; + delete[] fUserAgentHeaderStr; +} + +Boolean RTSPClient::isRTSPClient() const { + return True; +} + +void RTSPClient::resetTCPSockets() { + if (fInputSocketNum >= 0) { + ::closeSocket(fInputSocketNum); + if (fOutputSocketNum != fInputSocketNum) ::closeSocket(fOutputSocketNum); + } + fInputSocketNum = fOutputSocketNum = -1; +} + +void RTSPClient::reset() { + resetTCPSockets(); + fServerAddress = 0; + + delete[] fBaseURL; fBaseURL = NULL; + + fCurrentAuthenticator.reset(); + + delete[] fKasennaContentType; fKasennaContentType = NULL; +#ifdef SUPPORT_REAL_RTSP + delete[] fRealChallengeStr; fRealChallengeStr = NULL; + delete[] fRealETagStr; fRealETagStr = NULL; +#endif + delete[] fLastSessionId; fLastSessionId = NULL; +} + +static char* getLine(char* startOfLine) { + // returns the start of the next line, or NULL if none + for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) { + // Check for the end of line: \r\n (but also accept \r or \n by itself): + if (*ptr == '\r' || *ptr == '\n') { + // We found the end of the line + if (*ptr == '\r') { + *ptr++ = '\0'; + if (*ptr == '\n') ++ptr; + } else { + *ptr++ = '\0'; + } + return ptr; + } + } + + return NULL; +} + +char* RTSPClient::describeURL(char const* url, Authenticator* authenticator, + Boolean allowKasennaProtocol, int timeout) { + char* cmd = NULL; + fDescribeStatusCode = 0; + do { + // First, check whether "url" contains a username:password to be used: + char* username; char* password; + if (authenticator == NULL + && parseRTSPURLUsernamePassword(url, username, password)) { + char* result = describeWithPassword(url, username, password, allowKasennaProtocol, timeout); + delete[] username; delete[] password; // they were dynamically allocated + return result; + } + + if (!openConnectionFromURL(url, authenticator, timeout)) break; + + // Send the DESCRIBE command: + + // First, construct an authenticator string: + fCurrentAuthenticator.reset(); + char* authenticatorStr + = createAuthenticatorString(authenticator, "DESCRIBE", url); + + char const* acceptStr = allowKasennaProtocol + ? "Accept: application/x-rtsp-mh, application/sdp\r\n" + : "Accept: application/sdp\r\n"; + + // (Later implement more, as specified in the RTSP spec, sec D.1 #####) + char const* const cmdFmt = + "DESCRIBE %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "%s" + "%s" + "%s" +#ifdef SUPPORT_REAL_RTSP + REAL_DESCRIBE_HEADERS +#endif + "\r\n"; + size_t cmdSize = strlen(cmdFmt) + + strlen(url) + + 20 /* max int len */ + + strlen(acceptStr) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize; + cmd = new char[cmdSize]; + sprintf(cmd, cmdFmt, + url, + ++fCSeq, + acceptStr, + authenticatorStr, + fUserAgentHeaderStr); + delete[] authenticatorStr; + + if (!sendRequest(cmd, "DESCRIBE")) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("DESCRIBE", bytesRead, responseCode, firstLine, nextLineStart, + False /*don't check for response code 200*/)) break; + + // Inspect the first line to check whether it's a result code that + // we can handle. + Boolean wantRedirection = False; + char* redirectionURL = NULL; +#ifdef SUPPORT_REAL_RTSP + delete[] fRealETagStr; fRealETagStr = new char[fResponseBufferSize]; +#endif + if (responseCode == 301 || responseCode == 302) { + wantRedirection = True; + redirectionURL = new char[fResponseBufferSize]; // ensures enough space + } else if (responseCode != 200) { + checkForAuthenticationFailure(responseCode, nextLineStart, authenticator); + envir().setResultMsg("cannot handle DESCRIBE response: ", firstLine); + break; + } + + // Skip over subsequent header lines, until we see a blank line. + // The remaining data is assumed to be the SDP descriptor that we want. + // While skipping over the header lines, we also check for certain headers + // that we recognize. + // (We should also check for "Content-type: application/sdp", + // "Content-location", "CSeq", etc.) ##### + char* serverType = new char[fResponseBufferSize]; // ensures enough space + int contentLength = -1; + char* lineStart; + while (1) { + lineStart = nextLineStart; + if (lineStart == NULL) break; + + nextLineStart = getLine(lineStart); + if (lineStart[0] == '\0') break; // this is a blank line + + if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1 + || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) { + if (contentLength < 0) { + envir().setResultMsg("Bad \"Content-length:\" header: \"", + lineStart, "\""); + break; + } + } else if (strncmp(lineStart, "Content-Base:", 13) == 0) { + size_t cbIndex = 13; + + while (lineStart[cbIndex] == ' ' || lineStart[cbIndex] == '\t') ++cbIndex; + if (lineStart[cbIndex] != '\0'/*sanity check*/) { + delete[] fBaseURL; fBaseURL = strDup(&lineStart[cbIndex]); + } + } else if (sscanf(lineStart, "Server: %s", serverType) == 1) { + if (strncmp(serverType, "Kasenna", 7) == 0) fServerIsKasenna = True; + if (strncmp(serverType, "WMServer", 8) == 0) fServerIsMicrosoft = True; +#ifdef SUPPORT_REAL_RTSP + } else if (sscanf(lineStart, "ETag: %s", fRealETagStr) == 1) { +#endif + } else if (wantRedirection) { + if (sscanf(lineStart, "Location: %s", redirectionURL) == 1) { + // Try again with this URL + if (fVerbosityLevel >= 1) { + envir() << "Redirecting to the new URL \"" + << redirectionURL << "\"\n"; + } + reset(); + char* result = describeURL(redirectionURL, authenticator, allowKasennaProtocol, timeout); + delete[] redirectionURL; + delete[] serverType; + delete[] cmd; + return result; + } + } + } + delete[] serverType; + + // We're now at the end of the response header lines + if (wantRedirection) { + envir().setResultMsg("Saw redirection response code, but not a \"Location:\" header"); + delete[] redirectionURL; + break; + } + if (lineStart == NULL) { + envir().setResultMsg("no content following header lines: ", fResponseBuffer); + break; + } + + // Use the remaining data as the SDP descr, but first, check + // the "Content-length:" header (if any) that we saw. We may need to + // read more data, or we may have extraneous data in the buffer. + char* bodyStart = nextLineStart; + if (contentLength >= 0) { + // We saw a "Content-length:" header + unsigned numBodyBytes = &firstLine[bytesRead] - bodyStart; + if (contentLength > (int)numBodyBytes) { + // We need to read more data. First, make sure we have enough + // space for it: + unsigned numExtraBytesNeeded = contentLength - numBodyBytes; + unsigned remainingBufferSize + = fResponseBufferSize - (bytesRead + (firstLine - fResponseBuffer)); + if (numExtraBytesNeeded > remainingBufferSize) { + char tmpBuf[200]; + sprintf(tmpBuf, "Read buffer size (%lu) is too small for \"Content-length:\" %d (need a buffer size of >= %lu bytes\n", + (unsigned long) fResponseBufferSize, contentLength, + (unsigned long) (fResponseBufferSize + numExtraBytesNeeded - remainingBufferSize)); + envir().setResultMsg(tmpBuf); + break; + } + + // Keep reading more data until we have enough: + if (fVerbosityLevel >= 1) { + envir() << "Need to read " << numExtraBytesNeeded + << " extra bytes\n"; + } + while (numExtraBytesNeeded > 0) { + struct sockaddr_in fromAddress; + char* ptr = &firstLine[bytesRead]; + int bytesRead2 = readSocket(envir(), fInputSocketNum, (unsigned char*)ptr, + numExtraBytesNeeded, fromAddress); + if (bytesRead2 <= 0) break; + ptr[bytesRead2] = '\0'; + if (fVerbosityLevel >= 1) { + envir() << "Read " << bytesRead2 << " extra bytes: " + << ptr << "\n"; + } + + bytesRead += bytesRead2; + numExtraBytesNeeded -= bytesRead2; + } + if (numExtraBytesNeeded > 0) break; // one of the reads failed + } + + // Remove any '\0' characters from inside the SDP description. + // Any such characters would violate the SDP specification, but + // some RTSP servers have been known to include them: + int from, to = 0; + for (from = 0; from < contentLength; ++from) { + if (bodyStart[from] != '\0') { + if (to != from) bodyStart[to] = bodyStart[from]; + ++to; + } + } + if (from != to && fVerbosityLevel >= 1) { + envir() << "Warning: " << from-to << " invalid 'NULL' bytes were found in (and removed from) the SDP description.\n"; + } + bodyStart[to] = '\0'; // trims any extra data + } + + ////////// BEGIN Kasenna BS ////////// + // If necessary, handle Kasenna's non-standard BS response: + if (fServerIsKasenna && strncmp(bodyStart, "", 18) == 0) { + // Translate from x-rtsp-mh to sdp + int videoPid, audioPid; + u_int64_t mh_duration; + char* currentWord = new char[fResponseBufferSize]; // ensures enough space + delete[] fKasennaContentType; + fKasennaContentType = new char[fResponseBufferSize]; // ensures enough space + char* currentPos = bodyStart; + + while (strcmp(currentWord, "") != 0) { + sscanf(currentPos, "%s", currentWord); + + if (strcmp(currentWord, "VideoPid") == 0) { + currentPos += strlen(currentWord) + 1; + sscanf(currentPos, "%s", currentWord); + currentPos += strlen(currentWord) + 1; + sscanf(currentPos, "%d", &videoPid); + currentPos += 3; + } + + if (strcmp(currentWord, "AudioPid") == 0) { + currentPos += strlen(currentWord) + 1; + sscanf(currentPos, "%s", currentWord); + currentPos += strlen(currentWord) + 1; + sscanf(currentPos, "%d", &audioPid); + currentPos += 3; + } + + if (strcmp(currentWord, "Duration") == 0) { + currentPos += strlen(currentWord) + 1; + sscanf(currentPos, "%s", currentWord); + currentPos += strlen(currentWord) + 1; + sscanf(currentPos, "%llu", (long long unsigned int*) &mh_duration); + currentPos += 3; + } + + if (strcmp(currentWord, "TypeSpecificData") == 0) { + currentPos += strlen(currentWord) + 1; + sscanf(currentPos, "%s", currentWord); + currentPos += strlen(currentWord) + 1; + sscanf(currentPos, "%s", fKasennaContentType); + currentPos += 3; + printf("Kasenna Content Type: %s\n", fKasennaContentType); + } + + currentPos += strlen(currentWord) + 1; + } + + if (fKasennaContentType != NULL + && strcmp(fKasennaContentType, "PARTNER_41_MPEG-4") == 0) { + char* describeSDP = describeURL(url, authenticator, True, timeout); + + delete[] currentWord; + delete[] cmd; + return describeSDP; + } + + unsigned char byte1 = fServerAddress & 0x000000ff; + unsigned char byte2 = (fServerAddress & 0x0000ff00) >> 8; + unsigned char byte3 = (fServerAddress & 0x00ff0000) >> 16; + unsigned char byte4 = (fServerAddress & 0xff000000) >> 24; + + char const* sdpFmt = + "v=0\r\n" + "o=NoSpacesAllowed 1 1 IN IP4 %u.%u.%u.%u\r\n" + "s=%s\r\n" + "c=IN IP4 %u.%u.%u.%u\r\n" + "t=0 0\r\n" + "a=control:*\r\n" + "a=range:npt=0-%llu\r\n" + "m=video 1554 RAW/RAW/UDP 33\r\n" + "a=control:trackID=%d\r\n"; + size_t sdpBufSize = strlen(sdpFmt) + + 4*3 // IP address + + strlen(url) + + 20 // max int length + + 20; // max int length + char* sdpBuf = new char[sdpBufSize]; + sprintf(sdpBuf, sdpFmt, + byte1, byte2, byte3, byte4, + url, + byte1, byte2, byte3, byte4, + mh_duration/1000000, + videoPid); + + char* result = strDup(sdpBuf); + delete[] sdpBuf; delete[] currentWord; + delete[] cmd; + return result; + } + ////////// END Kasenna BS ////////// + + delete[] cmd; + return strDup(bodyStart); + } while (0); + + delete[] cmd; + if (fDescribeStatusCode == 0) fDescribeStatusCode = 2; + return NULL; +} + +char* RTSPClient +::describeWithPassword(char const* url, + char const* username, char const* password, + Boolean allowKasennaProtocol, int timeout) { + Authenticator authenticator; + authenticator.setUsernameAndPassword(username, password); + char* describeResult = describeURL(url, &authenticator, allowKasennaProtocol, timeout); + if (describeResult != NULL) { + // We are already authorized + return describeResult; + } + + // The "realm" field should have been filled in: + if (authenticator.realm() == NULL) { + // We haven't been given enough information to try again, so fail: + return NULL; + } + + // Try again: + describeResult = describeURL(url, &authenticator, allowKasennaProtocol, timeout); + if (describeResult != NULL) { + // The authenticator worked, so use it in future requests: + fCurrentAuthenticator = authenticator; + } + + return describeResult; +} + +char* RTSPClient::sendOptionsCmd(char const* url, + char* username, char* password, + Authenticator* authenticator, + int timeout) { + char* result = NULL; + char* cmd = NULL; + Boolean haveAllocatedAuthenticator = False; + do { + if (authenticator == NULL) { + // First, check whether "url" contains a username:password to be used + // (and no username,password pair was supplied separately): + if (username == NULL && password == NULL + && parseRTSPURLUsernamePassword(url, username, password)) { + Authenticator newAuthenticator; + newAuthenticator.setUsernameAndPassword(username, password); + result = sendOptionsCmd(url, username, password, &newAuthenticator, timeout); + delete[] username; delete[] password; // they were dynamically allocated + break; + } else if (username != NULL && password != NULL) { + // Use the separately supplied username and password: + authenticator = new Authenticator; + haveAllocatedAuthenticator = True; + authenticator->setUsernameAndPassword(username, password); + + result = sendOptionsCmd(url, username, password, authenticator, timeout); + if (result != NULL) break; // We are already authorized + + // The "realm" field should have been filled in: + if (authenticator->realm() == NULL) { + // We haven't been given enough information to try again, so fail: + break; + } + // Try again: + } + } + + if (!openConnectionFromURL(url, authenticator, timeout)) break; + + // Send the OPTIONS command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(authenticator, "OPTIONS", url); + + char const* const cmdFmt = + "OPTIONS %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "%s" + "%s" +#ifdef SUPPORT_REAL_RTSP + REAL_OPTIONS_HEADERS +#endif + "\r\n"; + size_t cmdSize = strlen(cmdFmt) + + strlen(url) + + 20 /* max int len */ + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize; + cmd = new char[cmdSize]; + sprintf(cmd, cmdFmt, + url, + ++fCSeq, + authenticatorStr, + fUserAgentHeaderStr); + delete[] authenticatorStr; + + if (!sendRequest(cmd, "OPTIONS")) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("OPTIONS", bytesRead, responseCode, firstLine, nextLineStart, + False /*don't check for response code 200*/)) break; + if (responseCode != 200) { + checkForAuthenticationFailure(responseCode, nextLineStart, authenticator); + envir().setResultMsg("cannot handle OPTIONS response: ", firstLine); + break; + } + + // Look for a "Public:" header (which will contain our result str): + char* lineStart; + while (1) { + lineStart = nextLineStart; + if (lineStart == NULL) break; + + nextLineStart = getLine(lineStart); + + if (_strncasecmp(lineStart, "Public: ", 8) == 0) { + delete[] result; result = strDup(&lineStart[8]); +#ifdef SUPPORT_REAL_RTSP + } else if (_strncasecmp(lineStart, "RealChallenge1: ", 16) == 0) { + delete[] fRealChallengeStr; fRealChallengeStr = strDup(&lineStart[16]); +#endif + } + } + } while (0); + + delete[] cmd; + if (haveAllocatedAuthenticator) delete authenticator; + return result; +} + +static Boolean isAbsoluteURL(char const* url) { + // Assumption: "url" is absolute if it contains a ':', before any + // occurrence of '/' + while (*url != '\0' && *url != '/') { + if (*url == ':') return True; + ++url; + } + + return False; +} + +char const* RTSPClient::sessionURL(MediaSession const& session) const { + char const* url = session.controlPath(); + if (url == NULL || strcmp(url, "*") == 0) url = fBaseURL; + + return url; +} + +void RTSPClient::constructSubsessionURL(MediaSubsession const& subsession, + char const*& prefix, + char const*& separator, + char const*& suffix) { + // Figure out what the URL describing "subsession" will look like. + // The URL is returned in three parts: prefix; separator; suffix + //##### NOTE: This code doesn't really do the right thing if "sessionURL()" + // doesn't end with a "/", and "subsession.controlPath()" is relative. + // The right thing would have been to truncate "sessionURL()" back to the + // rightmost "/", and then add "subsession.controlPath()". + // In practice, though, each "DESCRIBE" response typically contains + // a "Content-Base:" header that consists of "sessionURL()" followed by + // a "/", in which case this code ends up giving the correct result. + // However, we should really fix this code to do the right thing, and + // also check for and use the "Content-Base:" header appropriately. ##### + prefix = sessionURL(subsession.parentSession()); + if (prefix == NULL) prefix = ""; + + suffix = subsession.controlPath(); + if (suffix == NULL) suffix = ""; + + if (isAbsoluteURL(suffix)) { + prefix = separator = ""; + } else { + size_t prefixLen = strlen(prefix); + separator = (prefix[prefixLen-1] == '/' || suffix[0] == '/') ? "" : "/"; + } +} + +Boolean RTSPClient::announceSDPDescription(char const* url, + char const* sdpDescription, + Authenticator* authenticator, + int timeout) { + char* cmd = NULL; + do { + if (!openConnectionFromURL(url, authenticator, timeout)) break; + + // Send the ANNOUNCE command: + + // First, construct an authenticator string: + fCurrentAuthenticator.reset(); + char* authenticatorStr + = createAuthenticatorString(authenticator, "ANNOUNCE", url); + + char const* const cmdFmt = + "ANNOUNCE %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Content-Type: application/sdp\r\n" + "%s" + "Content-length: %lu\r\n\r\n" + "%s"; + // Note: QTSS hangs if an "ANNOUNCE" contains a "User-Agent:" field (go figure), so don't include one here + size_t sdpSize = strlen(sdpDescription); + size_t cmdSize = strlen(cmdFmt) + + strlen(url) + + 20 /* max int len */ + + strlen(authenticatorStr) + + 20 /* max int len */ + + sdpSize; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + url, + ++fCSeq, + authenticatorStr, + (unsigned long) sdpSize, + sdpDescription); + cmd[cmdSize - 1] = '\0'; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "ANNOUNCE")) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("ANNOUNCE", bytesRead, responseCode, firstLine, nextLineStart, + False /*don't check for response code 200*/)) break; + + // Inspect the first line to check whether it's a result code 200 + if (responseCode != 200) { + checkForAuthenticationFailure(responseCode, nextLineStart, authenticator); + envir().setResultMsg("cannot handle ANNOUNCE response: ", firstLine); + break; + } + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient +::announceWithPassword(char const* url, char const* sdpDescription, + char const* username, char const* password, int timeout) { + Authenticator authenticator; + authenticator.setUsernameAndPassword(username, password); + if (announceSDPDescription(url, sdpDescription, &authenticator, timeout)) { + // We are already authorized + return True; + } + + // The "realm" field should have been filled in: + if (authenticator.realm() == NULL) { + // We haven't been given enough information to try again, so fail: + return False; + } + + // Try again: + Boolean secondTrySuccess + = announceSDPDescription(url, sdpDescription, &authenticator, timeout); + + if (secondTrySuccess) { + // The authenticator worked, so use it in future requests: + fCurrentAuthenticator = authenticator; + } + + return secondTrySuccess; +} + +Boolean RTSPClient::setupMediaSubsession(MediaSubsession& subsession, + Boolean streamOutgoing, + Boolean streamUsingTCP, + Boolean forceMulticastOnUnspecified) { + char* cmd = NULL; + char* setupStr = NULL; + + if (fServerIsMicrosoft) { + // Microsoft doesn't send the right endTime on live streams. Correct this: + char *tmpStr = subsession.parentSession().mediaSessionType(); + if (tmpStr != NULL && strncmp(tmpStr, "broadcast", 9) == 0) { + subsession.parentSession().playEndTime() = 0.0; + } + } + + do { + // Construct the SETUP command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, + "SETUP", fBaseURL); + + // When sending more than one "SETUP" request, include a "Session:" + // header in the 2nd and later "SETUP"s. + char* sessionStr; + if (fLastSessionId != NULL) { + sessionStr = new char[20+strlen(fLastSessionId)]; + sprintf(sessionStr, "Session: %s\r\n", fLastSessionId); + } else { + sessionStr = strDup(""); + } + + char* transportStr = NULL; +#ifdef SUPPORT_REAL_RTSP + if (usingRealNetworksChallengeResponse()) { + // Use a special "Transport:" header, and also add a 'challenge response'. + char challenge2[64]; + char checksum[34]; + RealCalculateChallengeResponse(fRealChallengeStr, challenge2, checksum); + + char const* etag = fRealETagStr == NULL ? "" : fRealETagStr; + + char* transportHeader; + if (subsession.parentSession().isRealNetworksRDT) { + transportHeader = strDup("Transport: x-pn-tng/tcp;mode=play,rtp/avp/unicast;mode=play\r\n"); + } else { + // Use a regular "Transport:" header: + char const* transportHeaderFmt + = "Transport: RTP/AVP%s%s=%d-%d\r\n"; + char const* transportTypeStr; + char const* portTypeStr; + unsigned short rtpNumber, rtcpNumber; + if (streamUsingTCP) { // streaming over the RTSP connection + transportTypeStr = "/TCP;unicast"; + portTypeStr = ";interleaved"; + rtpNumber = fTCPStreamIdCount++; + rtcpNumber = fTCPStreamIdCount++; + } else { // normal RTP streaming + unsigned connectionAddress = subsession.connectionEndpointAddress(); + Boolean requestMulticastStreaming = IsMulticastAddress(connectionAddress) + || (connectionAddress == 0 && forceMulticastOnUnspecified); + transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast"; + portTypeStr = ";client_port"; + rtpNumber = subsession.clientPortNum(); + if (rtpNumber == 0) { + envir().setResultMsg("Client port number unknown\n"); + break; + } + rtcpNumber = rtpNumber + 1; + } + + unsigned transportHeaderSize = strlen(transportHeaderFmt) + + strlen(transportTypeStr) + strlen(portTypeStr) + 2*5 /* max port len */; + transportHeader = new char[transportHeaderSize]; + sprintf(transportHeader, transportHeaderFmt, + transportTypeStr, portTypeStr, rtpNumber, rtcpNumber); + } + char const* transportFmt = + "%s" + "RealChallenge2: %s, sd=%s\r\n" + "If-Match: %s\r\n"; + unsigned transportSize = strlen(transportFmt) + + strlen(transportHeader) + + sizeof challenge2 + sizeof checksum + + strlen(etag); + transportStr = new char[transportSize]; + sprintf(transportStr, transportFmt, + transportHeader, + challenge2, checksum, + etag); + delete[] transportHeader; + + if (subsession.parentSession().isRealNetworksRDT) { + // Also, tell the RDT source to use the RTSP TCP socket: + RealRDTSource* rdtSource + = (RealRDTSource*)(subsession.readSource()); + rdtSource->setInputSocket(fInputSocketNum); + } + } +#endif + + char const *prefix, *separator, *suffix; + constructSubsessionURL(subsession, prefix, separator, suffix); + char const* transportFmt; + + if (strcmp(subsession.protocolName(), "UDP") == 0) { + char const* setupFmt = "SETUP %s%s RTSP/1.0\r\n"; + size_t setupSize = strlen(setupFmt) + + strlen(prefix) + strlen (separator) + 1; + setupStr = new char[setupSize]; + snprintf(setupStr, (setupSize - 1), setupFmt, prefix, separator); + setupStr[setupSize - 1] = '\0'; + + transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n"; + } else { + char const* setupFmt = "SETUP %s%s%s RTSP/1.0\r\n"; + size_t setupSize = strlen(setupFmt) + + strlen(prefix) + strlen (separator) + strlen(suffix) + 1; + setupStr = new char[setupSize]; + snprintf(setupStr, (setupSize - 1), setupFmt, prefix, separator, suffix); + setupStr[setupSize - 1] = '\0'; + + transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n"; + } + + if (transportStr == NULL) { + // Construct a "Transport:" header. + char const* transportTypeStr; + char const* modeStr = streamOutgoing ? ";mode=receive" : ""; + // Note: I think the above is nonstandard, but DSS wants it this way + char const* portTypeStr; + unsigned short rtpNumber, rtcpNumber; + if (streamUsingTCP) { // streaming over the RTSP connection + transportTypeStr = "/TCP;unicast"; + portTypeStr = ";interleaved"; + rtpNumber = fTCPStreamIdCount++; + rtcpNumber = fTCPStreamIdCount++; + } else { // normal RTP streaming + unsigned connectionAddress = subsession.connectionEndpointAddress(); + Boolean requestMulticastStreaming = IsMulticastAddress(connectionAddress) + || (connectionAddress == 0 && forceMulticastOnUnspecified); + transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast"; + portTypeStr = ";client_port"; + rtpNumber = subsession.clientPortNum(); + if (rtpNumber == 0) { + envir().setResultMsg("Client port number unknown\n"); + delete[] authenticatorStr; delete[] sessionStr; delete[] setupStr; + break; + } + rtcpNumber = rtpNumber + 1; + } + + size_t transportSize = strlen(transportFmt) + + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 + 1 /* max port len */; + transportStr = new char[transportSize]; + snprintf(transportStr, (transportSize -1), transportFmt, + transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber); + transportStr[transportSize - 1] = '\0'; + } + + // (Later implement more, as specified in the RTSP spec, sec D.1 #####) + char const* const cmdFmt = + "%s" + "CSeq: %d\r\n" + "%s" + "%s" + "%s" + "%s" + "\r\n"; + + size_t cmdSize = strlen(cmdFmt) + + strlen(setupStr) + + 20 /* max int len */ + + strlen(transportStr) + + strlen(sessionStr) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + setupStr, + ++fCSeq, + transportStr, + sessionStr, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + delete[] authenticatorStr; delete[] sessionStr; delete[] setupStr; delete[] transportStr; + + // And then send it: + if (!sendRequest(cmd, "SETUP")) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("SETUP", bytesRead, responseCode, firstLine, nextLineStart)) break; + + // Look for a "Session:" header (to set our session id), and + // a "Transport: " header (to set the server address/port) + // For now, ignore other headers. + char* lineStart; + char* sessionId = new char[fResponseBufferSize]; // ensures we have enough space + unsigned cLength = 0; + while (1) { + lineStart = nextLineStart; + if (lineStart == NULL) break; + + nextLineStart = getLine(lineStart); + + if (sscanf(lineStart, "Session: %[^;]", sessionId) == 1) { + subsession.sessionId = strDup(sessionId); + delete[] fLastSessionId; fLastSessionId = strDup(sessionId); + + // Also look for an optional "; timeout = " parameter following this: + char* afterSessionId + = lineStart + strlen(sessionId) + strlen ("Session: ");; + int timeoutVal; + if (sscanf(afterSessionId, "; timeout = %d", &timeoutVal) == 1) { + fSessionTimeoutParameter = timeoutVal; + } + continue; + } + + char* serverAddressStr; + portNumBits serverPortNum; + unsigned char rtpChannelId, rtcpChannelId; + if (parseTransportResponse(lineStart, + serverAddressStr, serverPortNum, + rtpChannelId, rtcpChannelId)) { + delete[] subsession.connectionEndpointName(); + subsession.connectionEndpointName() = serverAddressStr; + subsession.serverPortNum = serverPortNum; + subsession.rtpChannelId = rtpChannelId; + subsession.rtcpChannelId = rtcpChannelId; + continue; + } + + // Also check for a "Content-Length:" header. Some weird servers include this + // in the RTSP "SETUP" response. + if (sscanf(lineStart, "Content-Length: %d", &cLength) == 1) continue; + } + delete[] sessionId; + + if (subsession.sessionId == NULL) { + envir().setResultMsg("\"Session:\" header is missing in the response"); + break; + } + + // If we saw a "Content-Length:" header in the response, then discard whatever + // included data it refers to: + if (cLength > 0) { + char* dummyBuf = new char[cLength+1]; // allow for a trailing '\0' + getResponse1(dummyBuf, cLength); + delete[] dummyBuf; + } + + if (streamUsingTCP) { + // Tell the subsession to receive RTP (and send/receive RTCP) + // over the RTSP stream: + if (subsession.rtpSource() != NULL) + subsession.rtpSource()->setStreamSocket(fInputSocketNum, + subsession.rtpChannelId); + if (subsession.rtcpInstance() != NULL) + subsession.rtcpInstance()->setStreamSocket(fInputSocketNum, + subsession.rtcpChannelId); + } else { + // Normal case. + // Set the RTP and RTCP sockets' destination address and port + // from the information in the SETUP response (if present): + netAddressBits destAddress = subsession.connectionEndpointAddress(); + if (destAddress == 0) destAddress = fServerAddress; + subsession.setDestinations(destAddress); + } + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +static char* createScaleString(float scale, float currentScale) { + char buf[100]; + if (scale == 1.0f && currentScale == 1.0f) { + // This is the default value; we don't need a "Scale:" header: + buf[0] = '\0'; + } else { + Locale l("C", Numeric); + sprintf(buf, "Scale: %f\r\n", scale); + } + + return strDup(buf); +} + +static char* createRangeString(double start, double end) { + char buf[100]; + if (start < 0) { + // We're resuming from a PAUSE; there's no "Range:" header at all + buf[0] = '\0'; + } else if (end < 0) { + // There's no end time: + Locale l("C", Numeric); + sprintf(buf, "Range: npt=%.3f-\r\n", start); + } else { + // There's both a start and an end time; include them both in the "Range:" hdr + Locale l("C", Numeric); + sprintf(buf, "Range: npt=%.3f-%.3f\r\n", start, end); + } + + return strDup(buf); +} + +static char const* NoSessionErr = "No RTSP session is currently in progress\n"; + +Boolean RTSPClient::playMediaSession(MediaSession& session, + double start, double end, float scale) { +#ifdef SUPPORT_REAL_RTSP + if (session.isRealNetworksRDT) { + // This is a RealNetworks stream; set the "Subscribe" parameter before proceeding: + char* streamRuleString = RealGetSubscribeRuleString(&session); + setMediaSessionParameter(session, "Subscribe", streamRuleString); + delete[] streamRuleString; + } +#endif + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progress + if (fLastSessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the PLAY command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, "PLAY", fBaseURL); + // And then a "Scale:" string: + char* scaleStr = createScaleString(scale, session.scale()); + // And then a "Range:" string: + char* rangeStr = createRangeString(start, end); + + char const* const cmdFmt = + "PLAY %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "%s" + "%s" + "\r\n"; + + char const* sessURL = sessionURL(session); + size_t cmdSize = strlen(cmdFmt) + + strlen(sessURL) + + 20 /* max int len */ + + strlen(fLastSessionId) + + strlen(scaleStr) + + strlen(rangeStr) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + sessURL, + ++fCSeq, + fLastSessionId, + scaleStr, + rangeStr, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + delete[] scaleStr; + delete[] rangeStr; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "PLAY")) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("PLAY", bytesRead, responseCode, firstLine, nextLineStart)) break; + + // Look for various headers that we understand: + while (1) { + char* lineStart = nextLineStart; + if (lineStart == NULL) break; + + nextLineStart = getLine(lineStart); + + if (parseScaleHeader(lineStart, session.scale())) continue; + if (parseRangeHeader(lineStart, session.playStartTime(), session.playEndTime())) continue; + + u_int16_t seqNum; u_int32_t timestamp; + if (parseRTPInfoHeader(lineStart, seqNum, timestamp)) { + // This is data for our first subsession. Fill it in, and do the same for our other subsessions: + MediaSubsessionIterator iter(session); + MediaSubsession* subsession; + while ((subsession = iter.next()) != NULL) { + subsession->rtpInfo.seqNum = seqNum; + subsession->rtpInfo.timestamp = timestamp; + subsession->rtpInfo.infoIsNew = True; + + if (!parseRTPInfoHeader(lineStart, seqNum, timestamp)) break; + } + continue; + } + } + + if (fTCPStreamIdCount == 0) { // we're not receiving RTP-over-TCP + // Arrange to handle incoming requests sent by the server + envir().taskScheduler().turnOnBackgroundReadHandling(fInputSocketNum, + (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this); + } + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient::playMediaSubsession(MediaSubsession& subsession, + double start, double end, float scale, + Boolean hackForDSS) { + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progress + if (subsession.sessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the PLAY command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, "PLAY", fBaseURL); + // And then a "Scale:" string: + char* scaleStr = createScaleString(scale, subsession.scale()); + // And then a "Range:" string: + char* rangeStr = createRangeString(start, end); + + char const* const cmdFmt = + "PLAY %s%s%s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "%s" + "%s" + "\r\n"; + + char const *prefix, *separator, *suffix; + constructSubsessionURL(subsession, prefix, separator, suffix); + if (hackForDSS || fServerIsKasenna) { + // When "PLAY" is used to inject RTP packets into a DSS + // (violating the RTSP spec, btw; "RECORD" should have been used) + // the DSS can crash (or hang) if the '/trackid=...' portion of + // the URL is present. + separator = suffix = ""; + } + + size_t cmdSize = strlen(cmdFmt) + + strlen(prefix) + strlen(separator) + strlen(suffix) + + 20 /* max int len */ + + strlen(subsession.sessionId) + + strlen(scaleStr) + + strlen(rangeStr) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + prefix, separator, suffix, + ++fCSeq, + subsession.sessionId, + scaleStr, + rangeStr, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + delete[] scaleStr; + delete[] rangeStr; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "PLAY")) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("PLAY", bytesRead, responseCode, firstLine, nextLineStart)) break; + + // Look for various headers that we understand: + while (1) { + char* lineStart = nextLineStart; + if (lineStart == NULL) break; + + nextLineStart = getLine(lineStart); + + if (parseScaleHeader(lineStart, subsession.scale())) continue; + if (parseRangeHeader(lineStart, subsession._playStartTime(), subsession._playEndTime())) continue; + + u_int16_t seqNum; u_int32_t timestamp; + if (parseRTPInfoHeader(lineStart, seqNum, timestamp)) { + subsession.rtpInfo.seqNum = seqNum; + subsession.rtpInfo.timestamp = timestamp; + subsession.rtpInfo.infoIsNew = True; + continue; + } + } + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient::pauseMediaSession(MediaSession& session) { + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progress + if (fLastSessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the PAUSE command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, "PAUSE", fBaseURL); + + char const* sessURL = sessionURL(session); + char const* const cmdFmt = + "PAUSE %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "\r\n"; + + size_t cmdSize = strlen(cmdFmt) + + strlen(sessURL) + + 20 /* max int len */ + + strlen(fLastSessionId) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + sessURL, + ++fCSeq, + fLastSessionId, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "PAUSE")) break; + + if (fTCPStreamIdCount == 0) { // When TCP streaming, don't look for a response + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("PAUSE", bytesRead, responseCode, firstLine, nextLineStart)) break; + } + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient::pauseMediaSubsession(MediaSubsession& subsession) { + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progress + if (subsession.sessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the PAUSE command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, "PAUSE", fBaseURL); + + char const* const cmdFmt = + "PAUSE %s%s%s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "\r\n"; + + char const *prefix, *separator, *suffix; + constructSubsessionURL(subsession, prefix, separator, suffix); + if (fServerIsKasenna) separator = suffix = ""; + + size_t cmdSize = strlen(cmdFmt) + + strlen(prefix) + strlen(separator) + strlen(suffix) + + 20 /* max int len */ + + strlen(subsession.sessionId) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + prefix, separator, suffix, + ++fCSeq, + subsession.sessionId, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "PAUSE")) break; + + if (fTCPStreamIdCount == 0) { // When TCP streaming, don't look for a response + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("PAUSE", bytesRead, responseCode, firstLine, nextLineStart)) break; + } + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient::recordMediaSubsession(MediaSubsession& subsession) { + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progress + if (subsession.sessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the RECORD command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, + "RECORD", fBaseURL); + + char const* const cmdFmt = + "RECORD %s%s%s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "Range: npt=0-\r\n" + "%s" + "%s" + "\r\n"; + + char const *prefix, *separator, *suffix; + constructSubsessionURL(subsession, prefix, separator, suffix); + + size_t cmdSize = strlen(cmdFmt) + + strlen(prefix) + strlen(separator) + strlen(suffix) + + 20 /* max int len */ + + strlen(subsession.sessionId) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + prefix, separator, suffix, + ++fCSeq, + subsession.sessionId, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "RECORD")) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("RECORD", bytesRead, responseCode, firstLine, nextLineStart)) break; + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient::setMediaSessionParameter(MediaSession& /*session*/, + char const* parameterName, + char const* parameterValue) { + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progress + if (fLastSessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the SET_PARAMETER command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, + "SET_PARAMETER", fBaseURL); + + char const* const cmdFmt = + "SET_PARAMETER %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "Content-length: %lu\r\n\r\n" + "%s: %s\r\n"; + + size_t parameterNameLen = strlen(parameterName); + size_t parameterValueLen = strlen(parameterValue); + size_t cmdSize = strlen(cmdFmt) + + strlen(fBaseURL) + + 20 /* max int len */ + + strlen(fLastSessionId) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + + parameterNameLen + parameterValueLen + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + fBaseURL, + ++fCSeq, + fLastSessionId, + authenticatorStr, + fUserAgentHeaderStr, + (unsigned long) (parameterNameLen + parameterValueLen + 2), // the "+ 2" is for the \r\n after the parameter "name: value" + parameterName, parameterValue); + cmd[cmdSize - 1] = '\0'; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "SET_PARAMETER")) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("SET_PARAMETER", bytesRead, responseCode, firstLine, nextLineStart)) break; + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient::getMediaSessionParameter(MediaSession& /*session*/, + char const* parameterName, + char*& parameterValue) { + parameterValue = NULL; // default result + Boolean const haveParameterName = parameterName != NULL && parameterName[0] != '\0'; + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progress + if (fLastSessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the GET_PARAMETER command: + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, + "GET_PARAMETER", fBaseURL); + + if (haveParameterName) { + char const* const cmdFmt = + "GET_PARAMETER %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "Content-type: text/parameters\r\n" + "Content-length: %lu\r\n\r\n" + "%s\r\n"; + + size_t parameterNameLen = strlen(parameterName); + size_t cmdSize = strlen(cmdFmt) + + strlen(fBaseURL) + + 20 /* max int len */ + + strlen(fLastSessionId) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + + parameterNameLen + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + fBaseURL, + ++fCSeq, + fLastSessionId, + authenticatorStr, + fUserAgentHeaderStr, + (unsigned long) parameterNameLen + 2, // the "+ 2" is for the \r\n after the parameter name + parameterName); + cmd[cmdSize - 1] = '\0'; + } else { + char const* const cmdFmt = + "GET_PARAMETER %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "\r\n"; + + size_t cmdSize = strlen(cmdFmt) + + strlen(fBaseURL) + + 20 /* max int len */ + + strlen(fLastSessionId) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + fBaseURL, + ++fCSeq, + fLastSessionId, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + } + delete[] authenticatorStr; + + if (!sendRequest(cmd, "GET_PARAMETER")) break; + + // Get the response from the server: + // This section was copied/modified from the RTSPClient::describeURL func + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("GET_PARAMETER", bytesRead, responseCode, firstLine, + nextLineStart, False /*don't check for response code 200*/)) break; + + // Inspect the first line to check whether it's a result code that + // we can handle. + if (responseCode != 200) { + envir().setResultMsg("cannot handle GET_PARAMETER response: ", firstLine); + break; + } + + // Skip every subsequent header line, until we see a blank line + // The remaining data is assumed to be the parameter data that we want. + int contentLength = -1; + char* lineStart; + while (1) { + lineStart = nextLineStart; + if (lineStart == NULL) break; + + nextLineStart = getLine(lineStart); + if (lineStart[0] == '\0') break; // this is a blank line + + if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1 + || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) + { + if (contentLength < 0) { + envir().setResultMsg("Bad \"Content-length:\" header: \"", + lineStart, "\""); + break; + } + } + } + + // We're now at the end of the response header lines + if (lineStart == NULL) { + envir().setResultMsg("no content following header lines: ", + fResponseBuffer); + break; + } + + // Use the remaining data as the parameter data, but first, check + // the "Content-length:" header (if any) that we saw. We may need to + // read more data, or we may have extraneous data in the buffer. + char* bodyStart = nextLineStart; + if (contentLength >= 0) { + // We saw a "Content-length:" header + unsigned numBodyBytes = &firstLine[bytesRead] - bodyStart; + if (contentLength > (int)numBodyBytes) { + // We need to read more data. First, make sure we have enough + // space for it: + unsigned numExtraBytesNeeded = contentLength - numBodyBytes; + unsigned remainingBufferSize + = fResponseBufferSize - (bytesRead + (firstLine - fResponseBuffer)); + if (numExtraBytesNeeded > remainingBufferSize) { + char tmpBuf[200]; + sprintf(tmpBuf, "Read buffer size (%lu) is too small for \"Content-length:\" %d (need a buffer size of >= %lu bytes\n", + (unsigned long) fResponseBufferSize, contentLength, + (unsigned long) (fResponseBufferSize + numExtraBytesNeeded - remainingBufferSize)); + envir().setResultMsg(tmpBuf); + break; + } + + // Keep reading more data until we have enough: + if (fVerbosityLevel >= 1) { + envir() << "Need to read " << numExtraBytesNeeded + << " extra bytes\n"; + } + while (numExtraBytesNeeded > 0) { + struct sockaddr_in fromAddress; + char* ptr = &firstLine[bytesRead]; + int bytesRead2 = readSocket(envir(), fInputSocketNum, (unsigned char*)ptr, + numExtraBytesNeeded, fromAddress); + if (bytesRead2 <= 0) break; + ptr[bytesRead2] = '\0'; + if (fVerbosityLevel >= 1) { + envir() << "Read " << bytesRead2 << " extra bytes: " + << ptr << "\n"; + } + + bytesRead += bytesRead2; + numExtraBytesNeeded -= bytesRead2; + } + if (numExtraBytesNeeded > 0) break; // one of the reads failed + } + } + + if (haveParameterName + && !parseGetParameterHeader(bodyStart, parameterName, parameterValue)) break; + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient::teardownMediaSession(MediaSession& session) { + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progreee + if (fLastSessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the TEARDOWN command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, + "TEARDOWN", fBaseURL); + + char const* sessURL = sessionURL(session); + char const* const cmdFmt = + "TEARDOWN %s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "\r\n"; + + size_t cmdSize = strlen(cmdFmt) + + strlen(sessURL) + + 20 /* max int len */ + + strlen(fLastSessionId) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + sessURL, + ++fCSeq, + fLastSessionId, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "TEARDOWN")) break; + + if (fTCPStreamIdCount == 0) { // When TCP streaming, don't look for a response + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + getResponse("TEARDOWN", bytesRead, responseCode, firstLine, nextLineStart); // ignore the response; from our POV, we're done + + // Run through each subsession, deleting its "sessionId": + MediaSubsessionIterator iter(session); + MediaSubsession* subsession; + while ((subsession = iter.next()) != NULL) { + delete[] (char*)subsession->sessionId; + subsession->sessionId = NULL; + } + + delete[] fLastSessionId; fLastSessionId = NULL; + // we're done with this session + } + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient::teardownMediaSubsession(MediaSubsession& subsession) { + char* cmd = NULL; + do { + // First, make sure that we have a RTSP session in progreee + if (subsession.sessionId == NULL) { + envir().setResultMsg(NoSessionErr); + break; + } + + // Send the TEARDOWN command: + + // First, construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(&fCurrentAuthenticator, + "TEARDOWN", fBaseURL); + + char const* const cmdFmt = + "TEARDOWN %s%s%s RTSP/1.0\r\n" + "CSeq: %d\r\n" + "Session: %s\r\n" + "%s" + "%s" + "\r\n"; + + char const *prefix, *separator, *suffix; + constructSubsessionURL(subsession, prefix, separator, suffix); + + size_t cmdSize = strlen(cmdFmt) + + strlen(prefix) + strlen(separator) + strlen(suffix) + + 20 /* max int len */ + + strlen(subsession.sessionId) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + 1; + cmd = new char[cmdSize]; + snprintf(cmd, (cmdSize - 1), cmdFmt, + prefix, separator, suffix, + ++fCSeq, + subsession.sessionId, + authenticatorStr, + fUserAgentHeaderStr); + cmd[cmdSize - 1] = '\0'; + delete[] authenticatorStr; + + if (!sendRequest(cmd, "TEARDOWN")) break; + + if (fTCPStreamIdCount == 0) { // When TCP streaming, don't look for a response + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + getResponse("TEARDOWN", bytesRead, responseCode, firstLine, nextLineStart); // ignore the response; from our POV, we're done + } + + delete[] (char*)subsession.sessionId; + subsession.sessionId = NULL; + // we're done with this session + + delete[] cmd; + return True; + } while (0); + + delete[] cmd; + return False; +} + +Boolean RTSPClient +::openConnectionFromURL(char const* url, Authenticator* authenticator, int timeout) { + do { + // Set this as our base URL: + delete[] fBaseURL; fBaseURL = strDup(url); if (fBaseURL == NULL) break; + + // Begin by parsing the URL: + + NetAddress destAddress; + portNumBits urlPortNum; + char const* urlSuffix; + if (!parseRTSPURL(envir(), url, destAddress, urlPortNum, &urlSuffix)) break; + portNumBits destPortNum + = fTunnelOverHTTPPortNum == 0 ? urlPortNum : fTunnelOverHTTPPortNum; + + if (fInputSocketNum < 0) { + // We don't yet have a TCP socket. Set one up (blocking) now: + fInputSocketNum = fOutputSocketNum + = setupStreamSocket(envir(), 0, False /* =>blocking */); + if (fInputSocketNum < 0) break; + + // Connect to the remote endpoint: + fServerAddress = *(unsigned*)(destAddress.data()); + MAKE_SOCKADDR_IN(remoteName, fServerAddress, htons(destPortNum)); + fd_set set; + FD_ZERO(&set); + timeval tvout = {0,0}; + + // If we were supplied with a timeout, make our socket temporarily non-blocking + if (timeout > 0) { + FD_SET((unsigned)fInputSocketNum, &set); + tvout.tv_sec = timeout; + tvout.tv_usec = 0; + makeSocketNonBlocking(fInputSocketNum); + } + if (connect(fInputSocketNum, (struct sockaddr*) &remoteName, sizeof remoteName) != 0) { + if (envir().getErrno() != EINPROGRESS && envir().getErrno() != EWOULDBLOCK) { + envir().setResultErrMsg("connect() failed: "); + break; + } + if (timeout > 0 && (select(fInputSocketNum + 1, NULL, &set, NULL, &tvout) <= 0)) { + envir().setResultErrMsg("select/connect() failed: "); + break; + } + } + // If we set our socket to non-blocking, put it back in blocking mode now. + if (timeout > 0) { + makeSocketBlocking(fInputSocketNum); + } + + if (fTunnelOverHTTPPortNum != 0 && !setupHTTPTunneling(urlSuffix, authenticator)) break; + } + + return True; + } while (0); + + fDescribeStatusCode = 1; + resetTCPSockets(); + return False; +} + +Boolean RTSPClient::parseRTSPURL(UsageEnvironment& env, char const* url, + NetAddress& address, + portNumBits& portNum, + char const** urlSuffix) { + do { + // Parse the URL as "rtsp://
:/" + // (with ":" and "/" optional) + // Also, skip over any "[:]@" preceding
+ char const* prefix = "rtsp://"; + unsigned const prefixLength = 7; + if (_strncasecmp(url, prefix, prefixLength) != 0) { + env.setResultMsg("URL is not of the form \"", prefix, "\""); + break; + } + + unsigned const parseBufferSize = 100; + char parseBuffer[parseBufferSize]; + char const* from = &url[prefixLength]; + + // Skip over any "[:]@" + // (Note that this code fails if contains '@' or '/', but + // given that these characters can also appear in , there seems to + // be no way of unambiguously parsing that situation.) + char const* from1 = from; + while (*from1 != '\0' && *from1 != '/') { + if (*from1 == '@') { + from = ++from1; + break; + } + ++from1; + } + + char* to = &parseBuffer[0]; + unsigned i; + for (i = 0; i < parseBufferSize; ++i) { + if (*from == '\0' || *from == ':' || *from == '/') { + // We've completed parsing the address + *to = '\0'; + break; + } + *to++ = *from++; + } + if (i == parseBufferSize) { + env.setResultMsg("URL is too long"); + break; + } + + NetAddressList addresses(parseBuffer); + if (addresses.numAddresses() == 0) { + env.setResultMsg("Failed to find network address for \"", + parseBuffer, "\""); + break; + } + address = *(addresses.firstAddress()); + + portNum = 554; // default value + char nextChar = *from; + if (nextChar == ':') { + int portNumInt; + if (sscanf(++from, "%d", &portNumInt) != 1) { + env.setResultMsg("No port number follows ':'"); + break; + } + if (portNumInt < 1 || portNumInt > 65535) { + env.setResultMsg("Bad port number"); + break; + } + portNum = (portNumBits)portNumInt; + while (*from >= '0' && *from <= '9') ++from; // skip over port number + } + + // The remainder of the URL is the suffix: + if (urlSuffix != NULL) *urlSuffix = from; + + return True; + } while (0); + + return False; +} + +Boolean RTSPClient::parseRTSPURLUsernamePassword(char const* url, + char*& username, + char*& password) { + username = password = NULL; // by default + do { + // Parse the URL as "rtsp://[:]@" + char const* prefix = "rtsp://"; + unsigned const prefixLength = 7; + if (_strncasecmp(url, prefix, prefixLength) != 0) break; + + // Look for the ':' and '@': + size_t usernameIndex = prefixLength; + size_t colonIndex = 0, atIndex = 0; + for (size_t i = usernameIndex; url[i] != '\0' && url[i] != '/'; ++i) { + if (url[i] == ':' && colonIndex == 0) { + colonIndex = i; + } else if (url[i] == '@') { + atIndex = i; + break; // we're done + } + } + if (atIndex == 0) break; // no '@' found + + char* urlCopy = strDup(url); + urlCopy[atIndex] = '\0'; + if (colonIndex > 0) { + urlCopy[colonIndex] = '\0'; + password = strDup(&urlCopy[colonIndex+1]); + } else { + password = strDup(""); + } + username = strDup(&urlCopy[usernameIndex]); + delete[] urlCopy; + + return True; + } while (0); + + return False; +} + +char* +RTSPClient::createAuthenticatorString(Authenticator const* authenticator, + char const* cmd, char const* url) { + if (authenticator != NULL && authenticator->realm() != NULL + && authenticator->username() != NULL && authenticator->password() != NULL) { + // We've been provided a filled-in authenticator, so use it: + char* authenticatorStr; + if (authenticator->nonce() != NULL) { // Digest authentication + char const* const authFmt = + "Authorization: Digest username=\"%s\", realm=\"%s\", " + "nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n"; + char const* response = authenticator->computeDigestResponse(cmd, url); + size_t authBufSize = strlen(authFmt) + + strlen(authenticator->username()) + strlen(authenticator->realm()) + + strlen(authenticator->nonce()) + strlen(url) + strlen(response) + 1; + authenticatorStr = new char[authBufSize]; + snprintf(authenticatorStr, (authBufSize - 1), authFmt, + authenticator->username(), authenticator->realm(), + authenticator->nonce(), url, response); + authenticatorStr[authBufSize - 1] = '\0'; + authenticator->reclaimDigestResponse(response); + } else { // Basic authentication + char const* const authFmt = "Authorization: Basic %s\r\n"; + + size_t usernamePasswordLength = strlen(authenticator->username()) + 1 + strlen(authenticator->password()) + 1; + char* usernamePassword = new char[usernamePasswordLength+1]; + snprintf(usernamePassword, (usernamePasswordLength - 1), "%s:%s", authenticator->username(), authenticator->password()); + usernamePassword[usernamePasswordLength - 1] = '\0'; + + char* response = base64Encode(usernamePassword, usernamePasswordLength); + size_t const authBufSize = strlen(authFmt) + strlen(response) + 1; + authenticatorStr = new char[authBufSize]; + snprintf(authenticatorStr, (authBufSize - 1), authFmt, response); + authenticatorStr[authBufSize - 1] = '\0'; + delete[] response; delete[] usernamePassword; + } + + return authenticatorStr; + } + + return strDup(""); +} + +void RTSPClient::checkForAuthenticationFailure(unsigned responseCode, + char*& nextLineStart, + Authenticator* authenticator) { + if (responseCode == 401 && authenticator != NULL) { + // We have an authentication failure, so fill in "authenticator" + // using the contents of a following "WWW-Authenticate:" line. + // (Once we compute a 'response' for "authenticator", it can be + // used in a subsequent request - that will hopefully succeed.) + while (1) { + char* lineStart = nextLineStart; + if (lineStart == NULL) break; + + nextLineStart = getLine(lineStart); + if (lineStart[0] == '\0') break; // this is a blank line + + char* realm = strDupSize(lineStart); + char* nonce = strDupSize(lineStart); + Boolean foundAuthenticateHeader = False; + if (sscanf(lineStart, "WWW-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", + realm, nonce) == 2) { + authenticator->setRealmAndNonce(realm, nonce); + foundAuthenticateHeader = True; + } else if (sscanf(lineStart, "WWW-Authenticate: Basic realm=\"%[^\"]\"", + realm) == 1) { + authenticator->setRealmAndNonce(realm, NULL); // Basic authentication + foundAuthenticateHeader = True; + } + delete[] realm; delete[] nonce; + if (foundAuthenticateHeader) break; + } + } +} + +Boolean RTSPClient::sendRequest(char const* requestString, char const* tag, + Boolean base64EncodeIfOverHTTP) { + if (fVerbosityLevel >= 1) { + envir() << "Sending request: " << requestString << "\n"; + } + + char* newRequestString = NULL; + if (fTunnelOverHTTPPortNum != 0 && base64EncodeIfOverHTTP) { + requestString = newRequestString = base64Encode(requestString, strlen(requestString)); + if (fVerbosityLevel >= 1) { + envir() << "\tThe request was base-64 encoded to: " << requestString << "\n\n"; + } + } + + Boolean result + = send(fOutputSocketNum, requestString, strlen(requestString), 0) >= 0; + delete[] newRequestString; + + if (!result) { + if (tag == NULL) tag = ""; + char const* errFmt = "%s send() failed: "; + size_t const errLength = strlen(errFmt) + strlen(tag) + 1; + char* err = new char[errLength]; + snprintf(err, (errLength - 1), errFmt, tag); + err[errLength - 1] = '\0'; + envir().setResultErrMsg(err); + delete[] err; + } + return result; +} + +Boolean RTSPClient::getResponse(char const* tag, + unsigned& bytesRead, unsigned& responseCode, + char*& firstLine, char*& nextLineStart, + Boolean checkFor200Response) { + do { + char* readBuf = fResponseBuffer; + bytesRead = getResponse1(readBuf, fResponseBufferSize); + if (bytesRead == 0) { + envir().setResultErrMsg("Failed to read response: "); + break; + } + if (fVerbosityLevel >= 1) { + envir() << "Received " << tag << " response: " << readBuf << "\n"; + } + + firstLine = readBuf; + nextLineStart = getLine(firstLine); + if (!parseResponseCode(firstLine, responseCode)) break; + + + if (responseCode != 200 && checkFor200Response) { + envir().setResultMsg(tag, ": cannot handle response: ", firstLine); + break; + } + + return True; + } while (0); + + // An error occurred: + return False; +} + +unsigned RTSPClient::getResponse1(char*& responseBuffer, + unsigned responseBufferSize) { + struct sockaddr_in fromAddress; + + if (responseBufferSize == 0) return 0; // just in case... + responseBuffer[0] = '\0'; // ditto + + // Begin by reading and checking the first byte of the response. + // If it's '$', then there's an interleaved RTP (or RTCP)-over-TCP + // packet here. We need to read and discard it first. + Boolean success = False; + while (1) { + unsigned char firstByte; + struct timeval timeout; + timeout.tv_sec = 30; timeout.tv_usec = 0; + if (readSocket(envir(), fInputSocketNum, &firstByte, 1, fromAddress, &timeout) + != 1) break; + if (firstByte != '$') { + // Normal case: This is the start of a regular response; use it: + responseBuffer[0] = firstByte; + success = True; + break; + } else { + // This is an interleaved packet; read and discard it: + unsigned char streamChannelId; + if (readSocket(envir(), fInputSocketNum, &streamChannelId, 1, fromAddress) + != 1) break; + + unsigned short size; + if (readSocketExact(envir(), fInputSocketNum, (unsigned char*)&size, 2, + fromAddress) != 2) break; + size = ntohs(size); + if (fVerbosityLevel >= 1) { + envir() << "Discarding interleaved RTP or RTCP packet (" + << size << " bytes, channel id " + << streamChannelId << ")\n"; + } + + unsigned char* tmpBuffer = new unsigned char[size]; + if (tmpBuffer == NULL) break; + unsigned bytesRead = 0; + unsigned bytesToRead = size; + int curBytesRead; + while ((curBytesRead = readSocket(envir(), fInputSocketNum, + &tmpBuffer[bytesRead], bytesToRead, + fromAddress)) > 0) { + bytesRead += curBytesRead; + if (bytesRead >= size) break; + bytesToRead -= curBytesRead; + } + delete[] tmpBuffer; + if (bytesRead != size) break; + + success = True; + } + } + if (!success) return 0; + + // Keep reading data from the socket until we see "\r\n\r\n" (except + // at the start), or until we fill up our buffer. + // Don't read any more than this. + char* p = responseBuffer; + Boolean haveSeenNonCRLF = False; + int bytesRead = 1; // because we've already read the first byte + while (bytesRead < (int)responseBufferSize) { + int bytesReadNow + = readSocket(envir(), fInputSocketNum, + (unsigned char*)(responseBuffer+bytesRead), + 1, fromAddress); + if (bytesReadNow <= 0) { + envir().setResultMsg("RTSP response was truncated"); + break; + } + bytesRead += bytesReadNow; + + // Check whether we have "\r\n\r\n" (or "\r\r" or "\n\n"): + char* lastToCheck = responseBuffer+bytesRead-4; + if (lastToCheck < responseBuffer) continue; + for (; p <= lastToCheck; ++p) { + if (haveSeenNonCRLF) { + if ((*p == '\r' && *(p+1) == '\n' && *(p+2) == '\r' && *(p+3) == '\n') + || (*(p+2) == '\r' && *(p+3) == '\r') + || (*(p+2) == '\n' && *(p+3) == '\n')) + { + responseBuffer[bytesRead] = '\0'; + + // Before returning, trim any \r or \n from the start: + while (*responseBuffer == '\r' || *responseBuffer == '\n') { + ++responseBuffer; + --bytesRead; + } + return bytesRead; + } + } else { + if (*p != '\r' && *p != '\n') { + haveSeenNonCRLF = True; + } + } + } + } + + envir().setResultMsg("We received a response not ending with "); + return 0; +} + +Boolean RTSPClient::parseResponseCode(char const* line, + unsigned& responseCode) { + if (sscanf(line, "%*s%u", &responseCode) != 1) { + envir().setResultMsg("no response code in line: \"", line, "\""); + return False; + } + + return True; +} + +Boolean RTSPClient::parseTransportResponse(char const* line, + char*& serverAddressStr, + portNumBits& serverPortNum, + unsigned char& rtpChannelId, + unsigned char& rtcpChannelId) { + // Initialize the return parameters to 'not found' values: + serverAddressStr = NULL; + serverPortNum = 0; + rtpChannelId = rtcpChannelId = 0xFF; + + char* foundServerAddressStr = NULL; + Boolean foundServerPortNum = False; + Boolean foundChannelIds = False; + unsigned rtpCid, rtcpCid; + Boolean isMulticast = True; // by default + char* foundDestinationStr = NULL; + portNumBits multicastPortNumRTP, multicastPortNumRTCP; + Boolean foundMulticastPortNum = False; + + // First, check for "Transport:" + if (_strncasecmp(line, "Transport: ", 11) != 0) return False; + line += 11; + + // Then, run through each of the fields, looking for ones we handle: + char const* fields = line; + char* field = strDupSize(fields); + while (sscanf(fields, "%[^;]", field) == 1) { + if (sscanf(field, "server_port=%hu", &serverPortNum) == 1) { + foundServerPortNum = True; + } else if (_strncasecmp(field, "source=", 7) == 0) { + delete[] foundServerAddressStr; + foundServerAddressStr = strDup(field+7); + } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) { + rtpChannelId = (unsigned char)rtpCid; + rtcpChannelId = (unsigned char)rtcpCid; + foundChannelIds = True; + } else if (strcmp(field, "unicast") == 0) { + isMulticast = False; + } else if (_strncasecmp(field, "destination=", 12) == 0) { + delete[] foundDestinationStr; + foundDestinationStr = strDup(field+12); + } else if (sscanf(field, "port=%hu-%hu", + &multicastPortNumRTP, &multicastPortNumRTCP) == 2) { + foundMulticastPortNum = True; + } + + fields += strlen(field); + while (fields[0] == ';') ++fields; // skip over all leading ';' chars + if (fields[0] == '\0') break; + } + delete[] field; + + // If we're multicast, and have a "destination=" (multicast) address, then use this + // as the 'server' address (because some weird servers don't specify the multicast + // address earlier, in the "DESCRIBE" response's SDP: + if (isMulticast && foundDestinationStr != NULL && foundMulticastPortNum) { + delete[] foundServerAddressStr; + serverAddressStr = foundDestinationStr; + serverPortNum = multicastPortNumRTP; + return True; + } + delete[] foundDestinationStr; + + if (foundServerPortNum || foundChannelIds) { + serverAddressStr = foundServerAddressStr; + return True; + } + + delete[] foundServerAddressStr; + return False; +} + +Boolean RTSPClient::parseRTPInfoHeader(char*& line, u_int16_t& seqNum, u_int32_t& timestamp) { + // At this point in the parsing, "line" should begin with either "RTP-Info: " (for the start of the header), + // or ",", indicating the RTP-Info parameter list for the 2nd-through-nth subsessions: + if (_strncasecmp(line, "RTP-Info: ", 10) == 0) { + line += 10; + } else if (line[0] == ',') { + ++line; + } else { + return False; + } + + // "line" now consists of a ';'-separated list of parameters, ending with ',' or '\0'. + char* field = strDupSize(line); + + while (sscanf(line, "%[^;,]", field) == 1) { + if (sscanf(field, "seq=%hu", &seqNum) == 1 || + sscanf(field, "rtptime=%u", ×tamp) == 1) { + } + + line += strlen(field); + if (line[0] == '\0' || line[0] == ',') break; + // ASSERT: line[0] == ';' + ++line; // skip over the ';' + } + + delete[] field; + return True; +} + +Boolean RTSPClient::parseScaleHeader(char const* line, float& scale) { + if (_strncasecmp(line, "Scale: ", 7) != 0) return False; + line += 7; + + Locale l("C", Numeric); + return sscanf(line, "%f", &scale) == 1; +} + +Boolean RTSPClient::parseGetParameterHeader(char const* line, + const char* param, + char*& value) { + if ((param != NULL && param[0] != '\0') && + (line != NULL && line[0] != '\0')) { + int param_len = strlen(param); + int line_len = strlen(line); + + if (_strncasecmp(line, param, param_len) != 0) { + if (fVerbosityLevel >= 1) { + envir() << "Parsing for \""<< param << "\" and didn't find it, return False\n"; + } + return False; + } + + // Strip \r\n from the end if it's there. + if (line[line_len-2] == '\r' && line[line_len-1] == '\n') { + line_len -= 2; + } + + // Look for ": " appended to our requested parameter + if (line[param_len] == ':' && line[param_len+1] == ' ') { + // But make sure ": " wasn't in our actual serach param before adjusting + if (param[param_len-2] != ':' && param[param_len-1] != ' ') { + if (fVerbosityLevel >= 1) { + envir() << "Found \": \" appended to parameter\n"; + } + param_len += 2; + } + } + + // Get the string we want out of the line: + value = strDup(line+param_len); + return True; + } + return False; +} + +Boolean RTSPClient::setupHTTPTunneling(char const* urlSuffix, + Authenticator* authenticator) { + // Set up RTSP-over-HTTP tunneling, as described in + // http://developer.apple.com/documentation/QuickTime/QTSS/Concepts/chapter_2_section_14.html + if (fVerbosityLevel >= 1) { + envir() << "Requesting RTSP-over-HTTP tunneling (on port " + << fTunnelOverHTTPPortNum << ")\n\n"; + } + if (urlSuffix == NULL || urlSuffix[0] == '\0') urlSuffix = "/"; + char* cmd = NULL; + + do { + // Create a 'session cookie' string, using MD5: + struct { + struct timeval timestamp; + unsigned counter; + } seedData; + gettimeofday(&seedData.timestamp, NULL); + static unsigned counter = 0; + seedData.counter = ++counter; + char sessionCookie[33]; + our_MD5Data((unsigned char*)(&seedData), sizeof seedData, sessionCookie); + // DSS seems to require that the 'session cookie' string be 22 bytes long: + sessionCookie[23] = '\0'; + + // Construct an authenticator string: + char* authenticatorStr + = createAuthenticatorString(authenticator, "GET", urlSuffix); + + // Begin by sending a HTTP "GET", to set up the server->client link: + char const* const getCmdFmt = + "GET %s HTTP/1.0\r\n" + "%s" + "%s" + "x-sessioncookie: %s\r\n" + "Accept: application/x-rtsp-tunnelled\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"; + unsigned cmdSize = strlen(getCmdFmt) + + strlen(urlSuffix) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + + strlen(sessionCookie); + cmd = new char[cmdSize]; + sprintf(cmd, getCmdFmt, + urlSuffix, + authenticatorStr, + fUserAgentHeaderStr, + sessionCookie); + delete[] authenticatorStr; + if (!sendRequest(cmd, "HTTP GET", False/*don't base64-encode*/)) break; + + // Get the response from the server: + unsigned bytesRead; unsigned responseCode; + char* firstLine; char* nextLineStart; + if (!getResponse("HTTP GET", bytesRead, responseCode, firstLine, nextLineStart, + False /*don't check for response code 200*/)) break; + if (responseCode != 200) { + checkForAuthenticationFailure(responseCode, nextLineStart, authenticator); + envir().setResultMsg("cannot handle HTTP GET response: ", firstLine); + break; + } + + // Next, set up a second TCP connection (to the same server & port as before) + // for the HTTP-tunneled client->server link. All future output will be to + // this socket. + fOutputSocketNum = setupStreamSocket(envir(), 0, False /* =>blocking */); + if (fOutputSocketNum < 0) break; + + // Connect to the remote endpoint: + MAKE_SOCKADDR_IN(remoteName, fServerAddress, htons(fTunnelOverHTTPPortNum)); + if (connect(fOutputSocketNum, + (struct sockaddr*)&remoteName, sizeof remoteName) != 0) { + envir().setResultErrMsg("connect() failed: "); + break; + } + + // Then, send a HTTP "POST", to set up the client->server link: + authenticatorStr = createAuthenticatorString(authenticator, "POST", urlSuffix); + char const* const postCmdFmt = + "POST %s HTTP/1.0\r\n" + "%s" + "%s" + "x-sessioncookie: %s\r\n" + "Content-Type: application/x-rtsp-tunnelled\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "Content-Length: 32767\r\n" + "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n" + "\r\n"; + cmdSize = strlen(postCmdFmt) + + strlen(urlSuffix) + + strlen(authenticatorStr) + + fUserAgentHeaderStrSize + + strlen(sessionCookie); + delete[] cmd; cmd = new char[cmdSize]; + sprintf(cmd, postCmdFmt, + urlSuffix, + authenticatorStr, + fUserAgentHeaderStr, + sessionCookie); + delete[] authenticatorStr; + if (!sendRequest(cmd, "HTTP POST", False/*don't base64-encode*/)) break; + + // Note that there's no response to the "POST". + + delete[] cmd; + return True; + } while (0); + + // An error occurred: + delete[] cmd; + return False; +} + +void RTSPClient::incomingRequestHandler(void* instance, int /*mask*/) { + RTSPClient* session = (RTSPClient*)instance; + session->incomingRequestHandler1(); +} + +void RTSPClient::incomingRequestHandler1() { + unsigned bytesRead; + char* readBuf = fResponseBuffer; + bytesRead = getResponse1(readBuf, fResponseBufferSize); + if (bytesRead == 0) { + envir().setResultMsg("Failed to read response: Connection was closed by the remote host."); + envir().taskScheduler().turnOffBackgroundReadHandling(fInputSocketNum); // because the connection died + return; + } + // Parse the request string into command name and 'CSeq', + // then handle the command: + char cmdName[RTSP_PARAM_STRING_MAX]; + char urlPreSuffix[RTSP_PARAM_STRING_MAX]; + char urlSuffix[RTSP_PARAM_STRING_MAX]; + char cseq[RTSP_PARAM_STRING_MAX]; + if (!parseRTSPRequestString((char*)readBuf, bytesRead, + cmdName, sizeof cmdName, + urlPreSuffix, sizeof urlPreSuffix, + urlSuffix, sizeof urlSuffix, + cseq, sizeof cseq)) { + return; + } else { + if (fVerbosityLevel >= 1) { + envir() << "Received request: " << readBuf << "\n"; + } + handleCmd_notSupported(cseq); + } +} + +void RTSPClient::handleCmd_notSupported(char const* cseq) { + char tmpBuf[512]; + snprintf((char*)tmpBuf, sizeof tmpBuf, + "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n\r\n", cseq); + int retval = send(fOutputSocketNum, tmpBuf, strlen(tmpBuf), 0); + if (retval == SOCKET_ERROR) + { + envir().setResultErrMsg("send() failed: "); + } +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTSPCommon.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTSPCommon.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/RTSPCommon.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/RTSPCommon.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,168 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Common routines used by both RTSP clients and servers +// Implementation + +#include "RTSPCommon.hh" +#include "Locale.hh" +#include +#include +#include // for "strftime()" and "gmtime()" + +#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4) +#else +#include +#define USE_SIGNALS 1 +#endif + +Boolean parseRTSPRequestString(char const* reqStr, + unsigned reqStrSize, + char* resultCmdName, + unsigned resultCmdNameMaxSize, + char* resultURLPreSuffix, + unsigned resultURLPreSuffixMaxSize, + char* resultURLSuffix, + unsigned resultURLSuffixMaxSize, + char* resultCSeq, + unsigned resultCSeqMaxSize) { + // This parser is currently rather dumb; it should be made smarter ##### + + // Read everything up to the first space as the command name: + Boolean parseSucceeded = False; + unsigned i; + for (i = 0; i < resultCmdNameMaxSize-1 && i < reqStrSize; ++i) { + char c = reqStr[i]; + if (c == ' ' || c == '\t') { + parseSucceeded = True; + break; + } + + resultCmdName[i] = c; + } + resultCmdName[i] = '\0'; + if (!parseSucceeded) return False; + + // Skip over the prefix of any "rtsp://" or "rtsp:/" URL that follows: + unsigned j = i+1; + while (j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t')) ++j; // skip over any additional white space + for (; (int)j < (int)(reqStrSize-8); ++j) { + if ((reqStr[j] == 'r' || reqStr[j] == 'R') + && (reqStr[j+1] == 't' || reqStr[j+1] == 'T') + && (reqStr[j+2] == 's' || reqStr[j+2] == 'S') + && (reqStr[j+3] == 'p' || reqStr[j+3] == 'P') + && reqStr[j+4] == ':' && reqStr[j+5] == '/') { + j += 6; + if (reqStr[j] == '/') { + // This is a "rtsp://" URL; skip over the host:port part that follows: + ++j; + while (j < reqStrSize && reqStr[j] != '/' && reqStr[j] != ' ') ++j; + } else { + // This is a "rtsp:/" URL; back up to the "/": + --j; + } + i = j; + break; + } + } + + // Look for the URL suffix (before the following "RTSP/"): + parseSucceeded = False; + for (unsigned k = i+1; (int)k < (int)(reqStrSize-5); ++k) { + if (reqStr[k] == 'R' && reqStr[k+1] == 'T' && + reqStr[k+2] == 'S' && reqStr[k+3] == 'P' && reqStr[k+4] == '/') { + while (--k >= i && reqStr[k] == ' ') {} // go back over all spaces before "RTSP/" + unsigned k1 = k; + while (k1 > i && reqStr[k1] != '/') --k1; + // the URL suffix comes from [k1+1,k] + + // Copy "resultURLSuffix": + if (k - k1 + 1 > resultURLSuffixMaxSize) return False; // there's no room + unsigned n = 0, k2 = k1+1; + while (k2 <= k) resultURLSuffix[n++] = reqStr[k2++]; + resultURLSuffix[n] = '\0'; + + // Also look for the URL 'pre-suffix' before this: + unsigned k3 = (k1 == 0) ? 0 : --k1; + while (k3 > i && reqStr[k3] != '/') --k3; + // the URL pre-suffix comes from [k3+1,k1] + + // Copy "resultURLPreSuffix": + if (k1 - k3 + 1 > resultURLPreSuffixMaxSize) return False; // there's no room + n = 0; k2 = k3+1; + while (k2 <= k1) resultURLPreSuffix[n++] = reqStr[k2++]; + resultURLPreSuffix[n] = '\0'; + + i = k + 7; // to go past " RTSP/" + parseSucceeded = True; + break; + } + } + if (!parseSucceeded) return False; + + // Look for "CSeq:", skip whitespace, + // then read everything up to the next \r or \n as 'CSeq': + parseSucceeded = False; + for (j = i; (int)j < (int)(reqStrSize-5); ++j) { + if (reqStr[j] == 'C' && reqStr[j+1] == 'S' && reqStr[j+2] == 'e' && + reqStr[j+3] == 'q' && reqStr[j+4] == ':') { + j += 5; + unsigned n; + while (j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t')) ++j; + for (n = 0; n < resultCSeqMaxSize-1 && j < reqStrSize; ++n,++j) { + char c = reqStr[j]; + if (c == '\r' || c == '\n') { + parseSucceeded = True; + break; + } + + resultCSeq[n] = c; + } + resultCSeq[n] = '\0'; + break; + } + } + if (!parseSucceeded) return False; + + return True; +} + +Boolean parseRangeHeader(char const* buf, double& rangeStart, double& rangeEnd) { + // First, find "Range:" + while (1) { + if (*buf == '\0') return False; // not found + if (_strncasecmp(buf, "Range: ", 7) == 0) break; + ++buf; + } + + // Then, run through each of the fields, looking for ones we handle: + char const* fields = buf + 7; + while (*fields == ' ') ++fields; + double start, end; + Locale l("C", Numeric); + if (sscanf(fields, "npt = %lf - %lf", &start, &end) == 2) { + rangeStart = start; + rangeEnd = end; + } else if (sscanf(fields, "npt = %lf -", &start) == 1) { + rangeStart = start; + rangeEnd = 0.0; + } else { + return False; // The header is malformed + } + + return True; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/SimpleRTPSink.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/SimpleRTPSink.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/SimpleRTPSink.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/SimpleRTPSink.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,89 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// A simple RTP sink that packs frames into each outgoing +// packet, without any fragmentation or special headers. +// Implementation + +#include "SimpleRTPSink.hh" + +SimpleRTPSink::SimpleRTPSink(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + char const* sdpMediaTypeString, + char const* rtpPayloadFormatName, + unsigned numChannels, + Boolean allowMultipleFramesPerPacket, + Boolean doNormalMBitRule) + : MultiFramedRTPSink(env, RTPgs, rtpPayloadFormat, + rtpTimestampFrequency, rtpPayloadFormatName, + numChannels), + fAllowMultipleFramesPerPacket(allowMultipleFramesPerPacket) { + fSDPMediaTypeString + = strDup(sdpMediaTypeString == NULL ? "unknown" : sdpMediaTypeString); + fSetMBitOnLastFrames = doNormalMBitRule && strcmp(fSDPMediaTypeString, "audio") != 0; +} + +SimpleRTPSink::~SimpleRTPSink() { + delete[] (char*)fSDPMediaTypeString; +} + +SimpleRTPSink* +SimpleRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + char const* sdpMediaTypeString, + char const* rtpPayloadFormatName, + unsigned numChannels, + Boolean allowMultipleFramesPerPacket, + Boolean doNormalMBitRule) { + return new SimpleRTPSink(env, RTPgs, + rtpPayloadFormat, rtpTimestampFrequency, + sdpMediaTypeString, rtpPayloadFormatName, + numChannels, + allowMultipleFramesPerPacket, + doNormalMBitRule); +} + +void SimpleRTPSink::doSpecialFrameHandling(unsigned fragmentationOffset, + unsigned char* frameStart, + unsigned numBytesInFrame, + struct timeval framePresentationTime, + unsigned numRemainingBytes) { + if (numRemainingBytes == 0) { + // This packet contains the last (or only) fragment of the frame. + // Set the RTP 'M' ('marker') bit, if appropriate: + if (fSetMBitOnLastFrames) setMarkerBit(); + } + + // Important: Also call our base class's doSpecialFrameHandling(), + // to set the packet's timestamp: + MultiFramedRTPSink::doSpecialFrameHandling(fragmentationOffset, + frameStart, numBytesInFrame, + framePresentationTime, + numRemainingBytes); +} + +Boolean SimpleRTPSink:: +frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/, + unsigned /*numBytesInFrame*/) const { + return fAllowMultipleFramesPerPacket; +} + +char const* SimpleRTPSink::sdpMediaType() const { + return fSDPMediaTypeString; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/SimpleRTPSource.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/SimpleRTPSource.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/liveMedia/SimpleRTPSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/liveMedia/SimpleRTPSource.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,68 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// "liveMedia" +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// A RTP source for a simple RTP payload format that +// - doesn't have any special headers following the RTP header +// - doesn't have any special framing apart from the packet data itself +// Implementation + +#include "SimpleRTPSource.hh" +#include + +SimpleRTPSource* +SimpleRTPSource::createNew(UsageEnvironment& env, + Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + char const* mimeTypeString, + unsigned offset, Boolean doNormalMBitRule) { + return new SimpleRTPSource(env, RTPgs, rtpPayloadFormat, + rtpTimestampFrequency, + mimeTypeString, offset, doNormalMBitRule); +} + +SimpleRTPSource +::SimpleRTPSource(UsageEnvironment& env, Groupsock* RTPgs, + unsigned char rtpPayloadFormat, + unsigned rtpTimestampFrequency, + char const* mimeTypeString, + unsigned offset, Boolean doNormalMBitRule) + : MultiFramedRTPSource(env, RTPgs, + rtpPayloadFormat, rtpTimestampFrequency), + fMIMEtypeString(strDup(mimeTypeString)), fOffset(offset) { + fUseMBitForFrameEnd = doNormalMBitRule && strncmp(mimeTypeString, "audio/", 6) != 0; +} + +SimpleRTPSource::~SimpleRTPSource() { + delete[] (char*)fMIMEtypeString; +} + +Boolean SimpleRTPSource +::processSpecialHeader(BufferedPacket* packet, + unsigned& resultSpecialHeaderSize) { + fCurrentPacketCompletesFrame + = !fUseMBitForFrameEnd || packet->rtpMarkerBit(); + + resultSpecialHeaderSize = fOffset; + return True; +} + +char const* SimpleRTPSource::MIMEtype() const { + if (fMIMEtypeString == NULL) return MultiFramedRTPSource::MIMEtype(); + + return fMIMEtypeString; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/COPYING kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/COPYING --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/COPYING 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/COPYING 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1 @@ +../COPYING \ No newline at end of file diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/HashTable.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/HashTable.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/HashTable.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/HashTable.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,50 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Generic Hash Table +// Implementation + +#include "HashTable.hh" + +HashTable::HashTable() { +} + +HashTable::~HashTable() { +} + +HashTable::Iterator::Iterator() { +} + +HashTable::Iterator::~Iterator() {} + +void* HashTable::RemoveNext() { + Iterator* iter = Iterator::create(*this); + char const* key; + void* removedValue = iter->next(key); + if (removedValue != 0) Remove(key); + + delete iter; + return removedValue; +} + +void* HashTable::getFirst() { + Iterator* iter = Iterator::create(*this); + char const* key; + void* firstValue = iter->next(key); + + delete iter; + return firstValue; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/Boolean.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/Boolean.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/Boolean.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/Boolean.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,37 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +#ifndef _BOOLEAN_HH +#define _BOOLEAN_HH + +#if defined(__BORLANDC__) || (!defined(USE_LIVE555_BOOLEAN) && defined(_MSC_VER) && _MSC_VER >= 1400) +// Use the "bool" type defined by the Borland compiler, and MSVC++ 8.0, Visual Studio 2005 and higher +typedef bool Boolean; +#define False false +#define True true +#else +typedef unsigned char Boolean; +#ifndef __MSHTML_LIBRARY_DEFINED__ +#ifndef False +const Boolean False = 0; +#endif +#ifndef True +const Boolean True = 1; +#endif + +#endif +#endif + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/HashTable.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/HashTable.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/HashTable.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/HashTable.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,76 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// Generic Hash Table +// C++ header + +#ifndef _HASH_TABLE_HH +#define _HASH_TABLE_HH + +#ifndef _BOOLEAN_HH +#include "Boolean.hh" +#endif + +class HashTable { +public: + virtual ~HashTable(); + + // The following must be implemented by a particular + // implementation (subclass): + static HashTable* create(int keyType); + + virtual void* Add(char const* key, void* value) = 0; + // Returns the old value if different, otherwise 0 + virtual Boolean Remove(char const* key) = 0; + virtual void* Lookup(char const* key) const = 0; + // Returns 0 if not found + virtual unsigned numEntries() const = 0; + Boolean IsEmpty() const { return numEntries() == 0; } + + // Used to iterate through the members of the table: + class Iterator { + public: + // The following must be implemented by a particular + // implementation (subclass): + static Iterator* create(HashTable const& hashTable); + + virtual ~Iterator(); + + virtual void* next(char const*& key) = 0; // returns 0 if none + + protected: + Iterator(); // abstract base class + }; + + // A shortcut that can be used to successively remove each of + // the entries in the table (e.g., so that their values can be + // deleted, if they happen to be pointers to allocated memory). + void* RemoveNext(); + + // Returns the first entry in the table. + // (This is useful for deleting each entry in the table, if the entry's destructor also removes itself from the table.) + void* getFirst(); + +protected: + HashTable(); // abstract base class +}; + +// Warning: The following are deliberately the same as in +// Tcl's hash table implementation +int const STRING_HASH_KEYS = 0; +int const ONE_WORD_HASH_KEYS = 1; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/strDup.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/strDup.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/strDup.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/strDup.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,37 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ + +#ifndef _STRDUP_HH +#define _STRDUP_HH + +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// A C++ equivalent to the standard C routine "strdup()". +// This generates a char* that can be deleted using "delete[]" +// Header + +#include + +char* strDup(char const* str); +// Note: strDup(NULL) returns NULL + +char* strDupSize(char const* str); +// Like "strDup()", except that it *doesn't* copy the original. +// (Instead, it just allocates a string of the same size as the original.) + +char* strDupSize(char const* str, size_t& resultBufSize); +// An alternative form of "strDupSize()" that also returns the size of the allocated buffer. + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/UsageEnvironment.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/UsageEnvironment.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/UsageEnvironment.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/UsageEnvironment.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,152 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. +// Usage Environment +// C++ header + +#ifndef _USAGE_ENVIRONMENT_HH +#define _USAGE_ENVIRONMENT_HH + +#ifndef _USAGEENVIRONMENT_VERSION_HH +#include "UsageEnvironment_version.hh" +#endif + +#ifndef _NETCOMMON_H +#include "NetCommon.h" +#endif + +#ifndef _BOOLEAN_HH +#include "Boolean.hh" +#endif + +#ifndef _STRDUP_HH +// "strDup()" is used often, so include this here, so everyone gets it: +#include "strDup.hh" +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#ifdef __BORLANDC__ +#define _setmode setmode +#define _O_BINARY O_BINARY +#endif + +class TaskScheduler; // forward + +// An abstract base class, subclassed for each use of the library + +class UsageEnvironment { +public: + void reclaim(); + + // task scheduler: + TaskScheduler& taskScheduler() const {return fScheduler;} + + // result message handling: + typedef char const* MsgString; + virtual MsgString getResultMsg() const = 0; + + virtual void setResultMsg(MsgString msg) = 0; + virtual void setResultMsg(MsgString msg1, MsgString msg2) = 0; + virtual void setResultMsg(MsgString msg1, MsgString msg2, MsgString msg3) = 0; + virtual void setResultErrMsg(MsgString msg, int err = 0) = 0; + // like setResultMsg(), except that an 'errno' message is appended. (If "err == 0", the "getErrno()" code is used instead.) + + virtual void appendToResultMsg(MsgString msg) = 0; + + virtual void reportBackgroundError() = 0; + // used to report a (previously set) error message within + // a background event + + virtual void internalError(); // used to 'handle' a 'should not occur'-type error condition within the library. + + // 'errno' + virtual int getErrno() const = 0; + + // 'console' output: + virtual UsageEnvironment& operator<<(char const* str) = 0; + virtual UsageEnvironment& operator<<(int i) = 0; + virtual UsageEnvironment& operator<<(unsigned u) = 0; + virtual UsageEnvironment& operator<<(double d) = 0; + virtual UsageEnvironment& operator<<(void* p) = 0; + + // a pointer to additional, optional, client-specific state + void* liveMediaPriv; + void* groupsockPriv; + +protected: + UsageEnvironment(TaskScheduler& scheduler); // abstract base class + virtual ~UsageEnvironment(); // we are deleted only by reclaim() + +private: + TaskScheduler& fScheduler; +}; + + +typedef void TaskFunc(void* clientData); +typedef void* TaskToken; + +class TaskScheduler { +public: + virtual ~TaskScheduler(); + + virtual TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc, + void* clientData) = 0; + // Schedules a task to occur (after a delay) when we next + // reach a scheduling point. + // (Does not delay if "microseconds" <= 0) + // Returns a token that can be used in a subsequent call to + // unscheduleDelayedTask() + + virtual void unscheduleDelayedTask(TaskToken& prevTask) = 0; + // (Has no effect if "prevTask" == NULL) + // Sets "prevTask" to NULL afterwards. + + virtual void rescheduleDelayedTask(TaskToken& task, + int64_t microseconds, TaskFunc* proc, + void* clientData); + // Combines "unscheduleDelayedTask()" with "scheduleDelayedTask()" + // (setting "task" to the new task token). + + // For handling socket reads in the background: + typedef void BackgroundHandlerProc(void* clientData, int mask); + // Possible bits to set in "mask". (These are deliberately defined + // the same as those in Tcl, to make a Tcl-based subclass easy.) + #define SOCKET_READABLE (1<<1) + #define SOCKET_WRITABLE (1<<2) + #define SOCKET_EXCEPTION (1<<3) + virtual void turnOnBackgroundReadHandling(int socketNum, + BackgroundHandlerProc* handlerProc, + void* clientData) = 0; + virtual void turnOffBackgroundReadHandling(int socketNum) = 0; + virtual void moveSocketHandling(int oldSocketNum, int newSocketNum) = 0; + // Changes any socket handling for "oldSocketNum" so that occurs with "newSocketNum" instead. + + virtual void doEventLoop(char* watchVariable = NULL) = 0; + // Stops the current thread of control from proceeding, + // but allows delayed tasks (and/or background I/O handling) + // to proceed. + // (If "watchVariable" is not NULL, then we return from this + // routine when *watchVariable != 0) + virtual void internalError(); // used to 'handle' a 'should not occur'-type error condition within the library. + +protected: + TaskScheduler(); // abstract base class +}; + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/UsageEnvironment_version.hh kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/UsageEnvironment_version.hh --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/include/UsageEnvironment_version.hh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/include/UsageEnvironment_version.hh 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,10 @@ +// Version information for the "UsageEnvironment" library +// Copyright (c) 1996-2010 Live Networks, Inc. All rights reserved. + +#ifndef _USAGEENVIRONMENT_VERSION_HH +#define _USAGEENVIRONMENT_VERSION_HH + +#define USAGEENVIRONMENT_LIBRARY_VERSION_STRING "2010.03.16" +#define USAGEENVIRONMENT_LIBRARY_VERSION_INT 1268697600 + +#endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/strDup.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/strDup.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/strDup.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/strDup.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,50 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. +// A C++ equivalent to the standard C routine "strdup()". +// This generates a char* that can be deleted using "delete[]" +// Implementation + +#include "strDup.hh" + +char* strDup(char const* str) { + if (str == NULL) return NULL; + size_t len = strlen(str) + 1; + char* copy = new char[len]; + + if (copy != NULL) { + memcpy(copy, str, len); + } + return copy; +} + +char* strDupSize(char const* str) { + size_t dummy; + + return strDupSize(str, dummy); +} + +char* strDupSize(char const* str, size_t& resultBufSize) { + if (str == NULL) { + resultBufSize = 0; + return NULL; + } + + resultBufSize = strlen(str) + 1; + char* copy = new char[resultBufSize]; + + return copy; +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/UsageEnvironment.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/UsageEnvironment.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/live555/UsageEnvironment/UsageEnvironment.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/live555/UsageEnvironment/UsageEnvironment.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,57 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) + +This library 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 Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2012 Live Networks, Inc. All rights reserved. +// Usage Environment +// Implementation + +#include "UsageEnvironment.hh" + +void UsageEnvironment::reclaim() { + // We delete ourselves only if we have no remainining state: + if (liveMediaPriv == NULL && groupsockPriv == NULL) delete this; +} + +UsageEnvironment::UsageEnvironment(TaskScheduler& scheduler) + : liveMediaPriv(NULL), groupsockPriv(NULL), fScheduler(scheduler) { +} + +UsageEnvironment::~UsageEnvironment() { +} + +// By default, we handle 'should not occur'-type library errors by calling abort(). Subclasses can redefine this, if desired. +// (If your runtime library doesn't define the "abort()" function, then define your own (e.g., that does nothing).) +void UsageEnvironment::internalError() { + abort(); +} + + +TaskScheduler::TaskScheduler() { +} + +TaskScheduler::~TaskScheduler() { +} + +void TaskScheduler::rescheduleDelayedTask(TaskToken& task, + int64_t microseconds, TaskFunc* proc, + void* clientData) { + unscheduleDelayedTask(task); + task = scheduleDelayedTask(microseconds, proc, clientData); +} + +// By default, we handle 'should not occur'-type library errors by calling abort(). Subclasses can redefine this, if desired. +void TaskScheduler::internalError() { + abort(); +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/DeMultiplexer.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/DeMultiplexer.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/DeMultiplexer.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/DeMultiplexer.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -104,7 +104,7 @@ if (m_filter.IsSeeking()) return 0; // Ambass : to check - PLATFORM::CLockObject lock(m_sectionRead); + P8PLATFORM::CLockObject lock(m_sectionRead); if (NULL == m_reader) return false; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/DeMultiplexer.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/DeMultiplexer.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/DeMultiplexer.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/DeMultiplexer.h 2017-01-11 10:28:16.000000000 +0000 @@ -37,7 +37,7 @@ #include "PacketSync.h" #include "TSHeader.h" #include "PatParser.h" -#include "platform/threads/mutex.h" +#include "p8-platform/threads/mutex.h" namespace MPTV { @@ -60,7 +60,7 @@ private: unsigned long m_LastDataFromRtsp; bool m_bEndOfFile; - PLATFORM::CMutex m_sectionRead; + P8PLATFORM::CMutex m_sectionRead; FileReader* m_reader; CPatParser m_patParser; CTsReader& m_filter; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/DvbUtil.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/DvbUtil.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/DvbUtil.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/DvbUtil.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -94,12 +94,12 @@ { } - void CDvbUtil::getString468A(byte *buf, int bufLen, char *text, int textLen) + void CDvbUtil::getString468A(byte *buf, size_t bufLen, char *text, size_t textLen) { byte c; uint16_t w; - int bufIndex = 0, textIndex = 0; + size_t bufIndex = 0, textIndex = 0; if (buf == NULL) return; if (bufLen < 1) return; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/DvbUtil.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/DvbUtil.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/DvbUtil.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/DvbUtil.h 2017-01-11 10:28:16.000000000 +0000 @@ -31,7 +31,7 @@ { public: CDvbUtil(void); - void getString468A(byte *buf, int bufLen, char *text, int textLen); + void getString468A(byte *buf, size_t bufLen, char *text, size_t textLen); public: virtual ~CDvbUtil(void); }; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/FileReader.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/FileReader.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/FileReader.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/FileReader.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -35,10 +35,10 @@ #include "FileReader.h" #include "client.h" //for XBMC->Log #include "TSDebug.h" -#include "platform/threads/threads.h" +#include "p8-platform/threads/threads.h" #include //std::min, std::max -#include "platform/util/timeutils.h" // for usleep -#include "platform/util/util.h" +#include "p8-platform/util/timeutils.h" // for usleep +#include "p8-platform/util/util.h" #include "utils.h" #include @@ -63,7 +63,8 @@ { FileReader::FileReader() : m_hFile(NULL), - m_fileSize(0) + m_fileSize(0), + m_fileName("") { } diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/FileReader.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/FileReader.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/FileReader.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/FileReader.h 2017-01-11 10:28:16.000000000 +0000 @@ -33,8 +33,8 @@ * http://forums.dvbowners.com/ */ -#include "platform/os.h" -#include "platform/util/StdString.h" +#include "p8-platform/os.h" +#include "p8-platform/util/StdString.h" namespace MPTV { diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MemoryBuffer.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MemoryBuffer.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MemoryBuffer.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MemoryBuffer.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2005-2012 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 of the License, 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 this program. If not, see . + * + ************************************************************************* + * This file is a modified version from Team MediaPortal's + * TsReader DirectShow filter + * MediaPortal is a GPL'ed HTPC-Application + * Copyright (C) 2005-2012 Team MediaPortal + * http://www.team-mediaportal.com + * + * Changes compared to Team MediaPortal's version: + * - Code cleanup for PVR addon usage + * - Code refactoring for cross platform usage + */ + +#ifdef LIVE555 + +#include "p8-platform/util/timeutils.h" +#include "p8-platform/threads/mutex.h" +#include "p8-platform/util/util.h" +#include "MemoryBuffer.h" +#include "client.h" +#include "TSDebug.h" + +using namespace ADDON; + +#define MAX_MEMORY_BUFFER_SIZE (1024L*1024L*12L) + +CMemoryBuffer::CMemoryBuffer(void) +{ + m_bRunning = true; + m_BytesInBuffer = 0; +} + +CMemoryBuffer::~CMemoryBuffer() +{ + Clear(); +} + +bool CMemoryBuffer::IsRunning() +{ + return m_bRunning; +} + +void CMemoryBuffer::Clear() +{ + P8PLATFORM::CLockObject BufferLock(m_BufferLock); + std::vector::iterator it = m_Array.begin(); + + for ( ; it != m_Array.end(); ++it ) + { + BufferItem *item = *it; + SAFE_DELETE_ARRAY(item->data); + SAFE_DELETE(item); + } + + m_Array.clear(); + m_BytesInBuffer = 0; +} + +size_t CMemoryBuffer::Size() +{ + return m_BytesInBuffer; +} + +void CMemoryBuffer::Run(bool onOff) +{ + TSDEBUG(LOG_DEBUG, "memorybuffer: run:%d %d", onOff, m_bRunning); + + if (m_bRunning != onOff) + { + m_bRunning = onOff; + + if (m_bRunning == false) + { + Clear(); + } + } + + TSDEBUG(LOG_DEBUG, "memorybuffer: running:%d", onOff); +} + +size_t CMemoryBuffer::ReadFromBuffer(unsigned char *pbData, size_t lDataLength) +{ + if (pbData == NULL || lDataLength <= 0 || !m_bRunning) + return 0; + + while (m_BytesInBuffer < lDataLength) + { + if (!m_bRunning) + return 0; + m_event.Wait(5000); + if (!m_bRunning) + return 0; + } + + // XBMC->Log(LOG_DEBUG, "get..%d/%d", lDataLength, m_BytesInBuffer); + size_t bytesWritten = 0; + P8PLATFORM::CLockObject BufferLock(m_BufferLock); + + while (bytesWritten < lDataLength) + { + if (m_Array.empty()) + { + XBMC->Log(LOG_DEBUG, "memorybuffer: read:empty buffer\n"); + return 0; + } + BufferItem *item = m_Array.at(0); + + if (NULL == item) + { + XBMC->Log(LOG_DEBUG, "memorybuffer: item==NULL\n"); + return 0; + } + + size_t copyLength; + if ( (item->nDataLength - item->nOffset) < (lDataLength - bytesWritten) ) + { + copyLength = item->nDataLength - item->nOffset; + } + else + { + copyLength = lDataLength - bytesWritten; + } + + if (NULL == item->data) + { + XBMC->Log(LOG_DEBUG, "memorybuffer: item->data==NULL\n"); + return 0; + } + + memcpy(&pbData[bytesWritten], &item->data[item->nOffset], copyLength); + + bytesWritten += copyLength; + item->nOffset += copyLength; + m_BytesInBuffer -= copyLength; + + if (item->nOffset >= item->nDataLength) + { + m_Array.erase(m_Array.begin()); + SAFE_DELETE_ARRAY(item->data); + SAFE_DELETE(item); + } + } + return bytesWritten; +} + +long CMemoryBuffer::PutBuffer(unsigned char *pbData, size_t lDataLength) +{ + if (lDataLength <= 0 || pbData == NULL) return E_FAIL; + + BufferItem* item = new BufferItem(); + item->nOffset = 0; + item->nDataLength = lDataLength; + item->data = new byte[lDataLength]; + memcpy(item->data, pbData, lDataLength); + bool sleep = false; + { + P8PLATFORM::CLockObject BufferLock(m_BufferLock); + m_Array.push_back(item); + m_BytesInBuffer += lDataLength; + + //XBMC->Log(LOG_DEBUG, "add..%d/%d",lDataLength,m_BytesInBuffer); + while (m_BytesInBuffer > MAX_MEMORY_BUFFER_SIZE) + { + sleep = true; + XBMC->Log(LOG_DEBUG, "memorybuffer:put full buffer (%d)", (unsigned long) m_BytesInBuffer); + BufferItem *item2 = m_Array.at(0); + size_t copyLength = item2->nDataLength - item2->nOffset; + + m_BytesInBuffer -= copyLength; + m_Array.erase(m_Array.begin()); + SAFE_DELETE_ARRAY(item2->data); + SAFE_DELETE(item2); + } + if (m_BytesInBuffer > 0) + { + m_event.Broadcast(); + } + } + + if (sleep) + { + usleep(10000); + } + return S_OK; +} + +#endif //LIVE555 diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MemoryBuffer.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MemoryBuffer.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MemoryBuffer.h 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MemoryBuffer.h 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,63 @@ +#pragma once +/* + * Copyright (C) 2005-2012 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 of the License, 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 this program. If not, see . + * + ************************************************************************* + * This file is a modified version from Team MediaPortal's + * TsReader DirectShow filter + * MediaPortal is a GPL'ed HTPC-Application + * Copyright (C) 2005-2012 Team MediaPortal + * http://www.team-mediaportal.com + * + * Changes compared to Team MediaPortal's version: + * - Code cleanup for PVR addon usage + * - Code refactoring for cross platform usage + */ + +#ifdef LIVE555 + +#include "p8-platform/threads/mutex.h" +#include + +class CMemoryBuffer +{ + public: + CMemoryBuffer(void); + virtual ~CMemoryBuffer(void); + + size_t ReadFromBuffer(unsigned char *pbData, size_t lDataLength); + long PutBuffer(unsigned char *pbData, size_t lDataLength); + void Clear(); + size_t Size(); + void Run(bool onOff); + bool IsRunning(); + + typedef struct + { + unsigned char* data; + size_t nDataLength; + size_t nOffset; + } BufferItem; + + protected: + std::vector m_Array; + P8PLATFORM::CMutex m_BufferLock; + size_t m_BytesInBuffer; + P8PLATFORM::CEvent m_event; + bool m_bRunning; +}; +#endif //LIVE555 diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MemorySink.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MemorySink.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MemorySink.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MemorySink.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2012 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 of the License, 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 this program. If not, see . + * + ************************************************************************* + * This file is a modified version from Team MediaPortal's + * TsReader DirectShow filter + * MediaPortal is a GPL'ed HTPC-Application + * Copyright (C) 2005-2012 Team MediaPortal + * http://www.team-mediaportal.com + * + * Changes compared to Team MediaPortal's version: + * - Code cleanup for PVR addon usage + * - Code refactoring for cross platform usage + */ + +#if defined LIVE555 + +#include "p8-platform/os.h" +#include "MemorySink.h" +#include "GroupsockHelper.hh" +#include "client.h" +#include "utils.h" + +using namespace ADDON; + +#define SUBMIT_BUF_SIZE (1316*30) + +CMemorySink::CMemorySink(UsageEnvironment& env, CMemoryBuffer& buffer, size_t bufferSize) + : MediaSink(env), + fBufferSize(bufferSize), + m_buffer(buffer) +{ + fBuffer = new byte[bufferSize]; + m_pSubmitBuffer = new byte[SUBMIT_BUF_SIZE]; + m_iSubmitBufferPos = 0; + m_bReEntrant = false; +} + +CMemorySink::~CMemorySink() +{ + delete[] fBuffer; + delete[] m_pSubmitBuffer; +} + +CMemorySink* CMemorySink::createNew(UsageEnvironment& env, CMemoryBuffer& buffer, size_t bufferSize) +{ + return new CMemorySink(env, buffer, bufferSize); +} + +Boolean CMemorySink::continuePlaying() +{ + if (fSource == NULL) + return False; + + fSource->getNextFrame(fBuffer, (unsigned) fBufferSize, afterGettingFrame, this, onSourceClosure, this); + return True; +} + +void CMemorySink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned /*numTruncatedBytes*/, struct timeval presentationTime, unsigned /*durationInMicroseconds*/) +{ + CMemorySink* sink = (CMemorySink*)clientData; + sink->afterGettingFrame1(frameSize, presentationTime); + sink->continuePlaying(); +} + +void CMemorySink::addData(unsigned char* data, size_t dataSize, struct timeval UNUSED(presentationTime)) +{ + if (dataSize == 0 || data == NULL) return; + + if (m_bReEntrant) + { + XBMC->Log(LOG_DEBUG, "REENTRANT IN MEMORYSINK.CPP"); + return; + } + + P8PLATFORM::CLockObject BufferLock(m_BufferLock); + + m_bReEntrant = true; + m_buffer.PutBuffer(data, dataSize); + m_bReEntrant = false; +} + + +void CMemorySink::afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) +{ + addData(fBuffer, frameSize, presentationTime); +} +#endif //LIVE555 diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MemorySink.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MemorySink.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MemorySink.h 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MemorySink.h 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,65 @@ +#pragma once +/* + * Copyright (C) 2005-2012 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 of the License, 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 this program. If not, see . + * + ************************************************************************* + * This file is a modified version from Team MediaPortal's + * TsReader DirectShow filter + * MediaPortal is a GPL'ed HTPC-Application + * Copyright (C) 2005-2012 Team MediaPortal + * http://www.team-mediaportal.com + * + * Changes compared to Team MediaPortal's version: + * - Code cleanup for PVR addon usage + * - Code refactoring for cross platform usage + */ + +#if defined LIVE555 + +#ifndef _MEDIA_SINK_HH + #include "MediaSink.hh" //Live555 header +#endif + +#include "MemoryBuffer.h" +#include "p8-platform/threads/mutex.h" + +class CMemorySink: public MediaSink +{ + public: + static CMemorySink* createNew(UsageEnvironment& env, CMemoryBuffer& buffer, size_t bufferSize = 20000); + void addData(unsigned char* data, size_t dataSize, struct timeval presentationTime); + + protected: + CMemorySink(UsageEnvironment& env, CMemoryBuffer& buffer, size_t bufferSize = 20000); + virtual ~CMemorySink(void); + + static void afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds); + virtual void afterGettingFrame1(unsigned frameSize,struct timeval presentationTime); + + unsigned char* fBuffer; + size_t fBufferSize; + CMemoryBuffer& m_buffer; + + private: // redefined virtual functions: + virtual Boolean continuePlaying(); + + P8PLATFORM::CMutex m_BufferLock; + unsigned char* m_pSubmitBuffer; + int m_iSubmitBufferPos; + bool m_bReEntrant; +}; +#endif //LIVE555 diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MepoRTSPClient.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MepoRTSPClient.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MepoRTSPClient.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MepoRTSPClient.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2005-2010 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 of the License, 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 this program. If not, see . + */ + +#if defined LIVE555 + +#include "p8-platform/util/timeutils.h" +#include "MepoRTSPClient.h" +#include "MemorySink.h" +#include "client.h" +#include "utils.h" + +using namespace ADDON; + +CRTSPClient::CRTSPClient() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::CRTSPClient()"); + allowProxyServers = false; + controlConnectionUsesTCP = true; + supportCodecSelection = false; + clientProtocolName = "RTSP"; + tunnelOverHTTPPortNum = 0; + statusCode = 0; + singleMedium = NULL; + desiredPortNum = 0; + createReceivers = true; + simpleRTPoffsetArg = -1; + socketInputBufferSize = 0; + streamUsingTCP = false; + fileSinkBufferSize = 20000; + oneFilePerFrame = false; + m_BufferThreadActive = false; + m_duration = 7200*1000; + m_fStart = 0.0f; + m_session = NULL; + m_ourClient = NULL; + m_bPaused = false; + m_outFileName[0] = '\0'; + m_buffer = NULL; + m_env = NULL; + m_fDuration = 0.0f; + m_url[0] = '\0'; + m_bRunning = false; +} + +CRTSPClient::~CRTSPClient() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::~CRTSPClient()"); + Medium::close(m_ourClient); + m_ourClient = NULL; + + if (m_env) + { + TaskScheduler *scheduler = &m_env->taskScheduler(); + m_env->reclaim(); + m_env = NULL; + delete scheduler; + } +} + + +Medium* CRTSPClient::createClient(UsageEnvironment& env,int verbosityLevel, char const* applicationName) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::createClient()"); + return RTSPClient::createNew(env, verbosityLevel, applicationName,tunnelOverHTTPPortNum); +} + +char* CRTSPClient::getOptionsResponse(Medium* client, char const* url,char* username, char* password) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::getOptionsResponse()"); + RTSPClient* rtspClient = (RTSPClient*)client; + char* optionsResponse = rtspClient->sendOptionsCmd(url, username, password); + + if (optionsResponse == NULL) + { + XBMC->Log(LOG_DEBUG, "CRTSPClient::getOptionsResponse(): \"OPTIONS\" request failed: %s", m_env->getResultMsg()); + } else { + XBMC->Log(LOG_DEBUG, "CRTSPClient::getOptionsResponse(): \"OPTIONS\" request returned: %s", optionsResponse); + } + + return optionsResponse; +} + +char* CRTSPClient::getSDPDescriptionFromURL(Medium* client, char const* url, + char const* username, char const* password, + char const* /*proxyServerName*/, + unsigned short /*proxyServerPortNum*/, + unsigned short /*clientStartPort*/) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::getSDPDescriptionFromURL()"); + RTSPClient* rtspClient = (RTSPClient*)client; + char* result; + if (username != NULL && password != NULL) + { + result = rtspClient->describeWithPassword(url, username, password); + } + else + { + result = rtspClient->describeURL(url); + } + + statusCode = rtspClient->describeStatus(); + return result; +} + +char* CRTSPClient::getSDPDescription() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::getSDPDescription()"); + RTSPClient *client = (RTSPClient*)m_ourClient; + RTSPClient *rtspClient = RTSPClient::createNew(client->envir(), 0, "TSFileSource", tunnelOverHTTPPortNum); + char* result; + result = rtspClient->describeURL(m_url); + + XBMC->Log(LOG_DEBUG, "CRTSPClient::getSDPDescription() statusCode = %d", rtspClient->describeStatus()); + Medium::close(rtspClient); + + return result; +} + +bool CRTSPClient::clientSetupSubsession(Medium* client, MediaSubsession* subsession, bool streamUsingTCP) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::clientSetupSubsession()"); + if (client == NULL || subsession == NULL) + return false; + RTSPClient* rtspClient = (RTSPClient*) client; + return ( rtspClient->setupMediaSubsession(*subsession, False, (streamUsingTCP ? True : False)) ? true : false); +} + +bool CRTSPClient::clientStartPlayingSession(Medium* client, MediaSession* session) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::clientStartPlayingSession()"); + if (client == NULL || session == NULL) + return false; + RTSPClient* rtspClient = (RTSPClient*) client; + + long dur = m_duration/1000; + double fStart = m_fStart; + + if (m_fDuration > 0.0) + { + double fStartToEnd = m_fDuration-m_fStart; + if (fStartToEnd<0) + fStartToEnd = 0; + fStart = dur - fStartToEnd; + if (fStart<0) + fStart = 0; + } + + XBMC->Log(LOG_DEBUG, "CRTSPClient::clientStartPlayingSession() play from %.3f / %.3f", fStart, (float) m_duration/1000); + return (rtspClient->playMediaSession(*session,fStart) ? true : false); +} + +bool CRTSPClient::clientTearDownSession(Medium* client,MediaSession* session) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::clientTearDownSession()"); + if (client == NULL || session == NULL) + return false; + RTSPClient* rtspClient = (RTSPClient*)client; + return (rtspClient->teardownMediaSession(*session) ? true : false); +} + +void my_subsessionAfterPlaying(void* UNUSED(clientData)) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::subsessionAfterPlaying()"); +} + +void my_subsessionByeHandler(void* UNUSED(clientData)) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::subsessionByeHandler()"); +} + +void CRTSPClient::closeMediaSinks() +{ + if (m_session == NULL) + return; + + XBMC->Log(LOG_DEBUG, "CRTSPClient::closeMediaSinks()"); + + MediaSubsessionIterator iter(*m_session); + MediaSubsession* subsession; + + while ((subsession = iter.next()) != NULL) + { + Medium::close(subsession->sink); + subsession->sink = NULL; + } +} + +void CRTSPClient::tearDownStreams() +{ + if (m_session == NULL) + return; + + XBMC->Log(LOG_DEBUG, "CRTSPClient::tearDownStreams()"); + + clientTearDownSession(m_ourClient, m_session); +} +bool CRTSPClient::setupStreams() +{ + // Setup streams + XBMC->Log(LOG_DEBUG, "CRTSPClient::setupStreams()"); + + bool madeProgress = false; + MediaSubsessionIterator iter(*m_session); + MediaSubsession *subsession; + + while ((subsession = iter.next()) != NULL) + { + if (subsession->clientPortNum() == 0) + continue; // port # was not set + + if (!clientSetupSubsession(m_ourClient, subsession, streamUsingTCP)) + { + XBMC->Log(LOG_ERROR, "Failed to setup %s %s %s", subsession->mediumName(), subsession->codecName(), m_env->getResultMsg() ); + } + else + { + XBMC->Log(LOG_DEBUG, "Setup %s %s %d %d", subsession->mediumName(), subsession->codecName(), subsession->clientPortNum(), subsession->clientPortNum() + 1); + madeProgress = true; + } + } + + if (!madeProgress) + { + shutdown(); + return false; + } + return true; +} + +bool CRTSPClient::startPlayingStreams() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::startPlayingStreams()"); + + if (!clientStartPlayingSession(m_ourClient, m_session)) + { + XBMC->Log(LOG_ERROR, "Failed to start playing session :%s", m_env ->getResultMsg() ); + shutdown(); + return false; + } + else + { + XBMC->Log(LOG_DEBUG, "Started playing session"); + } + return true; +} + +void CRTSPClient::shutdown() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::shutdown()"); + + // Close our output files: + closeMediaSinks(); + + // Teardown, then shutdown, any outstanding RTP/RTCP subsessions + tearDownStreams(); + Medium::close(m_session); + + // Finally, shut down our client: + Medium::close(m_ourClient); + m_session = NULL; + m_ourClient = NULL; +} + + +bool CRTSPClient::Initialize(CMemoryBuffer* buffer) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::Initialize()"); + + m_buffer = buffer; + m_duration = 7200*1000; + + TaskScheduler* scheduler = BasicTaskScheduler::createNew(); + m_env = BasicUsageEnvironment::createNew(*scheduler); + + m_ourClient = createClient(*m_env, 0/*verbosityLevel*/, "TSFileSource"); + + if (m_ourClient == NULL) + { + XBMC->Log(LOG_ERROR, "Failed to create %s %s", clientProtocolName, m_env->getResultMsg() ); + shutdown(); + return false; + } + return true; +} + +bool CRTSPClient::OpenStream(const char* url) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::OpenStream()"); + m_session = NULL; + + strncpy(m_url, url, RTSP_URL_BUFFERSIZE - 1); + m_url[RTSP_URL_BUFFERSIZE - 1] = '\0'; + + // Open the URL, to get a SDP description: + char* sdpDescription = getSDPDescriptionFromURL(m_ourClient, url, ""/*username*/, ""/*password*/,""/*proxyServerName*/, 0/*proxyServerPortNum*/,1234/*desiredPortNum*/); + + if (sdpDescription == NULL) + { + XBMC->Log(LOG_ERROR, "Failed to get a SDP description from URL %s %s", url, m_env->getResultMsg() ); + shutdown(); + return false; + } + XBMC->Log(LOG_DEBUG, "Opened URL %s %s", url, sdpDescription); + + char* range = strstr(sdpDescription, "a=range:npt="); + if (range != NULL) + { + char *pStart = range + strlen("a=range:npt="); + char *pEnd = strstr(range, "-"); + if (pEnd != NULL) + { + pEnd++; + double Start = atof(pStart); + double End = atof(pEnd); + + XBMC->Log(LOG_DEBUG, "rangestart:%f rangeend:%f", Start, End); + m_duration = (long) ((End-Start)*1000.0); + } + } + // Create a media session object from this SDP description: + m_session = MediaSession::createNew(*m_env, sdpDescription); + delete[] sdpDescription; + + if (m_session == NULL) + { + XBMC->Log(LOG_ERROR, "Failed to create a MediaSession object from the SDP description:%s ", m_env->getResultMsg()); + shutdown(); + return false; + } + else if (!m_session->hasSubsessions()) + { + XBMC->Log(LOG_DEBUG, "This session has no media subsessions"); + shutdown(); + return false; + } + + // Then, setup the "RTPSource"s for the session: + MediaSubsessionIterator iter(*m_session); + MediaSubsession *subsession; + bool madeProgress = false; + char const* singleMediumToTest = singleMedium; + + while ((subsession = iter.next()) != NULL) + { + // If we've asked to receive only a single medium, then check this now: + if (singleMediumToTest != NULL) + { + if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) + { + XBMC->Log(LOG_DEBUG, "Ignoring %s %s %s", subsession->mediumName(), subsession->codecName(), singleMedium); + continue; + } + else + { + // Receive this subsession only + singleMediumToTest = "xxxxx"; + // this hack ensures that we get only 1 subsession of this type + } + } + + if (desiredPortNum != 0) + { + subsession->setClientPortNum(desiredPortNum); + desiredPortNum += 2; + } + + if (createReceivers) + { + if (!subsession->initiate(simpleRTPoffsetArg)) + { + XBMC->Log(LOG_ERROR, "Unable to create receiver for %s %s %s", subsession->mediumName(), subsession->codecName(), m_env->getResultMsg()); + } + else + { + XBMC->Log(LOG_DEBUG, "Created receiver for type=%s codec=%s ports: %d %d ", subsession->mediumName(), subsession->codecName(), subsession->clientPortNum(), subsession->clientPortNum() + 1 ); + madeProgress = true; + + if (subsession->rtpSource() != NULL) + { + // Because we're saving the incoming data, rather than playing + // it in real time, allow an especially large time threshold + // (1 second) for reordering misordered incoming packets: + int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); + + XBMC->Log(LOG_DEBUG, "rtsp:increaseReceiveBufferTo to 2000000 for s:%d", socketNum); + increaseReceiveBufferTo( *m_env, socketNum, 2000000 ); + + unsigned const thresh = 1000000; // 1 second + subsession->rtpSource()->setPacketReorderingThresholdTime(thresh); + + if (socketInputBufferSize > 0) + { + // Set the RTP source's input buffer size as specified: + unsigned int curBufferSize = getReceiveBufferSize(*m_env, socketNum); + unsigned int newBufferSize = setReceiveBufferTo(*m_env, socketNum, socketInputBufferSize); + + XBMC->Log(LOG_DEBUG, "Changed socket receive buffer size for the %s %s %d %d", subsession->mediumName(), subsession->codecName(), curBufferSize, newBufferSize); + } + } + } + } + else + { + if (subsession->clientPortNum() == 0) + { + XBMC->Log(LOG_DEBUG, "No client port was specified for the %s %s", subsession->mediumName(), subsession->codecName()); + } + else + { + madeProgress = true; + } + } + } + + if (!madeProgress) + { + shutdown(); + return false; + } + + // Perform additional 'setup' on each subsession, before playing them: + if (!setupStreams()) + { + return false; + } + + // Create output files: + // Create and start "FileSink"s for each subsession: + madeProgress = false; + iter.reset(); + + while ((subsession = iter.next()) != NULL) + { + if (subsession->readSource() == NULL) continue; // was not initiated + + // Mediaportal: + CMemorySink* fileSink = CMemorySink::createNew(*m_env, *m_buffer, fileSinkBufferSize); + // XBMC test via file: + //FileSink* fileSink = FileSink::createNew(*m_env, m_outFileName, fileSinkBufferSize, false); //oneFilePerFrame + + subsession->sink = fileSink; + if (subsession->sink == NULL) + { + XBMC->Log(LOG_DEBUG, "Failed to create FileSink %s", m_env->getResultMsg()); + shutdown(); + return false; + } + XBMC->Log(LOG_DEBUG, "Created output sink: %s", m_outFileName); + subsession->sink->startPlaying(*(subsession->readSource()), my_subsessionAfterPlaying, subsession); + + // Also set a handler to be called if a RTCP "BYE" arrives + // for this subsession: + if (subsession->rtcpInstance() != NULL) + { + subsession->rtcpInstance()->setByeHandler(my_subsessionByeHandler, subsession); + } + madeProgress = true; + } + + return true; +} + + +void CRTSPClient::Stop() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient:Stop"); + + if (m_BufferThreadActive) + { + StopBufferThread(); + } + + shutdown(); + m_buffer->Clear(); + XBMC->Log(LOG_DEBUG, "CRTSPClient:Stop done"); +} + +void CRTSPClient::StartBufferThread() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::StartBufferThread"); + + if (!m_BufferThreadActive) + { + CreateThread(); + m_BufferThreadActive = true; + } + XBMC->Log(LOG_DEBUG, "CRTSPClient::StartBufferThread done"); +} + +void CRTSPClient::StopBufferThread() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::StopBufferThread"); + m_bRunning = false; + if (!m_BufferThreadActive) + return; + + StopThread(); + + m_BufferThreadActive = false; + XBMC->Log(LOG_DEBUG, "CRTSPClient::StopBufferThread done"); +} + +bool CRTSPClient::IsRunning() +{ + return m_BufferThreadActive; +} + +long CRTSPClient::Duration() +{ + return m_duration; +} + +void CRTSPClient::FillBuffer(unsigned long byteCount) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::Fillbuffer...%d\n", byteCount); + unsigned long tickCount = GetTickCount(); + + while ( IsRunning() && m_buffer->Size() < byteCount) + { + usleep(5000); + if (GetTickCount() - tickCount > 3000) + break; + } + XBMC->Log(LOG_DEBUG, "CRTSPClient::Fillbuffer...%d/%d\n", byteCount, m_buffer->Size() ); +} + +void *CRTSPClient::Process() +{ + m_BufferThreadActive = true; + m_bRunning = true; + + XBMC->Log(LOG_DEBUG, "CRTSPClient:: thread started"); + + while (m_env != NULL && !IsStopped()) + { + m_env->taskScheduler().doEventLoop(); + if (m_bRunning == false) + break; + } + + XBMC->Log(LOG_DEBUG, "CRTSPClient:: thread stopped"); + + m_BufferThreadActive = false; + + return NULL; +} + +void CRTSPClient::Continue() +{ + if (m_ourClient != NULL && m_session != NULL) + { + RTSPClient* rtspClient = (RTSPClient*) m_ourClient; + rtspClient->playMediaSession(*m_session, -1.0); + StartBufferThread(); + m_bPaused = false; + } +} + +bool CRTSPClient::IsPaused() +{ + return m_bPaused; +} + +bool CRTSPClient::Pause() +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::Pause()"); + if (m_ourClient != NULL && m_session != NULL) + { + XBMC->Log(LOG_DEBUG, "CRTSPClient::Pause() stopthread"); + StopThread(10000); // Ambass : sometimes 100mS ( prev value ) is not enough and thread is not stopped. + // now stopping takes around 5 secs ?!?! why ???? + XBMC->Log(LOG_DEBUG, "CRTSPClient::Pause() thread stopped"); + RTSPClient* rtspClient=(RTSPClient*)m_ourClient; + rtspClient->pauseMediaSession(*m_session); + m_bPaused = true; + } + XBMC->Log(LOG_DEBUG, "CRTSPClient::Pause() done"); + + return true; +} +bool CRTSPClient::Play(double fStart,double fDuration) +{ + XBMC->Log(LOG_DEBUG, "CRTSPClient::Play from %f / %f", (float)fStart, (float)fDuration); + + m_bPaused = false; + m_fStart = fStart; + m_fDuration = fDuration; + + if (m_BufferThreadActive) + { + Stop(); + m_buffer->Clear(); + if (Initialize(m_buffer) == false) + { + shutdown(); + return false; + } + if (OpenStream(m_url) == false) + { + shutdown(); + return false; + } + } + + if (m_ourClient == NULL || m_session == NULL) + { + m_buffer->Clear(); + if (Initialize(m_buffer) == false) + { + shutdown(); + return false; + } + if (OpenStream(m_url) == false) + { + shutdown(); + return false; + } + } + + if (!startPlayingStreams()) + { + shutdown(); + return false; + } + StartBufferThread(); + + return true; +} + +bool CRTSPClient::UpdateDuration() +{ + char* sdpDescription = getSDPDescription(); + if (sdpDescription == NULL) + { + XBMC->Log(LOG_ERROR, "UpdateStreamDuration: Failed to get a SDP description from URL %s %s", m_url, m_env->getResultMsg() ); + return false; + } + //XBMC->Log(LOG_DEBUG, "Opened URL %s %s",url,sdpDescription); + + char* range = strstr(sdpDescription, "a=range:npt="); + if (range != NULL) + { + char *pStart = range + strlen("a=range:npt="); + char *pEnd = strstr(range,"-"); + + if (pEnd != NULL) + { + pEnd++; + double Start = atof(pStart); + double End = atof(pEnd); + + //XBMC->Log(LOG_DEBUG, "rangestart:%f rangeend:%f", Start,End); + m_duration = (long) ((End-Start)*1000.0); + } + } + + return true; +} +#endif //LIVE555 diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MepoRTSPClient.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MepoRTSPClient.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MepoRTSPClient.h 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MepoRTSPClient.h 2017-01-11 10:28:16.000000000 +0000 @@ -0,0 +1,117 @@ +#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 of the License, 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 this program. If not, see . + * + ************************************************************************* + * This file is a modified version from Team MediaPortal's + * TsReader DirectShow filter + * MediaPortal is a GPL'ed HTPC-Application + * Copyright (C) 2005-2012 Team MediaPortal + * http://www.team-mediaportal.com + * + * Changes compared to Team MediaPortal's version: + * - Code cleanup for PVR addon usage + * - Code refactoring for cross platform usage + ************************************************************************* + */ + +#ifdef LIVE555 + +#include "p8-platform/threads/threads.h" +#include "lib/tsreader/MemoryBuffer.h" + +#include "liveMedia.hh" +#include "BasicUsageEnvironment.hh" +#include "GroupsockHelper.hh" + + +#define RTSP_URL_BUFFERSIZE 2048 + +class CRTSPClient: public P8PLATFORM::CThread +{ +public: + CRTSPClient(); + virtual ~CRTSPClient(void); + bool Initialize(CMemoryBuffer* buffer); + bool OpenStream(const char* url); + bool Play(double fStart,double fDuration); + void Stop(); + bool IsRunning(); + long Duration(); + bool Pause(); + bool IsPaused(); + void Continue(); + void FillBuffer(unsigned long byteCount); + + char* getSDPDescription(); + bool UpdateDuration(); + +protected: + CMemoryBuffer* m_buffer; + Medium* createClient(UsageEnvironment& env,int verbosityLevel, char const* applicationName); + char* getSDPDescriptionFromURL(Medium* client, char const* url, + char const* username, char const* password, + char const* /*proxyServerName*/, + unsigned short /*proxyServerPortNum*/, + unsigned short /*clientStartPort*/); + bool clientSetupSubsession(Medium* client, MediaSubsession* subsession, bool streamUsingTCP); + bool clientStartPlayingSession(Medium* client, MediaSession* session); + bool clientTearDownSession(Medium* client, MediaSession* session); + void closeMediaSinks(); + void tearDownStreams(); + bool setupStreams(); + //void checkForPacketArrival(void* /*clientData*/); + MediaSession* m_session; + + bool allowProxyServers; + bool controlConnectionUsesTCP; + bool supportCodecSelection; + char const* clientProtocolName; + portNumBits tunnelOverHTTPPortNum; + unsigned statusCode; + char const* singleMedium; + unsigned short desiredPortNum; + bool createReceivers; + int simpleRTPoffsetArg; + unsigned socketInputBufferSize; + bool streamUsingTCP; + size_t fileSinkBufferSize; + bool oneFilePerFrame; + +public: + UsageEnvironment* m_env; + Medium* m_ourClient; + char* getOptionsResponse(Medium* client, char const* url,char* username, char* password); + void shutdown(); + bool startPlayingStreams(); + + // Thread +private: + virtual void *Process(void); + void StartBufferThread(); + void StopBufferThread(); + bool m_BufferThreadActive; + + long m_duration; + double m_fStart; + double m_fDuration; + char m_url[RTSP_URL_BUFFERSIZE]; + bool m_bRunning; + bool m_bPaused; + char m_outFileName[1000]; +}; +#endif //LIVE555 diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MultiFileReader.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MultiFileReader.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/MultiFileReader.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/MultiFileReader.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -38,13 +38,13 @@ #include #include "utils.h" #include -#include "platform/util/timeutils.h" -#include "platform/util/StdString.h" -#include "platform/threads/threads.h" +#include "p8-platform/util/timeutils.h" +#include "p8-platform/util/StdString.h" +#include "p8-platform/threads/threads.h" #include using namespace ADDON; -using namespace PLATFORM; +using namespace P8PLATFORM; //Maximum time in msec to wait for the buffer file to become available - Needed for DVB radio (this sometimes takes some time) #define MAX_BUFFER_TIMEOUT 1500 @@ -112,7 +112,7 @@ if (RefreshTSBufferFile() == S_FALSE) { // For radio the buffer sometimes needs some time to become available, so wait and try it more than once - PLATFORM::CTimeout timeout(MAX_BUFFER_TIMEOUT); + P8PLATFORM::CTimeout timeout(MAX_BUFFER_TIMEOUT); do { diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/PatParser.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/PatParser.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/PatParser.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/PatParser.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -53,7 +53,7 @@ void CPatParser::CleanUp() { - for (int i = 0; i < (int)m_pmtParsers.size(); ++i) + for (size_t i = 0; i < m_pmtParsers.size(); ++i) { CPmtParser* parser = m_pmtParsers[i]; delete parser; @@ -74,14 +74,14 @@ } - int CPatParser::Count() + size_t CPatParser::Count() { - int count = m_pmtParsers.size(); + size_t count = m_pmtParsers.size(); if (count == 0) return 0; - for (int i = 0; i < (int)m_pmtParsers.size(); ++i) + for (size_t i = 0; i < m_pmtParsers.size(); ++i) { CPmtParser* parser = m_pmtParsers[i]; if (true == parser->IsReady()) @@ -92,11 +92,11 @@ return 0; } - bool CPatParser::GetChannel(int index, CChannelInfo& info) + bool CPatParser::GetChannel(size_t index, CChannelInfo& info) { static CChannelInfo unknownChannel; - if (index < 0 || index > Count()) + if (index > Count()) { return false; } @@ -125,7 +125,7 @@ if (m_packetsReceived > m_packetsToSkip) { - for (int i = 0; i < (int)m_pmtParsers.size(); ++i) + for (size_t i = 0; i < m_pmtParsers.size(); ++i) { CPmtParser* parser = m_pmtParsers[i]; parser->OnTsPacket(tsPacket); @@ -135,7 +135,7 @@ if (m_iState == Parsing && m_pCallback != NULL) { - for (int i = 0; i < (int)m_pmtParsers.size(); ++i) + for (size_t i = 0; i < m_pmtParsers.size(); ++i) { CPmtParser* parser = m_pmtParsers[i]; if (true == parser->IsReady()) @@ -187,7 +187,7 @@ } bool found = false; - for (int idx = 0; idx < (int)m_pmtParsers.size(); idx++) + for (size_t idx = 0; idx < m_pmtParsers.size(); idx++) { CPmtParser* pmtParser = m_pmtParsers[idx]; if (pmtParser->GetPid() == pmtPid) @@ -202,7 +202,7 @@ pmtParser->SetPid(pmtPid); //pmtParser->SetPmtCallBack(this); m_pmtParsers.push_back(pmtParser); - XBMC->Log(LOG_DEBUG, "PatParser: add pmt# %d pid: %x", m_pmtParsers.size(), pmtPid); + XBMC->Log(LOG_DEBUG, "PatParser: add pmt# %u pid: %x", (unsigned int) m_pmtParsers.size(), pmtPid); } } } @@ -214,18 +214,18 @@ void CPatParser::Dump() { - for (int i = 0; i < Count(); ++i) + for (size_t i = 0; i < Count(); ++i) { CChannelInfo info; if (GetChannel(i, info)) { - XBMC->Log(LOG_DEBUG, "%d) onid:%x tsid:%x sid:%x major:%d minor:%x freq:%x type:%d provider:%s service:%s", i, + XBMC->Log(LOG_DEBUG, "%u) onid:%x tsid:%x sid:%x major:%d minor:%x freq:%x type:%d provider:%s service:%s", (unsigned int) i, info.NetworkId, info.TransportId, info.ServiceId, info.MajorChannel, info.MinorChannel, info.Frequency, info.ServiceType, info.ProviderName, info.ServiceName); info.PidTable.LogPIDs(); } else { - XBMC->Log(LOG_DEBUG, "%d) not found", i); + XBMC->Log(LOG_DEBUG, "%u) not found", (unsigned int) i); } } } diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/PatParser.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/PatParser.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/PatParser.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/PatParser.h 2017-01-11 10:28:16.000000000 +0000 @@ -48,17 +48,17 @@ void OnTsPacket(byte* tsPacket); void Reset(); void OnNewSection(CSection& section); - int Count(); - bool GetChannel(int index, CChannelInfo& info); + size_t Count(); void Dump(); void SetCallBack(IPatParserCallback* callback); private: + bool GetChannel(size_t index, CChannelInfo& info); void CleanUp(); IPatParserCallback* m_pCallback; std::vector m_pmtParsers; int64_t m_packetsReceived; int64_t m_packetsToSkip; - int m_iPatTableVersion; + int m_iPatTableVersion; PatState m_iState; }; } diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/PmtParser.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/PmtParser.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/PmtParser.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/PmtParser.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -149,7 +149,7 @@ pid.Pid = elementary_PID; pid.AudioServiceType = (indicator == DESCRIPTOR_DVB_AC3) ? SERVICE_TYPE_AUDIO_AC3 : SERVICE_TYPE_AUDIO_DD_PLUS; - for (unsigned int i(0); i < tempPids.size(); i++) + for (size_t i(0); i < tempPids.size(); i++) { if (tempPids[i].Pid == elementary_PID) { diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/TSReader.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/TSReader.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/TSReader.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/TSReader.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -32,7 +32,7 @@ #include "MultiFileReader.h" #include "utils.h" #include "TSDebug.h" -#include "platform/util/timeutils.h" +#include "p8-platform/util/timeutils.h" #ifdef LIVE555 #include "MemoryReader.h" #include "MepoRTSPClient.h" @@ -45,7 +45,8 @@ namespace MPTV { - CTsReader::CTsReader() : m_demultiplexer(*this) + CTsReader::CTsReader() : m_demultiplexer(*this), + m_fileName("") { m_fileReader = NULL; m_fileDuration = NULL; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/TSReader.h kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/TSReader.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/lib/tsreader/TSReader.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/lib/tsreader/TSReader.h 2017-01-11 10:28:16.000000000 +0000 @@ -30,16 +30,16 @@ #include "client.h" #include "FileReader.h" -#include "platform/util/StdString.h" +#include "p8-platform/util/StdString.h" #include "DeMultiplexer.h" #include "Cards.h" +#ifdef LIVE555 +#include "MepoRTSPClient.h" +#include "MemoryBuffer.h" +#endif namespace MPTV { -#ifdef LIVE555 - class CRTSPClient; - class CMemoryBuffer; -#endif typedef enum _TsReaderState { State_Stopped = 0, diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/os-dependent.h kodi-pvr-mediaportal-tvserver-2.4.16/src/os-dependent.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/os-dependent.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/os-dependent.h 2017-01-11 10:28:16.000000000 +0000 @@ -18,7 +18,7 @@ * */ -#include "platform/os.h" +#include "p8-platform/os.h" // Additional typedefs typedef uint8_t byte; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/pvrclient-mediaportal.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/pvrclient-mediaportal.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/pvrclient-mediaportal.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/pvrclient-mediaportal.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -21,7 +21,7 @@ #include #include -#include "platform/util/timeutils.h" +#include "p8-platform/util/timeutils.h" #include "client.h" #include "timers.h" @@ -49,19 +49,19 @@ /* TVServerXBMC plugin supported versions */ #define TVSERVERXBMC_MIN_VERSION_STRING "1.1.7.107" #define TVSERVERXBMC_MIN_VERSION_BUILD 107 -#define TVSERVERXBMC_RECOMMENDED_VERSION_STRING "1.2.3.122 till 1.12.0.130" -#define TVSERVERXBMC_RECOMMENDED_VERSION_BUILD 127 +#define TVSERVERXBMC_RECOMMENDED_VERSION_STRING "1.2.3.122 till 1.15.0.137" +#define TVSERVERXBMC_RECOMMENDED_VERSION_BUILD 137 /************************************************************/ /** Class interface */ -cPVRClientMediaPortal::cPVRClientMediaPortal() +cPVRClientMediaPortal::cPVRClientMediaPortal() : + m_state(PVR_CONNECTION_STATE_UNKNOWN) { m_iCurrentChannel = -1; m_bCurrentChannelIsRadio = false; m_iCurrentCard = -1; - m_tcpclient = new MPTV::Socket(MPTV::af_inet, MPTV::pf_inet, MPTV::sock_stream, MPTV::tcp); - m_bConnected = false; + m_tcpclient = new MPTV::Socket(MPTV::af_unspec, MPTV::pf_inet, MPTV::sock_stream, MPTV::tcp); m_bStop = true; m_bTimeShiftStarted = false; m_BackendUTCoffset = 0; @@ -72,13 +72,15 @@ m_signalStateCounter = 0; m_iSignal = 0; m_iSNR = 0; + + /* Generate the recording life time strings */ + Timer::lifetimeValues = new cLifeTimeValues(); } cPVRClientMediaPortal::~cPVRClientMediaPortal() { XBMC->Log(LOG_DEBUG, "->~cPVRClientMediaPortal()"); - if (m_bConnected) - Disconnect(); + Disconnect(); SAFE_DELETE(Timer::lifetimeValues); SAFE_DELETE(m_tcpclient); SAFE_DELETE(m_genretable); @@ -86,14 +88,16 @@ string cPVRClientMediaPortal::SendCommand(string command) { - PLATFORM::CLockObject critsec(m_mutex); + P8PLATFORM::CLockObject critsec(m_mutex); if ( !m_tcpclient->send(command) ) { if ( !m_tcpclient->is_valid() ) { + SetConnectionState(PVR_CONNECTION_STATE_DISCONNECTED); + // Connection lost, try to reconnect - if ( Connect() == ADDON_STATUS_OK ) + if (TryConnect() == ADDON_STATUS_OK) { // Resend the command if (!m_tcpclient->send(command)) @@ -121,7 +125,7 @@ bool cPVRClientMediaPortal::SendCommand2(string command, vector& lines) { - PLATFORM::CLockObject critsec(m_mutex); + P8PLATFORM::CLockObject critsec(m_mutex); if ( !m_tcpclient->send(command) ) { @@ -165,23 +169,53 @@ return true; } -ADDON_STATUS cPVRClientMediaPortal::Connect() +ADDON_STATUS cPVRClientMediaPortal::TryConnect() { - string result; - /* Open Connection to MediaPortal Backend TV Server via the XBMC TV Server plugin */ XBMC->Log(LOG_INFO, "Mediaportal pvr addon " PVRCLIENT_MEDIAPORTAL_VERSION_STRING " connecting to %s:%i", g_szHostname.c_str(), g_iPort); + PVR_CONNECTION_STATE result = Connect(); + + switch (result) + { + case PVR_CONNECTION_STATE_ACCESS_DENIED: + case PVR_CONNECTION_STATE_UNKNOWN: + case PVR_CONNECTION_STATE_SERVER_MISMATCH: + case PVR_CONNECTION_STATE_VERSION_MISMATCH: + return ADDON_STATUS_PERMANENT_FAILURE; + case PVR_CONNECTION_STATE_DISCONNECTED: + case PVR_CONNECTION_STATE_SERVER_UNREACHABLE: + XBMC->Log(LOG_ERROR, "Could not connect to MediaPortal TV Server backend."); + // Start background thread for connecting to the backend + if (!IsRunning()) + { + XBMC->Log(LOG_INFO, "Waiting for a connection in the background."); + CreateThread(); + } + return ADDON_STATUS_LOST_CONNECTION; + } + + return ADDON_STATUS_OK; +} + +PVR_CONNECTION_STATE cPVRClientMediaPortal::Connect() +{ + P8PLATFORM::CLockObject critsec(m_connectionMutex); + + string result; + if (!m_tcpclient->create()) { XBMC->Log(LOG_ERROR, "Could not connect create socket"); - return ADDON_STATUS_PERMANENT_FAILURE; + SetConnectionState(PVR_CONNECTION_STATE_UNKNOWN); + return PVR_CONNECTION_STATE_UNKNOWN; } + SetConnectionState(PVR_CONNECTION_STATE_CONNECTING); if (!m_tcpclient->connect(g_szHostname, (unsigned short) g_iPort)) { - XBMC->Log(LOG_ERROR, "Could not connect to MediaPortal TV Server backend"); - return ADDON_STATUS_LOST_CONNECTION; + SetConnectionState(PVR_CONNECTION_STATE_SERVER_UNREACHABLE); + return PVR_CONNECTION_STATE_SERVER_UNREACHABLE; } m_tcpclient->set_non_blocking(1); @@ -190,12 +224,16 @@ result = SendCommand("PVRclientXBMC:0-1\n"); if (result.length() == 0) - return ADDON_STATUS_UNKNOWN; + { + SetConnectionState(PVR_CONNECTION_STATE_UNKNOWN); + return PVR_CONNECTION_STATE_UNKNOWN; + } if(result.find("Unexpected protocol") != std::string::npos) { XBMC->Log(LOG_ERROR, "TVServer does not accept protocol: PVRclientXBMC:0-1"); - return ADDON_STATUS_UNKNOWN; + SetConnectionState(PVR_CONNECTION_STATE_SERVER_MISMATCH); + return PVR_CONNECTION_STATE_SERVER_MISMATCH; } vector fields; @@ -207,7 +245,8 @@ { XBMC->Log(LOG_ERROR, "Your TVServerXBMC version is too old. Please upgrade to '%s' or higher!", TVSERVERXBMC_MIN_VERSION_STRING); XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30051), TVSERVERXBMC_MIN_VERSION_STRING); - return ADDON_STATUS_PERMANENT_FAILURE; + SetConnectionState(PVR_CONNECTION_STATE_VERSION_MISMATCH); + return PVR_CONNECTION_STATE_VERSION_MISMATCH; } // Ok, this TVServerXBMC version answers with a version string @@ -215,7 +254,8 @@ if( count < 4 ) { XBMC->Log(LOG_ERROR, "Could not parse the TVServerXBMC version string '%s'", fields[1].c_str()); - return ADDON_STATUS_UNKNOWN; + SetConnectionState(PVR_CONNECTION_STATE_VERSION_MISMATCH); + return PVR_CONNECTION_STATE_VERSION_MISMATCH; } // Check for the minimal requirement: 1.1.0.70 @@ -223,7 +263,8 @@ { XBMC->Log(LOG_ERROR, "Your TVServerXBMC version '%s' is too old. Please upgrade to '%s' or higher!", fields[1].c_str(), TVSERVERXBMC_MIN_VERSION_STRING); XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30050), fields[1].c_str(), TVSERVERXBMC_MIN_VERSION_STRING); - return ADDON_STATUS_PERMANENT_FAILURE; + SetConnectionState(PVR_CONNECTION_STATE_VERSION_MISMATCH); + return PVR_CONNECTION_STATE_VERSION_MISMATCH; } else { @@ -241,19 +282,16 @@ snprintf(buffer, 512, "%s:%i", g_szHostname.c_str(), g_iPort); m_ConnectionString = buffer; - m_bConnected = true; + SetConnectionState(PVR_CONNECTION_STATE_CONNECTED); /* Load additional settings */ LoadGenreTable(); LoadCardSettings(); - /* Generate the recording life time strings */ - Timer::lifetimeValues = new cLifeTimeValues(); - /* The pvr addon cannot access XBMC's current locale settings, so just use the system default */ setlocale(LC_ALL, ""); - return ADDON_STATUS_OK; + return PVR_CONNECTION_STATE_CONNECTED; } void cPVRClientMediaPortal::Disconnect() @@ -262,6 +300,11 @@ XBMC->Log(LOG_INFO, "Disconnect"); + if (IsRunning()) + { + StopThread(1000); + } + if (m_tcpclient->is_valid() && m_bTimeShiftStarted) { result = SendCommand("IsTimeshifting:\n"); @@ -281,7 +324,7 @@ m_tcpclient->close(); - m_bConnected = false; + SetConnectionState(PVR_CONNECTION_STATE_DISCONNECTED); } /* IsUp() @@ -291,20 +334,48 @@ */ bool cPVRClientMediaPortal::IsUp() { - if(!m_tcpclient->is_valid()) + if (m_state == PVR_CONNECTION_STATE_CONNECTED) { - if( Connect() != ADDON_STATUS_OK ) - { - XBMC->Log(LOG_DEBUG, "Backend not connected!"); - return false; - } + return true; + } + else + { + return false; } - return true; } -void* cPVRClientMediaPortal::Process(void*) +void* cPVRClientMediaPortal::Process(void) { - XBMC->Log(LOG_DEBUG, "->Process() Not yet implemented"); + XBMC->Log(LOG_DEBUG, "Background thread started."); + + bool keepWaiting = true; + + while (!IsStopped() && keepWaiting) + { + PVR_CONNECTION_STATE result = Connect(); + + switch (result) + { + case PVR_CONNECTION_STATE_ACCESS_DENIED: + case PVR_CONNECTION_STATE_UNKNOWN: + case PVR_CONNECTION_STATE_SERVER_MISMATCH: + case PVR_CONNECTION_STATE_VERSION_MISMATCH: + keepWaiting = false; + case PVR_CONNECTION_STATE_CONNECTED: + keepWaiting = false; + default: + break; + } + + if (keepWaiting) + { + // Wait for 1 minute before re-trying + usleep(60000000); + } + } + + XBMC->Log(LOG_DEBUG, "Background thread finished."); + return NULL; } @@ -970,9 +1041,10 @@ PVR_STRCPY(tag.strRecordingId, strRecordingId.c_str()); PVR_STRCPY(tag.strTitle, recording.Title()); - PVR_STRCPY(tag.strPlotOutline, recording.EpisodeName()); + PVR_STRCPY(tag.strEpisodeName, recording.EpisodeName()); PVR_STRCPY(tag.strPlot, recording.Description()); PVR_STRCPY(tag.strChannelName, recording.ChannelName()); + tag.iChannelUid = recording.ChannelID(); tag.recordingTime = recording.StartTime(); tag.iDuration = (int) recording.Duration(); tag.iPriority = 0; // only available for schedules, not for recordings @@ -983,6 +1055,8 @@ tag.iLastPlayedPosition = recording.LastPlayedPosition(); tag.iEpisodeNumber = recording.GetEpisodeNumber(); tag.iSeriesNumber = recording.GetSeriesNumber(); + tag.iEpgEventId = EPG_TAG_INVALID_UID; + tag.channelType = recording.GetChannelType(); strDirectory = recording.Directory(); if (strDirectory.length() > 0) @@ -1039,11 +1113,12 @@ // Use rtsp url and XBMC's internal FFMPeg playback PVR_STRCPY(tag.strStreamURL, recording.Stream()); } + PVR->TransferRecordingEntry(handle, &tag); } } - m_iLastRecordingUpdate = PLATFORM::GetTimeMs(); + m_iLastRecordingUpdate = P8PLATFORM::GetTimeMs(); return PVR_ERROR_NO_ERROR; } @@ -1235,7 +1310,7 @@ } } - if ( PLATFORM::GetTimeMs() > m_iLastRecordingUpdate + 15000) + if ( P8PLATFORM::GetTimeMs() > m_iLastRecordingUpdate + 15000) { PVR->TriggerRecordingUpdate(); } @@ -1253,7 +1328,7 @@ if (!IsUp()) return PVR_ERROR_SERVER_ERROR; - snprintf(command, 256, "GetScheduleInfo:%u\n", timernumber); + snprintf(command, 256, "GetScheduleInfo:%u:True\n", timernumber); result = SendCommand(command); @@ -1261,6 +1336,9 @@ return PVR_ERROR_SERVER_ERROR; cTimer timer; + + uri::decode(result); + if( timer.ParseLine(result.c_str()) == false ) { XBMC->Log(LOG_DEBUG, "GetTimerInfo(%i) parsing server response failed. Response: %s", timernumber, result.c_str()); @@ -1277,6 +1355,9 @@ int& count = *size; // the amount of filled items in the types[] array count = 0; + if (Timer::lifetimeValues == NULL) + return PVR_ERROR_FAILED; + if (count > maxsize) return PVR_ERROR_NO_ERROR; @@ -1540,6 +1621,7 @@ if (!IsUp()) { m_iCurrentChannel = -1; + m_bTimeShiftStarted = false; m_signalStateCounter = 0; XBMC->Log(LOG_ERROR, "Open Live stream failed. No connection to backend."); return false; @@ -1553,6 +1635,7 @@ m_iCurrentChannel = -1; // make sure that it is not a valid channel nr in case it will fail lateron m_signalStateCounter = 0; + m_bTimeShiftStarted = false; // Start the timeshift // Use the optimized TimeshiftChannel call (don't stop a running timeshift) @@ -1811,7 +1894,7 @@ string result; if (!IsUp()) - return; + return; if (m_bTimeShiftStarted) { @@ -1867,6 +1950,11 @@ return m_tsreader->GetFilePointer(); } +bool cPVRClientMediaPortal::IsRealTimeStream(void) +{ + return m_bTimeShiftStarted; +} + bool cPVRClientMediaPortal::SwitchChannel(const PVR_CHANNEL &channel) { if (((int)channel.iUniqueId) == m_iCurrentChannel) @@ -1894,12 +1982,6 @@ } -int cPVRClientMediaPortal::GetCurrentClientChannel() -{ - XBMC->Log(LOG_DEBUG, "GetCurrentClientChannel: uid=%i", m_iCurrentChannel); - return m_iCurrentChannel; -} - PVR_ERROR cPVRClientMediaPortal::SignalStatus(PVR_SIGNAL_STATUS &signalStatus) { if (g_iTVServerXBMCBuild < 108 || (m_iCurrentChannel == -1)) @@ -1964,13 +2046,16 @@ // These URLs are stored in the field PVR_RECORDINGINFO_OLD.stream_url bool cPVRClientMediaPortal::OpenRecordedStream(const PVR_RECORDING &recording) { - XBMC->Log(LOG_NOTICE, "OpenRecordedStream (id=%s)", recording.strRecordingId); + XBMC->Log(LOG_NOTICE, "OpenRecordedStream (id=%s, RTSP=%d)", recording.strRecordingId, (g_bUseRTSP ? "true" : "false")); + + m_bTimeShiftStarted = false; + if (!IsUp()) return false; if (g_eStreamingMethod == ffmpeg) { - XBMC->Log(LOG_ERROR, "Addon is in 'ffmpeg' mode. XBMC should play the RTSP url directly. Please reset your XBMC PVR database!"); + XBMC->Log(LOG_ERROR, "Addon is in 'ffmpeg' mode. Kodi should play the RTSP url directly. Please reset your Kodi PVR database!"); return false; } @@ -1982,51 +2067,71 @@ //if(g_bUseRecordingsDir) if(!g_bUseRTSP) - snprintf(command, 256, "GetRecordingInfo:%s|False\n", recording.strRecordingId); + snprintf(command, 256, "GetRecordingInfo:%s|False|True\n", recording.strRecordingId); else - snprintf(command, 256, "GetRecordingInfo:%s|True\n", recording.strRecordingId); + snprintf(command, 256, "GetRecordingInfo:%s|True|True\n", recording.strRecordingId); result = SendCommand(command); - if(result.length() > 0) + if (result.empty()) { - cRecording myrecording; - if (myrecording.ParseLine(result)) - { - XBMC->Log(LOG_NOTICE, "RECORDING: %s", result.c_str() ); + XBMC->Log(LOG_ERROR, "Backend command '%s' returned a zero-length answer.", command); + return false; + } - if (!g_bUseRTSP) - { - recfile = myrecording.FilePath(); - } - else + cRecording myrecording; + + uri::decode(result); + + if (!myrecording.ParseLine(result)) + { + XBMC->Log(LOG_ERROR, "Parsing result from '%s' command failed. Result='%s'.", command, result.c_str()); + return false; + } + + XBMC->Log(LOG_NOTICE, "RECORDING: %s", result.c_str() ); + if (!g_bUseRTSP) + { + recfile = myrecording.FilePath(); + if (recfile.length() == 0) + { + XBMC->Log(LOG_ERROR, "Backend returned an empty recording filename for recording id %s.", recording.strRecordingId); + recfile = myrecording.Stream(); + if (recfile.length() > 0) { - recfile = myrecording.Stream(); + XBMC->Log(LOG_NOTICE, "Trying to use the recording RTSP stream URL name instead."); } } } else { - XBMC->Log(LOG_ERROR, "Backend command '%s' returned a zero-length answer", command); + recfile = myrecording.Stream(); + if (recfile.length() == 0) + { + XBMC->Log(LOG_ERROR, "Backend returned an empty RTSP stream URL for recording id %s.", recording.strRecordingId); + recfile = myrecording.FilePath(); + if (recfile.length() > 0) + { + XBMC->Log(LOG_NOTICE, "Trying to use the filename instead."); + } + } } - if (recfile.length() > 0) - { - m_tsreader = new CTsReader(); - m_tsreader->SetCardSettings(&m_cCards); - if ( m_tsreader->Open(recfile.c_str()) != S_OK ) - return false; - else - return true; - } - else + if (recfile.empty()) { - XBMC->Log(LOG_ERROR, "Recording playback not possible. Backend returned empty filename or stream URL for recording id %s", recording.strRecordingId ); + XBMC->Log(LOG_ERROR, "Recording playback not possible. Backend returned an empty filename and no RTSP stream URL for recording id %s", recording.strRecordingId); XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30052)); // Tell XBMC to re-read the list with recordings to remove deleted/non-existing recordings as a result of backend auto-deletion. PVR->TriggerRecordingUpdate(); + return false; } - return false; + // We have a recording file name or RTSP url, time to open it... + m_tsreader = new CTsReader(); + m_tsreader->SetCardSettings(&m_cCards); + if ( m_tsreader->Open(recfile.c_str()) != S_OK ) + return false; + + return true; } void cPVRClientMediaPortal::CloseRecordedStream(void) @@ -2174,3 +2279,15 @@ m_cCards.ParseLines(lines); } } + +void cPVRClientMediaPortal::SetConnectionState(PVR_CONNECTION_STATE newState) +{ + if (newState != m_state) + { + XBMC->Log(LOG_DEBUG, "Connection state change (%d -> %d)", m_state, newState); + m_state = newState; + + /* Notify connection state change (callback!) */ + PVR->ConnectionStateChange(GetConnectionString(), m_state, NULL); + } +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/pvrclient-mediaportal.h kodi-pvr-mediaportal-tvserver-2.4.16/src/pvrclient-mediaportal.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/pvrclient-mediaportal.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/pvrclient-mediaportal.h 2017-01-11 10:28:16.000000000 +0000 @@ -21,13 +21,14 @@ #include /* Master defines for client control */ -#include "kodi/xbmc_pvr_types.h" +#include "xbmc_pvr_types.h" /* Local includes */ #include "Socket.h" #include "Cards.h" #include "epg.h" -#include "platform/threads/mutex.h" +#include "p8-platform/threads/mutex.h" +#include "p8-platform/threads/threads.h" /* Use a forward declaration here. Including RTSPClient.h via TSReader.h at this point gives compile errors */ namespace MPTV @@ -35,18 +36,15 @@ class CTsReader; } -class cPVRClientMediaPortal: public PLATFORM::PreventCopy +class cPVRClientMediaPortal: public P8PLATFORM::PreventCopy, public P8PLATFORM::CThread { public: /* Class interface */ cPVRClientMediaPortal(); ~cPVRClientMediaPortal(); - /* TVServerKodi Listening Thread */ - static void* Process(void*); - /* Server handling */ - ADDON_STATUS Connect(); + ADDON_STATUS TryConnect(); void Disconnect(); bool IsUp(); @@ -91,7 +89,6 @@ bool OpenLiveStream(const PVR_CHANNEL &channel); void CloseLiveStream(); int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize); - int GetCurrentClientChannel(); bool SwitchChannel(const PVR_CHANNEL &channel); PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus); const char* GetLiveStreamURL(const PVR_CHANNEL &channel); @@ -100,6 +97,7 @@ long long PositionLiveStream(void); bool CanPauseAndSeek(void); void PauseStream(bool bPaused); + bool IsRealTimeStream(void); /* Record stream handling */ bool OpenRecordedStream(const PVR_RECORDING &recording); @@ -113,14 +111,20 @@ MPTV::Socket *m_tcpclient; private: + /* TVServerKodi Listening Thread */ + void* Process(void); + PVR_CONNECTION_STATE Connect(); + + bool GetChannel(unsigned int number, PVR_CHANNEL &channeldata); void LoadGenreTable(void); void LoadCardSettings(void); + void SetConnectionState(PVR_CONNECTION_STATE newState); int m_iCurrentChannel; int m_iCurrentCard; bool m_bCurrentChannelIsRadio; - bool m_bConnected; + PVR_CONNECTION_STATE m_state; bool m_bStop; bool m_bTimeShiftStarted; std::string m_ConnectionString; @@ -131,7 +135,8 @@ time_t m_BackendTime; CCards m_cCards; CGenreTable* m_genretable; - PLATFORM::CMutex m_mutex; + P8PLATFORM::CMutex m_mutex; + P8PLATFORM::CMutex m_connectionMutex; int64_t m_iLastRecordingUpdate; MPTV::CTsReader* m_tsreader; std::map m_channelNames; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/recordings.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/recordings.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/recordings.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/recordings.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -30,12 +30,13 @@ using namespace ADDON; -cRecording::cRecording() +cRecording::cRecording() : + m_channelType(TvDatabase::ChannelType::Unknown) { m_duration = 0; m_Index = -1; m_cardSettings = NULL; - m_channelID = 0; + m_channelID = PVR_CHANNEL_INVALID_UID; m_isRecording = false; m_genre_type = 0; m_genre_subtype = 0; @@ -85,6 +86,7 @@ //[18] isrecording (bool) //[19] timesWatched (int) //[20] stopTime (int) + //[21] channelType (int) m_Index = atoi(fields[0].c_str()); @@ -170,6 +172,14 @@ if (fields.size() >= 21) // Since TVServerXBMC 1.2.x.121 { m_lastPlayedPosition = atoi( fields[20].c_str() ); + if (fields.size() >= 22) // Since TVServerKodi 1.15.136 + { + m_channelType = atoi(fields[21].c_str()); + } + else + { + m_channelType = TvDatabase::ChannelType::Unknown; + } } } } @@ -178,6 +188,7 @@ } else { + XBMC->Log(LOG_ERROR, "Recording information has not enough fields. At least 9 fields expected, got only %d fields.", fields.size()); return false; } } @@ -330,3 +341,18 @@ return atoi(m_episodeNumber.c_str()); } + +PVR_RECORDING_CHANNEL_TYPE cRecording::GetChannelType(void) const +{ + switch (m_channelType) + { + case TvDatabase::ChannelType::Tv: + return PVR_RECORDING_CHANNEL_TYPE_TV; + break; + case TvDatabase::ChannelType::Radio: + return PVR_RECORDING_CHANNEL_TYPE_RADIO; + break; + default: + return PVR_RECORDING_CHANNEL_TYPE_UNKNOWN; + } +} diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/recordings.h kodi-pvr-mediaportal-tvserver-2.4.16/src/recordings.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/recordings.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/recordings.h 2017-01-11 10:28:16.000000000 +0000 @@ -18,12 +18,12 @@ */ #include -#include "kodi/libXBMC_addon.h" -#include "kodi/libXBMC_pvr.h" +#include "libXBMC_addon.h" +#include "libXBMC_pvr.h" #include "Cards.h" #include "GenreTable.h" #include "DateTime.h" - +#include "channels.h" #define DEFAULTFRAMESPERSECOND 25.0 #define MAXPRIORITY 99 @@ -44,9 +44,9 @@ MPTV::CDateTime m_startTime; MPTV::CDateTime m_endTime; int m_duration; - std::string m_title; // Title of this event - std::string m_description; // Description of this event - std::string m_episodeName; // Short description of this event (typically the episode name in case of a series) + std::string m_title; ///< Title of this event + std::string m_description; ///< Description of this event + std::string m_episodeName; ///< Short description of this event (typically the episode name in case of a series) std::string m_seriesNumber; std::string m_episodeNumber; std::string m_episodePart; @@ -61,6 +61,7 @@ CGenreTable* m_genretable; int m_timesWatched; int m_lastPlayedPosition; + int m_channelType; public: cRecording(); @@ -82,6 +83,8 @@ int TimesWatched(void) const {return m_timesWatched; } int LastPlayedPosition(void) const { return m_lastPlayedPosition; } bool IsRecording(void) {return m_isRecording; } + int ChannelID(void) const { return m_channelID; } + PVR_RECORDING_CHANNEL_TYPE GetChannelType(void) const; /** * \brief Filename of this recording with full path (at server side) diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/Socket.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/Socket.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/Socket.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/Socket.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -16,10 +16,10 @@ * along with this program. If not, see . * */ -#include "kodi/libXBMC_addon.h" +#include "libXBMC_addon.h" #include "utils.h" #include -#include "platform/os.h" +#include "p8-platform/os.h" #include "client.h" #include "Socket.h" @@ -40,7 +40,11 @@ _domain = domain; _type = type; _protocol = protocol; + _port = 0; memset (&_sockaddr, 0, sizeof( _sockaddr ) ); +#ifdef TARGET_WINDOWS + memset(&_wsaData, 0, sizeof(_wsaData)); +#endif } @@ -52,33 +56,24 @@ _domain = pf_inet; _type = sock_stream; _protocol = tcp; + _port = 0; memset (&_sockaddr, 0, sizeof( _sockaddr ) ); +#ifdef TARGET_WINDOWS + memset(&_wsaData, 0, sizeof(_wsaData)); +#endif + osInit(); } Socket::~Socket() { close(); + osCleanup(); } -bool Socket::setHostname ( const std::string& host ) +bool Socket::setHostname(const std::string& host) { - if (isalpha(host.c_str()[0])) - { - // host address is a name - struct hostent *he = NULL; - if ((he = gethostbyname( host.c_str() )) == 0) - { - errormessage( getLastError(), "Socket::setHostname"); - return false; - } - - _sockaddr.sin_addr = *((in_addr *) he->h_addr); - } - else - { - _sockaddr.sin_addr.s_addr = inet_addr(host.c_str()); - } + _hostname = host; return true; } @@ -86,14 +81,11 @@ { if (is_valid()) { - if (_sd != SOCKET_ERROR) -#ifdef TARGET_WINDOWS + if ((_sd != SOCKET_ERROR) && (_sd != INVALID_SOCKET)) + { closesocket(_sd); -#else - ::close(_sd); -#endif + } _sd = INVALID_SOCKET; - osCleanup(); return true; } return false; @@ -101,26 +93,7 @@ bool Socket::create() { - if( is_valid() ) - { - close(); - } - - if(!osInit()) - { - return false; - } - - _sd = socket(_family, _type, _protocol ); - //0 indicates that the default protocol for the type selected is to be used. - //For example, IPPROTO_TCP is chosen for the protocol if the type was set to - //SOCK_STREAM and the address family is AF_INET. - - if (_sd == INVALID_SOCKET) - { - errormessage( getLastError(), "Socket::create" ); - return false; - } + close(); return true; } @@ -134,9 +107,10 @@ return false; } + _port = port; _sockaddr.sin_family = (sa_family_t) _family; _sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all - _sockaddr.sin_port = htons( port ); + _sockaddr.sin_port = htons( _port ); int bind_return = ::bind(_sd, (sockaddr*)(&_sockaddr), sizeof(_sockaddr)); @@ -227,13 +201,13 @@ if (result < 0) { XBMC->Log(LOG_ERROR, "Socket::send - select failed"); - _sd = INVALID_SOCKET; + close(); return 0; } if (FD_ISSET(_sd, &set_w)) { XBMC->Log(LOG_ERROR, "Socket::send - failed to send data"); - _sd = INVALID_SOCKET; + close(); return 0; } @@ -243,7 +217,7 @@ { errormessage( getLastError(), "Socket::send"); XBMC->Log(LOG_ERROR, "Socket::send - failed to send data"); - _sd = INVALID_SOCKET; + close(); return 0; } return status; @@ -328,7 +302,7 @@ { XBMC->Log(LOG_DEBUG, "%s: select failed", __FUNCTION__); errormessage(getLastError(), __FUNCTION__); - _sd = INVALID_SOCKET; + close(); return false; } @@ -350,7 +324,7 @@ { XBMC->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__); errormessage(getLastError(), __FUNCTION__); - _sd = INVALID_SOCKET; + close(); return false; } buffer[result] = 0; @@ -415,54 +389,79 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) { - if ( !is_valid() ) - { - return false; - } - - _sockaddr.sin_family = (sa_family_t) _family; - _sockaddr.sin_port = htons ( port ); + close(); if ( !setHostname( host ) ) { XBMC->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); return false; } + _port = port; + + char strPort[16]; + snprintf(strPort, 15, "%hu", port); + strPort[15] = '\0'; - int status = ::connect ( _sd, reinterpret_cast(&_sockaddr), sizeof ( _sockaddr ) ); + struct addrinfo hints; + struct addrinfo* result = NULL; + struct addrinfo *address = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = _family; + hints.ai_socktype = _type; + hints.ai_protocol = _protocol; - if ( status == SOCKET_ERROR ) + int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); + if (retval != 0) { - XBMC->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); - errormessage( getLastError(), "Socket::connect" ); + errormessage(getLastError(), "Socket::connect"); + return false; + } + if (result == NULL) + { + XBMC->Log(LOG_ERROR, "Socket::connect %s:%u: no address info found\n", host.c_str(), port); return false; } - return true; -} - -bool Socket::reconnect() -{ - if ( _sd != INVALID_SOCKET ) + for (address = result; address != NULL; address = address->ai_next) { - return true; + // Create the socket + _sd = socket(address->ai_family, address->ai_socktype, address->ai_protocol); + + if (_sd == INVALID_SOCKET) + { + errormessage(getLastError(), "Socket::create"); + continue; + } + + int status = ::connect(_sd, address->ai_addr, address->ai_addrlen); + if (status == SOCKET_ERROR) + { + close(); + continue; + } + + // We have a conection + break; } - if( !create() ) - return false; + freeaddrinfo(result); if (_sd == INVALID_SOCKET) + { return false; + } - int status = ::connect ( _sd, reinterpret_cast(&_sockaddr), sizeof ( _sockaddr ) ); + return true; +} - if ( status == SOCKET_ERROR ) +bool Socket::reconnect() +{ + if ( is_valid() ) { - errormessage( getLastError(), "Socket::connect" ); - return false; + return true; } - return true; + return connect(_hostname, _port); } bool Socket::is_valid() const diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/Socket.h kodi-pvr-mediaportal-tvserver-2.4.16/src/Socket.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/Socket.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/Socket.h 2017-01-11 10:28:16.000000000 +0000 @@ -23,6 +23,7 @@ #define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support #pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" #include + #include #pragma warning(default:4005) #include @@ -63,6 +64,8 @@ #define INVALID_SOCKET (-1) #endif #define SOCKET_ERROR (-1) + + #define closesocket(sd) ::close(sd) #else #error Platform specific socket support is not yet available on this platform! #endif @@ -78,7 +81,9 @@ enum SocketFamily { - af_inet = AF_INET + af_unspec = AF_UNSPEC, + af_inet = AF_INET, + af_inet6 = AF_INET6 }; enum SocketDomain @@ -277,6 +282,9 @@ SOCKET _sd; ///< Socket Descriptor SOCKADDR_IN _sockaddr; ///< Socket Address + //struct addrinfo* _addrinfo; ///< Socket address info + std::string _hostname; ///< Hostname + unsigned short _port; ///< Port number enum SocketFamily _family; ///< Socket Address Family enum SocketProtocol _protocol; ///< Socket Protocol diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/timers.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/timers.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/timers.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/timers.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -23,7 +23,7 @@ using namespace std; -#include "platform/os.h" //needed for snprintf +#include "p8-platform/os.h" //needed for snprintf #include "client.h" #include "timers.h" #include "utils.h" @@ -39,7 +39,7 @@ { m_index = PVR_TIMER_NO_CLIENT_INDEX; m_active = true; - m_channel = 0; + m_channel = PVR_CHANNEL_INVALID_UID; m_schedtype = TvDatabase::Once; m_priority = 0; m_keepmethod = TvDatabase::UntilSpaceNeeded; @@ -49,7 +49,7 @@ m_done = false; m_ismanual = false; m_isrecording = false; - m_progid = -1; + m_progid = (EPG_TAG_INVALID_UID - cKodiEpgIndexOffset); m_genretable = NULL; m_parentScheduleID = MPTV_NO_PARENT_SCHEDULE; } @@ -327,7 +327,7 @@ if(schedulefields.size() >= 19) m_progid = atoi(schedulefields[18].c_str()); else - m_progid = -1; + m_progid = (EPG_TAG_INVALID_UID - cKodiEpgIndexOffset); if (schedulefields.size() >= 22) { diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/timers.h kodi-pvr-mediaportal-tvserver-2.4.16/src/timers.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/timers.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/timers.h 2017-01-11 10:28:16.000000000 +0000 @@ -24,7 +24,7 @@ #ifndef __TIMERS_H #define __TIMERS_H -#include "kodi/libXBMC_pvr.h" +#include "libXBMC_pvr.h" #include #include #include "DateTime.h" @@ -224,21 +224,22 @@ std::string AddScheduleCommand(); std::string UpdateScheduleCommand(); void SetScheduleRecordingType(TvDatabase::ScheduleRecordingType schedType); - void SetKeepMethod(TvDatabase::KeepMethodType keepmethod); void SetPreRecordInterval(int minutes); void SetPostRecordInterval(int minutes); void SetGenreTable(CGenreTable* genretable); - private: - int SchedRecType2RepeatFlags(TvDatabase::ScheduleRecordingType schedtype); - + void SetKeepMethod(TvDatabase::KeepMethodType keepmethod); /** - * @brief Convert a XBMC Lifetime value to MediaPortals keepMethod+keepDate settings - * @param lifetime the XBMC lifetime value (in days) (following the VDR syntax) + * @brief Convert a Kodi Lifetime value to MediaPortals keepMethod+keepDate settings + * @param lifetime the Kodi lifetime value (in days) (following the VDR syntax) * Should be called after setting m_starttime !! */ void SetKeepMethod(int lifetime); + + private: + int SchedRecType2RepeatFlags(TvDatabase::ScheduleRecordingType schedtype); + int GetLifetime(void); int XBMC2MepoPriority(int xbmcprio); int Mepo2XBMCPriority(int mepoprio); diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/utils.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/utils.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/utils.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/utils.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -24,7 +24,7 @@ #include "utils.h" #include #include -#include "platform/util/StdString.h" +#include "p8-platform/util/StdString.h" using namespace std; using namespace ADDON; diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/utils.h kodi-pvr-mediaportal-tvserver-2.4.16/src/utils.h --- kodi-pvr-mediaportal-tvserver-1.13.7/src/utils.h 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/utils.h 2017-01-11 10:28:16.000000000 +0000 @@ -20,10 +20,10 @@ #include #include #include "uri.h" -#include "platform/util/util.h" +#include "p8-platform/util/util.h" #ifdef TARGET_WINDOWS -#include "platform/util/StdString.h" +#include "p8-platform/util/StdString.h" #include "windows/WindowsUtils.h" #endif diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/src/windows/FileUtils.cpp kodi-pvr-mediaportal-tvserver-2.4.16/src/windows/FileUtils.cpp --- kodi-pvr-mediaportal-tvserver-1.13.7/src/windows/FileUtils.cpp 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/src/windows/FileUtils.cpp 2017-01-11 10:28:16.000000000 +0000 @@ -18,9 +18,9 @@ */ #include "../FileUtils.h" -#include "platform/os.h" +#include "p8-platform/os.h" #include -#include "platform/util/StdString.h" +#include "p8-platform/util/StdString.h" #include "../utils.h" namespace OS diff -Nru kodi-pvr-mediaportal-tvserver-1.13.7/.travis.yml kodi-pvr-mediaportal-tvserver-2.4.16/.travis.yml --- kodi-pvr-mediaportal-tvserver-1.13.7/.travis.yml 2016-04-25 07:36:03.000000000 +0000 +++ kodi-pvr-mediaportal-tvserver-2.4.16/.travis.yml 2017-01-11 10:28:16.000000000 +0000 @@ -36,8 +36,8 @@ # before_script: - cd $TRAVIS_BUILD_DIR/.. - - git clone -b Jarvis --depth=1 https://github.com/xbmc/xbmc.git + - git clone --depth=1 -b Krypton https://github.com/xbmc/xbmc.git - cd pvr.mediaportal.tvserver && mkdir build && cd build - - cmake -DADDONS_TO_BUILD=pvr.mediaportal.tvserver -DADDON_SRC_PREFIX=../.. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=../../xbmc/addons -DPACKAGE_ZIP=1 ../../xbmc/project/cmake/addons + - cmake -DADDONS_TO_BUILD=pvr.mediaportal.tvserver -DADDON_SRC_PREFIX=$TRAVIS_BUILD_DIR/.. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$TRAVIS_BUILD_DIR/../xbmc/addons -DPACKAGE_ZIP=1 $TRAVIS_BUILD_DIR/../xbmc/project/cmake/addons script: make