pax_global_header00006660000000000000000000000064134675670060014530gustar00rootroot0000000000000052 comment=903d69fe4490a800ab757bbb3e8a026fb6ea2b0b pvr.mediaportal.tvserver-3.5.18-Leia/000077500000000000000000000000001346756700600174645ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/.gitignore000066400000000000000000000011031346756700600214470ustar00rootroot00000000000000# build artifacts build/ debian/changelog debian/files debian/kodi-pvr-mediaportal.tvserver-dbg.debhelper.log debian/kodi-pvr-mediaportal.tvserver-dbg.substvars debian/kodi-pvr-mediaportal.tvserver-dbg/ debian/kodi-pvr-mediaportal.tvserver.debhelper.log debian/kodi-pvr-mediaportal.tvserver.postinst.debhelper debian/kodi-pvr-mediaportal.tvserver.postrm.debhelper debian/kodi-pvr-mediaportal.tvserver.substvars debian/kodi-pvr-mediaportal.tvserver/ debian/tmp/ obj-x86_64-linux-gnu/ pvr.mediaportal.tvserver/addon.xml # clion .idea/ # Eclipse/CDT .cproject .project .settings/ pvr.mediaportal.tvserver-3.5.18-Leia/.travis.yml000066400000000000000000000022411346756700600215740ustar00rootroot00000000000000language: cpp # # Define the build matrix # # Travis defaults to building on Ubuntu Trusty when building on # Linux. We need Xenial in order to get up to date versions of # cmake and g++. # env: global: - app_id=pvr.mediaportal.tvserver matrix: include: - os: linux dist: xenial sudo: required compiler: gcc - os: linux dist: xenial sudo: required compiler: clang - os: osx osx_image: xcode9 - os: osx osx_image: xcode9.4 # # The addon source is automatically checked out in $TRAVIS_BUILD_DIR, # we'll put the Kodi source on the same level # before_script: - cd $TRAVIS_BUILD_DIR/.. - git clone --branch Leia --depth=1 https://github.com/xbmc/xbmc.git - cd ${app_id} && mkdir build && cd build - mkdir -p definition/${app_id} - echo ${app_id} $TRAVIS_BUILD_DIR $TRAVIS_COMMIT > definition/${app_id}/${app_id}.txt - cmake -DADDONS_TO_BUILD=${app_id} -DADDON_SRC_PREFIX=$TRAVIS_BUILD_DIR/.. -DADDONS_DEFINITION_DIR=$TRAVIS_BUILD_DIR/build/definition -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$TRAVIS_BUILD_DIR/../xbmc/addons -DPACKAGE_ZIP=1 $TRAVIS_BUILD_DIR/../xbmc/cmake/addons script: make pvr.mediaportal.tvserver-3.5.18-Leia/CMakeLists.txt000066400000000000000000000243201346756700600222250ustar00rootroot00000000000000project(pvr.mediaportal.tvserver) cmake_minimum_required(VERSION 2.6) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}) enable_language(CXX) find_package(Kodi REQUIRED) find_package(kodiplatform REQUIRED) find_package(p8-platform REQUIRED) find_package(TinyXML REQUIRED) 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 -D_WINSOCK_DEPRECATED_NO_WARNINGS -DSOCKLEN_T=socklen_t -DBSD=1) include_directories(${kodiplatform_INCLUDE_DIRS} ${p8-platform_INCLUDE_DIRS} ${TINYXML_INCLUDE_DIR} ${KODI_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/src ${PROJECT_BINARY_DIR} ${LIVE555_INCLUDE_DIR} ) add_definitions(-D__STDC_FORMAT_MACROS ${LIVE555_DEFINES}) if (NOT WIN32) add_options(ALL_LANGUAGES ALL_BUILDS "-fPIC") endif() # Source files set(MPTV_SOURCES src/Cards.cpp src/channels.cpp src/client.cpp src/DateTime.cpp src/epg.cpp src/GenreTable.cpp src/GUIDialogRecordSettings.cpp src/pvrclient-mediaportal.cpp src/recordings.cpp src/Socket.cpp src/timers.cpp src/uri.cpp src/utils.cpp) if(WIN32) set(MPTV_SOURCES_WINDOWS src/windows/FileUtils.cpp src/windows/WindowsUtils.cpp) source_group("Source Files\\windows" FILES ${MPTV_SOURCES_WINDOWS}) list(APPEND MPTV_SOURCES ${MPTV_SOURCES_WINDOWS}) endif(WIN32) # Header files set(MPTV_HEADERS src/Cards.h src/channels.h src/client.h src/DateTime.h src/epg.h src/GenreTable.h src/GUIDialogRecordSettings.h src/os-dependent.h src/pvrclient-mediaportal.h src/recordings.h src/Socket.h src/timers.h src/uri.h src/utils.h) source_group("Header Files" FILES ${MPTV_HEADERS}) if(WIN32) set(MPTV_HEADERS_WINDOWS src/FileUtils.h src/windows/WindowsUtils.h ) source_group("Header Files\\windows" FILES ${MPTV_HEADERS_WINDOWS}) list(APPEND MPTV_HEADERS ${MPTV_HEADERS_WINDOWS}) endif(WIN32) if(WIN32) # Misc files set(RESOURCE_FILES pvr.mediaportal.tvserver/addon.xml pvr.mediaportal.tvserver/changelog.txt pvr.mediaportal.tvserver/resources/genre_translation.xml pvr.mediaportal.tvserver/resources/settings.xml pvr.mediaportal.tvserver/resources/language/resource.language.en_gb/strings.po) source_group("Resource Files" FILES ${RESOURCE_FILES}) endif(WIN32) # TSReader sources set(TSREADER_SOURCES src/lib/tsreader/ChannelInfo.cpp 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 src/lib/tsreader/PidTable.cpp src/lib/tsreader/PmtParser.cpp src/lib/tsreader/Section.cpp src/lib/tsreader/SectionDecoder.cpp src/lib/tsreader/TSHeader.cpp src/lib/tsreader/TSReader.cpp ) source_group("Source Files\\lib\\tsreader" FILES ${TSREADER_SOURCES}) # TSReader sources set(TSREADER_HEADERS src/lib/tsreader/ChannelInfo.h 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 src/lib/tsreader/PidTable.h src/lib/tsreader/PmtParser.h src/lib/tsreader/Section.h src/lib/tsreader/SectionDecoder.h src/lib/tsreader/TSDebug.h src/lib/tsreader/TSHeader.h 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} ${LIVE555_SOURCES} ${LIVE555_HEADERS}) set(DEPLIBS ${p8-platform_LIBRARIES} ${TINYXML_LIBRARIES}) if(WIN32) list(APPEND DEPLIBS ws2_32) endif(WIN32) build_addon(pvr.mediaportal.tvserver MPTV DEPLIBS) include(CPack) pvr.mediaportal.tvserver-3.5.18-Leia/FindTinyXML.cmake000066400000000000000000000016001346756700600225700ustar00rootroot00000000000000# - Find TinyXML # Find the native TinyXML includes and library # # TINYXML_FOUND - True if TinyXML found. # TINYXML_INCLUDE_DIRS - where to find tinyxml.h, etc. # TINYXML_LIBRARIES - List of libraries when using TinyXML. # if(PKG_CONFIG_FOUND) pkg_check_modules (TINYXML tinyxml) list(APPEND TINYXML_INCLUDE_DIRS ${TINYXML_INCLUDEDIR}) endif() if(NOT TINYXML_FOUND) find_path( TINYXML_INCLUDE_DIRS "tinyxml.h" PATH_SUFFIXES "tinyxml" ) find_library( TINYXML_LIBRARIES NAMES "tinyxml" PATH_SUFFIXES "tinyxml" ) endif() # handle the QUIETLY and REQUIRED arguments and set TINYXML_FOUND to TRUE if # all listed variables are TRUE include( "FindPackageHandleStandardArgs" ) find_package_handle_standard_args(TinyXML DEFAULT_MSG TINYXML_INCLUDE_DIRS TINYXML_LIBRARIES ) mark_as_advanced(TINYXML_INCLUDE_DIRS TINYXML_LIBRARIES) pvr.mediaportal.tvserver-3.5.18-Leia/Jenkinsfile000066400000000000000000000000351346756700600216460ustar00rootroot00000000000000buildPlugin(version: "Leia") pvr.mediaportal.tvserver-3.5.18-Leia/README.md000066400000000000000000000017141346756700600207460ustar00rootroot00000000000000[![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 MediaPortal TVServer PVR client addon for [Kodi] (http://kodi.tv) ## Build instructions ### Linux 1. `git clone --branch Leia https://github.com/xbmc/xbmc.git` 2. `git clone https://github.com/kodi-pvr/pvr.mediaportal.tvserver.git` 3. `cd pvr.mediaportal.tvserver && mkdir build && cd build` 4. `cmake -DADDONS_TO_BUILD=pvr.mediaportal.tvserver -DADDON_SRC_PREFIX=../.. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=../../xbmc/addons -DPACKAGE_ZIP=1 ../../xbmc/cmake/addons` 5. `make` ##### Useful links * [Kodi's PVR user support] (http://forum.kodi.tv/forumdisplay.php?fid=171) * [Kodi's PVR development support] (http://forum.kodi.tv/forumdisplay.php?fid=136) pvr.mediaportal.tvserver-3.5.18-Leia/debian/000077500000000000000000000000001346756700600207065ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/debian/changelog.in000066400000000000000000000003021346756700600231600ustar00rootroot00000000000000kodi-pvr-mediaportal-tvserver (#PACKAGEVERSION#-#TAGREV#~#DIST#) #DIST#; urgency=low [ kodi ] * autogenerated dummy changelog -- Nobody Sat, 01 Jun 2013 00:59:22 +0200 pvr.mediaportal.tvserver-3.5.18-Leia/debian/compat000066400000000000000000000000021346756700600221040ustar00rootroot000000000000009 pvr.mediaportal.tvserver-3.5.18-Leia/debian/control000066400000000000000000000013131346756700600223070ustar00rootroot00000000000000Source: kodi-pvr-mediaportal-tvserver Priority: extra Maintainer: Nobody Build-Depends: debhelper (>= 9.0.0), cmake, libtinyxml-dev, libkodiplatform-dev (>= 16.0.0), kodi-addon-dev Standards-Version: 3.9.4 Section: libs Homepage: http://www.scintilla.utwente.nl/~marcelg/kodi/ Package: kodi-pvr-mediaportal-tvserver Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: MediaPortal PVR for Kodi MediaPortal PVR for Kodi Package: kodi-pvr-mediaportal-tvserver-dbg Section: debug Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: debug symbols for MediaPortal PVR for Kodi debug symbols for the MediaPortal PVR for Kodi pvr.mediaportal.tvserver-3.5.18-Leia/debian/copyright000066400000000000000000000034651346756700600226510ustar00rootroot00000000000000Format: http://dep.debian.net/deps/dep5 Upstream-Name: pvr.mediaportal.tvserver Files: * Copyright: 2005-2011 Team XBMC 2005-2012 Team MediaPortal License: GPL-2+ This package 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 package 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 . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". Files: debian/* Copyright: 2013 Arne Morten Kvarving 2013 wsnipex License: GPL-2+ This package 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 package 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 . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". pvr.mediaportal.tvserver-3.5.18-Leia/debian/kodi-pvr-mediaportal-tvserver.install000066400000000000000000000000261346756700600302040ustar00rootroot00000000000000usr/lib/* usr/share/* pvr.mediaportal.tvserver-3.5.18-Leia/debian/rules000077500000000000000000000013721346756700600217710ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ override_dh_auto_configure: # LTO breaks with tsreader dh_auto_configure -- -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 override_dh_strip: dh_strip -pkodi-pvr-mediaportal-tvserver --dbg-package=kodi-pvr-mediaportal-tvserver-dbg override_dh_installdocs: dh_installdocs --link-doc=kodi-pvr-mediaportal-tvserver pvr.mediaportal.tvserver-3.5.18-Leia/debian/source/000077500000000000000000000000001346756700600222065ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/debian/source/format000066400000000000000000000000151346756700600234150ustar00rootroot000000000000003.0 (native) pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/000077500000000000000000000000001346756700600244525ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/LICENSE.txt000066400000000000000000000355641346756700600263120ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ------------------------------------------------------------------------- pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/TVServerKodi/000077500000000000000000000000001346756700600270015ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/TVServerKodi/readme.txt000066400000000000000000000001401346756700600307720ustar00rootroot00000000000000Download the latest version from http://www.scintilla.utwente.nl/~marcelg/xbmc/tvserverxbmc.htmlpvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/addon.xml.in000066400000000000000000000766421346756700600267050ustar00rootroot00000000000000 @ADDON_DEPENDS@ Kodi voorprogram vir die MediaPortal TV Bediener (ffmpeg + tsreader weergawe) Kodi frontend for the MediaPortal TV Server (ffmpeg + tsreader version) Клиент за ТВ сървър „MediaPortal“ Frontal de Kodi per al servidor de TV MediaPortal Rozhraní Kodi pro televizní server MediaPortal 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 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 liides MediaPortal TV serverile Kodin MediaPortal TV-server -asiakasohjelma Frontale Kodi pour le serveur télé MediaPortal 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) Kodi előtét a MediaPortal TV kiszolgálóhoz (ffmpeg + tsreader) Frontend Kodi untuk Server TV MediaPortal (versi ffmpeg + tsreader) Framendi fyrir Kodi til að nota MediaPortal TV Server (ffmpeg + tsreader version) Frontend Kodi per il server MediaPortal TV (versione ffmpeg + tsreader) MediaPortal TV サーバー (ffmpeg + tsreader バージョン) の Kodi フロントエンド MediaPortal TV Server (ffmpeg + tsreader version) 을 위한 Kodi 프론트엔드 Kodi sąsaja MediaPortal TV serveriui (ffmpeg + tsreader versija) Kodi galasistēma MediaPortal TV Server (ffmpeg + tsreader versija) Kodi интерфејс за MediaPortal TV Server (ffmpeg + tsreader верзија) Bahagian hadapan Kodi untuk Pelayan TV MediaPortal (versi ffmpeg + tsreader) MediaPortal TV Server အတွက် Kodi frontend (ffmpeg + tsreader version) Kodi-drakt for MediaPortal TV-tjener (ffmpeg + tsreader-versjon) 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) 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 ТВ Сервер Kodi interfejs za MediaPortal TV Server Kodi's frontend MediaPortal tv-server 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 服务器前端 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. Клиент за ТВ сървър „MediaPortal“. Поддържа телевизия на живо и записване, слушане на радио канали, електронен програмен справочник и броячи. 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 streamování živého vysílání a nahrávek, poslech kanálů rádia, televizní program a časovače. 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. 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 Serverin asiakasohjelma. Tukee suorien tv-lähetysten ja tallennusten katsomista, radiokanavia, ohjelmaopasta ja ohjelmien ajastamista. Frontale pour le serveur télé MediaPortal, prenant en charge la diffusion en continu des télés en direct et 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. MediaPortal TV kiszolgáló előtét. Élő adások, felvételek és rádióadások támogatása EPG-vel és időzítéssel. Ez a kiegészítő egyesíti az ffmpeg, és a tsreader kiegészítőket. Frontend Server TV MediaPortal. Mendukung pengaliran TV dan Rekaman langsung, mendengarkan kanal radio, EPG dan Timer. Pengaya ini mengkombinasikan penggaya ffmeg dan tsreader yang lama. Framendi fyrir MediaPortal TV. Styður streymingu á beinum útsendingum og upptökum, hlustun á útvarpsrásum, rafrænum sjónvarpsvísum (EPG) og tímatöku. Þessi viðbók sameinar fyrri viðbætur fyrir ffmpeg og tsreader Frontend del server MediaPortal TV. Supporta lo streaming di Live TV e le registrazioni, l'ascolto dei canali radio, EPG e i timer. Questo addon combina gli addon ffmpeg e tsreaded. MediaPortal TV サーバーのフロントエンドです。ライブテレビのストリーミングや録画、ラジオチャンネルの視聴、EPG、タイマーなどをサポートしています。以前の ffmpeg アドオンと tsreader アドオンを組み合わせたものです。 MediaPortal TV Server 프론트엔드. TV 시청 & 녹화, 라디오 채널 청취, EPG 및 타이머 지원. 이 애드온은 이전의 ffmpeg 과 tsreader 애드온을 합친 것입니다. MediaPortal TV Serverio sąsaja. Palaiko televizijos transliacijas ir Įrašus, radijo kanalus, EPG ir laikmačius. Šis priedas apjungia ankstesnius ffmpeg ir TSreader priedus. MediaPortal TV Server galasistēma. Atbalsta tiešraides TV un ierakstu straumēšanu, radio kanālu klausīšanos, EPG un taimerus. Šis pielikums kombinē senākos ffmpeg un tsreader pielikumus. 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-tjenerdrakt. Støtter strømming av direkte-TV og opptak, Lytting til radiokanaler, EPG og tidsur. Dette tillegget kombinererer de tidligere ffmpeg- og tsreader -tilleggene. MediaPortal TV Server frontend. Ondersteunt het bekijken van Live-TV en opnames, het beluisteren van radiozenders, 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. 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 и Тајмере. 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 Live-TV & inspelningar, lyssna på radiokanaler, EPG och timers. 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 服务器前端。支持流媒体直播电视和录像、收听电台、电子节目单和定时器。 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 qualsevol altre efecte indesitjat. Autoři nejsou žádným způsobem zodpovědní za neúspěšná nahrávání, chybné časovače, ztracený čas nebo jakékoliv jiné nežádoucí výsledky. 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.. Dies ist instabile Software! Die Autoren sind in keiner Weise verantwortlich für fehlgeschlagene Aufnahmen, falsche Timer, verschwendete Zeit oder andere ungewünschte Effekte. Ασταθές πρόγραμμα! Οι δημιουργοί δεν είναι σε καμία περίπτωση υπεύθυνοι για αποτυχημένες εγγραφές, λανθασμένους χρονοδιακόπτες, χαμένες ώρες, ή κάθε είδους ανεπιθύμητα αποτελέσματα.. This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects.. The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects. This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects.. This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects.. ¡Este software es inestable! Los autores no se responsabilizan por grabaciones fallidas, temporizadores incorrectos, horas perdidas, o cualquier otro efecto no deseado.. ¡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 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. זוהי איננה הרחבה יציבה! המפתחים אינם אחראים על כשלון בניגון, זמנים שגויים במדריך השידורים, שעות מבוזבזות או כל תופעה לא רצויה אחרת. Ovo je nestabilan softver! Autori nisu ni na koji način odgovorni za neuspjelo snimanje, netočna vremena snimanja, izgubljene sate, ili bilo koje druge nepoželjne učinke... Ez nem stabil szoftver! A készítők nem vállalnak felelősséget, a hibás felvételért, rossz időzítésért, elvesztegetett időért... Սա անկայուն ծրագրային ապահովում է: Հեղինակները պատասխանատու չեն վատ ձայնագրումների, սխալ ժամանակացույցերի, կորած ժամանակի կամ այլ ոչ ցանկալի երևույթների համար: Ini merupakan software yang tidak stabil! Penulis tidak bertanggung jawab untuk rekaman gagal, timer salah, waktu terbuang, atau efek tak diinginkan lainnya... Þetta er óstöðugur hugbúnaður! Höfundarnir eru á engann hátt ábyrgir fyrir misheppnuðum upptökum, röngum upptökutímum, klukkustundum sem að fóru í súginn eða nokkrum öðrum óæskilegum áhrifum. Questo software non è stabile! Gli autori non sono responsabili per registrazioni fallite, timer non corretti, ore perse, o altri effetti indesiderati... これは不安定なソフトウェアです!本プログラムの作者は、録画の失敗、正確に作動しなかったタイマー、無駄にした時間、その他あらゆる好ましくない結果について責任を負わないものとします。 이 소프트웨어는 불안정합니다! 제작자는 녹화 실패, 부정확한 타이머, 시간 낭비 및 기타 예상하지 못한 결과에 대해 책임지지 않습니다.. Tai yra nestabili programinė įranga! Autorius jokiu būdu neatsakingas už nepavykusius įrašus, neteisingus laikmačius, iššvaistytas valandas ar nutikus kitiems nepageidaujamiems poveikiams ...[COLOR=red](Kodi.lt siūlo/rekomenduoja testuojant šį priedą persijungti į Anglų [orinali] kalbą)[/COLOR] Šī ir nestabila programmatūra! Autori nav atbildīgi par nesanākušiem ierakstiem, nepareiziem taimeriem, iztērētām stundām vai jebkādiem citiem nevēlamiem efektiem.. Ова е нестабилен софтвер! Авторите на ниту еден начин не одговараат за неуспешни снимки, неточни тајмери, потрошени часови, или било кои други несакани ефекти. Тус програм нь гүйцэд хийгдэж дуусаагүй! Зохиогч нь алдаатай бичлэг, цагийн буруу хөтлөлт, алдагдсан цаг хугацаа эсвэл бусад ямар нэгэн хүсээгүй үр дүнд хариуцлага хүлээхгүй. Ini merupakan perisian tidak stabil! Pengarang tidak bertanggungjawab atas kegagalan rakaman, pemasa tidak betul, masa yang dibazirkan, atau apa jua kesan yang tidak dikehendaki.. Dan il-programm mhuwiex stabbli! L-Awturi m'humiex responsabbli bl-ebda mod għal rekordings li ma jirnexxewx, arloġġi żbaljati, siegħat moħlija, jew kwalunkwe effett ieħor mhux mixtieq. Dette er ustabilt programvare! Forfatterne er ikke ansvarlig på noen måte for ødelagte opptak, feilstilte tidsur, bortkastede timer, eller andre uønskede hendelser… Deze software is niet 100% stabiel! De auteurs zijn op geen enkele wijze aansprakelijk voor mislukte opnames, verspilde tijd, of enig ander ongewild neveneffect.. Oprogramowanie nadal jest w fazie rozwoju i jest niestabilne! Autorzy w żaden sposób nie są odpowiedzialni za nieudane nagrania, błędy w harmonogramie nagrań, zmarnowany czas ani jakiekolwiek inne niepożądane efekty. Este é um software instável! Os autores não são responsáveis por falhas de gravações, agendamentos incorretos, horas desperdiçadas, ou quaisquer outros efeitos indesejados. Este software é instável! Os autores não são de forma alguma responsáveis por gravações falhadas, temporizadores incorrectos, horas desperdiçadas, ou qualquer outro tipo de efeitos indesejáveis.. Acesta nu este o aplicație definitivă! Autorii nu sunt în nici un fel responsabili pentru înregistrări eșuate, cronometre incorecte, ore pierdute, sau orice alte efecte nedorite... Это нестабильная программа! Авторы не несут ответственности за неудачную запись, неправильные таймеры, потраченное время и другие нежелательные последствия. මෙය අස්ථිර මෘදුකාංගයකි! මෙහි සිදුවන පටිගත කිරීම් අසාර්ථක වීම්, සාවද්‍ය මුහුර්තක, නාස්ති වූ කාලයන්, හෝ වෙනත් යමිකිසි නුසුදුසු බලපෑම් සඳහා කතෘ වග කියනු නොලැබේ. Tento softvér nie je stabilný! Autori nenesú žiadnu zodpovednosť za chybné nahrávky vysielania, nesprávne časovače alebo iné neželané udalosti spôsobené týmto softvérom.. To je nestabilna programska oprema! Avtorji niso odgovorni za neuspela snemanja, nepravilne časovnike, zapravljen čas in katerikoli drug neželen učinek... Ky program nuk është ende stabil! Autorët së këtij programi nuk janë në as një menyr përgjegjës për rregistrime të gabuara, timer të pasaktë, kohë të humbur ose efekte të tillë të padëshirueshëm. Аутори ни на који начин нису одговорни за неуспела снимања, неисправне тајмере, изгубљене сате, или било које друге нежељене ефекте... Autori ni na koji način nisu odgovorni za neuspela snimanja, neispravne tajmere, izgubljene sate, ili bilo koje druge neželjene efekte... Upphovsmännen är inte ansvariga för misslyckade inspelningar, inkorrekta timers, bortslösade timmar, eller några andra oönskade effekter.. To je niystabilny softwer! Autōry niy sōm ôdpedzialne za niypodarzōne graniy, niynŏleżne godziny EPG, stracōne godziny i inksze niychciane efekty. ఇది అస్థిర సాఫ్ట్వేర్! విఫలమైన రికార్డింగ్లు, తప్పు టైమర్లు, వృధా గంటల, లేదా ఏ ఇతర అవాంఛనీయ ప్రభావాలకు ఏ విధంగా రచయితలు భాద్యుతులు కారు. Ин нармафзори ноустувор аст! Муаллифон барои вайрониҳои сабт, вақтсанҷҳои нодуруст, соатҳои бефоида ва дигар таъсирҳои номатлуб ҷавобгар намебошанд. Bu kararsız bir yazılımdır! Yapımcılar hatalı kayıtlardan, bozuk sürelerden, harcanan vakitten veya herhangi bir olumsuz etkiden dolayı sorumlu tutulamaz. Це нестабільна програма! Автор не несуть жодної відповідальності за зіпсуті записи, неправильні таймери, потрачений час, та інші небажані ефекти. Đây là phần mềm không ổn định! Các tác giả không chịu trách nhiệm đối với bản ghi âm thất bại, giờ không chính xác, giờ lãng phí, hoặc bất kỳ tác dụng không mong muốn khác... 作者不对录像失败、错误定时造成时间浪费或其它不良影响负责。 這是測試版軟體!其原創作者並無法對於以下情況負責,包含:錄影失敗,不正確的定時設定,多餘時數,或任何產生的其它不良影響... @PLATFORM@ pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/changelog.txt000066400000000000000000000403121346756700600271420ustar00rootroot00000000000000v3.5.18: - Fixed: TSReader: reset the timeshift start time after a successful channel switch - Fixed: radio webstream support (was broken since Kodi v18 streaming API changes) v3.5.17: - Added: GetStreamReadChunkSize support (Refs #92) - Fixed: TSReader (RTSP): pause hangs Kodi v3.5.16: - Fixed: skin background for the custom DialogRecordSettings on unsupported skins - Fixed: DialogRecordSettings for skin.estuary - Fixed: DialogRecordSettings for skin.transparency - Fixed: TSReader RTSP playback (regression bug introduced in v3.5.14) v3.5.15: - Fixed: recording playback when the description contains text with a return v3.5.14: - Fixed: Delete the tsreader when tuning to a new channel failed. This fixes the first re-tune to a working channel after a failure - Updated: recommended TVServerKodi version to build 140 - Recording playback: update the end time if the recording is still ongoing - Recording: Prevent setting a negative last played position. MediaPortal does not like this. - Recording: mark in-progress recording as "real-time" also on non-Windows targets - Fixed: various 64-bit compiler warnings - Fixed: Increase the command buffer size to fit the full contents of strRecordingId in DeleteRecording() and GetRecordings() - Fixed: tsreader: checking for < 0 on unsigned values is useless v3.5.13: - Translation update from Transifex v3.5.12: - Translation update from Transifex v3.5.11: - Fixed: GetAddonCapabilities() did not explicitly set all newly added capabilities - Fixed: Error: "Couldn't get 'defaultrecordinglifetime' setting, falling back to '100' as default" - Fixed: Kodi crash during plugin load v3.5.10 - Android: fix compiling for android with ndk-18 (credits Rechi) v3.5.9 - Translation update from Transifex v3.5.8 - Windows UWP: Fixes for recording playback (TSReader) via the vfs.smb2 plugin - Windows UWP: disable recording thumbnails code for now (does not work over network shares) v3.5.7 - Fixed: timeshift support for Live TV in v18 (TSReader) v3.5.6 - Translation update from Transifex v3.5.5 - Fixed: Windows UWP build - Added: Check also C:\ProgramData\Team MediaPortal\MediaPortal TV Server\thumbs for recording thumbs in single seat mode v3.5.4 - Remove Windows XP support - Don't update the Kodi connection state when trying to connect in the background thread. This prevents pop-ups every minute when the backend is down. - Report readable connection state changes instead of numbers - Remove the deprecated PositionLiveStream() and PositionRecordedStream() functions - Replace TVServerXBMC plugin references by TVServerKodi - Accessing %PROGRAMDATA% for TV thumbnails is only possbile for Windows Desktop - Replace all GetTickCount() calls by GetTickCount64() for UWP compatibility - Don't report an empty connection string in the log - Fixed: seek in recordings was broken in v18 - Fixed: recording playback as rtsp stream via internal Kodi videoplayer - Fixed: Restore file playback via internal Kodi videoplayer on Windows (PVR addon by-pass) - GetChannelGroupMembers: add iSubChannelNumber support (PVR API 5.8.0) v3.5.3 - Translation update from Transifex v3.5.2 - Translation update from Transifex v3.5.1 - Updated to PVR addon API v5.10.1 v3.5.0 - Updated to PVR addon API v5.10.0 v3.4.4 - Updated to PVR addon API v5.9.0 v3.4.1 - New addon setting for the keep method & default lifetime of recordings v3.4.0 - Updated to PVR addon API v5.8.0 v3.3.3 - MacOSX compile fix v3.3.2 - Code cleanup: remove legacy PVR API functions and CStdString - Set genre of recorded shows to custom string (Credits: Timothy Massing) - Re-implement fast channel switching logic in a different way after the removal of the SwitchChannel function - PVR API v5.3.0: Add PVR_TIMER_TYPE_SUPPORTS_ANY_CHANNEL tag to the "Every time on every channel" record option v3.3.1 - Translation update from Transifex v3.3.0 - Updated to PVR addon API v5.7.0 v3.2.0 - Updated to PVR addon API v5.6.0 - Removed GetLiveStreamURL functionality. v3.1.0 - Updated to PVR addon API v5.3.0 v3.0.5 - Remove of never used addon interface function v3.0.4 - Update Debian package control - PVR addon callback way changes v3.0.3 - Removed old no more needed version functions v3.0.2 - set dependency versions automatic during build - removed never used kodi to addon functions - build system cleanup and fixes v3.0.1 - 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. v3.0.0 - Initial Kodi v18 version 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 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 GUI API v5.10.0 v1.11.7 - Fixed a regression with timer id and epg id handling introduced with PVR API v4.0.0 v1.11.6 - Updated to PVR API v4.1.0 v1.11.5 - Updated to PVR API v4.0.0 v1.11.4 - Updated to PVR API v3.0.0 (API 1.9.7 compatibility mode) v1.11.3 - Updated to PVR API v2.1.0 - Automatically fill in platform and library name v1.11.2 - Debian packaging: fix library install path - Miscellaneous backend fixes v1.11.1 - Updated to PVR API v2.0.0 v1.11.0 - Updated to PVR API v1.9.7 v1.10.7 - Updated language files from Transifex v1.10.6 - Coverity fixes v1.10.5 - Updated to use new libplatform-dev v1.10.4 - Updated to PVR API v1.9.6 - Recordings: set recording thumbnail path if found on the backend (only TSReader) v1.10.3 - Move tsreader sources into the MPTV namespace to prevent a crash on RPi when using the FileReader class. Kodi now has an internal FileReader class (since xbmc/xbmc/pull/6306) which is somehow used by the addon instead of the class in the addon. - EPG: Use the episode name as plot outline (when available) - Channel: add support for ATSC Major.Minor channel numbers - Radio: attempt to fix the short hickup after 1 second when tuning a radio channel v1.10.2 - Updated to PVR API v1.9.5 - Fixed: Schedules: Fix displaying an incorrect day in Kodi for 'Weekly' and 'WeeklyEveryTimeOnThisChannel' series recordings - Fixed: Schedules: change the displayed days for 'EveryTimeOnThisChannel' and 'EveryTimeOnEveryChannel' to all days instead of sunday v1.10.1 - Updated to PVR API v1.9.4 - Updated to GUI API v5.8.0 v1.9.27 - Updated language files from Transifex v1.9.26 - Updated language files from Transifex - Minor changes to conform with C++11 v1.9.25 - Updated language files from Transifex v1.9.24 - added getBackendHostname function v1.9.23 - Fixed mime-type for MPEG-TS v1.9.22 - Updated language files from Transifex v1.9.21 - change library name to Kodi v1.9.20 - fixed deadlock when activating the addon on some darwin (osx, ios) runtimes v1.9.19 - Updated language files from Transifex v1.9.18 - Updated language files from Transifex v1.9.16 - Updated to API v1.9.2 v1.9.15 - Added: Series recording support (requires skin specific dialog) - Fixed: [TSReader] Direct playback of recordings that are stored on a network share v1.9.14 - Updated language files from Transifex v1.9.13 - Added: Live TV/Recordings: Report "permission denied" errors on SMB access failures - Changed: [Settings] enable fast channel switching by default - Changed: Reduce the signal status calls to once every 10s. - Fixed: Channel thumbnails: replace also the invalid file name characters <>*?\| in the channel name by a _ - Fixed: [SMB] Allow an empty username and password field - Fixed: [SMB] Don't replace \\ by smb:// when not at the start of the path - Fixed: Stop timeshifting at the backend side on buffer open failure. - Fixed: Recordings: Fix playback for in-progress recordings. Play them always via the PVR addon to account for the growing file length - Fixed: Recording playback over rtsp in ffmpeg mode v1.9.12 - Updated language files from Transifex v1.9.11 - Updated language files from Transifex v1.9.10 - add timeshift buffer functions v1.8.9 - TSReader: Fixed: channel switching behavior when switching across tsbuffer files - TSReader: Fixed: Windows share access in multiseat mode (return smb:// URI's instead of \\UNC paths to XBMC also for Windows) - TSReader: Fixed: SD<->HD channel switching with "fast channel switching" enabled (add PAT detection to find the exact start of the new channel in the timeshift buffer file) - Recordings: Add support for retrieving and storing the playback resume position (requires TVServerXBMC build 121 or higher) - Recordings: Trigger a recording list update when the backend couldn't find the selected recording - Fixed: potential out-of-bounds array access while accessing TV Card info - Fixed: Hide smb password in debug xbmc.log file - Added: support for retrieving channels from multiple specified groups (>1, < All groups) (requires TVServerXBMC build 122 or higher) - Changed: drop support for TVServerXBMC builds older than 1.1.x.107 - Changed: more log notifications on errors - EPG: added additional genre strings to the genre string to id translation table v1.8.8 - sync with PVR API v1.8.0 v1.7.8 - Bump after PVR API version bump v1.6.8 - Channels: Use MediaPortal VisibleInGuide property as inverted IsHidden flag in XBMC. (requires TVServerXBMC build 120) - Channelgroups: Don't import other channel groups when the user selected to import channels from a single group. - TSReader: non-Windows: start playback as soon as the buffer file has a non-zero length - Genre translation: seach also for the genre_translation.xml file in the profile folder. v1.6.7 - Updated language files from Transifex v1.6.6 - Added: Set recording play count on backend (requires TVServerXBMC build 118) - Added: Enable/Disable support for individual programs on Series Schedules (requires TVServerXBMC build 118) - Timers: Fixed: MarginStart and MarginEnd are in minuted - Timers: Fixed: Update and Delete of timers for series Schedules - TSReader: additional debug/error messages on recording/timeshift buffer file access errors v1.6.5 - Updated language files from Transifex v1.6.4 - New version number by Team XBMC v1.2.3.117 - Added: timeshift support for XBMC Frodo v1.2.3.116: - Fixed: playback of recordings with non-ASCII characters - Added: Hungarian translation - Added: Korean translation v1.2.3.115: - Changed: Switch to gettext (PO) files for addon translations v1.2.3.114: - Added: Live555 RTSP support for Linux v1.2.3.113: - Simplify settings. The TVServerXBMC plugin now reports the sharenames for the recordings and timeshift folder v1.2.3.112: - Merged: tsreader and ffmpeg addons are now combined into one addon v1.2.2.111: - Fixed: Day of the week mismatch for timers between MePo and XBMC. Credits: jajoflo - Added: Trigger recording update on timer retrieval so the recording list is kept up to date when there is a new recording. Credits: jajoflo - Fixed: tv group retrieval requested radio groups - Added: add support for recording genre field - Fixed: fixes for recording filepath split-up - Added: show an error message when the recording filename or playback URL is empty v1.2.2.110: - Added: use channel icons from MediaPortal (Windows, localhost only) - Changed: switch from libPlatform + libTcpSocket to platform library v1.2.2.109: - Fixed: fix mimetype for tv channels - Fixed: make sure that OpenLiveStream does not return true due to m_iCurrentChannel when the TSReader fails in a later stage - Fixed: compilation IOS/OSX/Linux - Fixed: compile fixes for API move into ADDON namespace - Added: show localized error messages when OpenLiveStream fails - Added: signal status support - Fixed: Don't return encrypted channels as group members when FTA only option is turned on - Added: add more debug log information about the addon settings and status - Added: add support for direct recording playback from network shares (smb, Windows only) - Added: implement support for recording subdirectories - Fixed: don't return radio channels/channelgroup/channelgroup mappings when radio support is disabled v1.1.3.107: - Added: add additional checks for communication errors - Fixed: trigger also a recording list update on timer changes - Fixed: add a mutex to prevent mixing up backend communication on concurrent access - Fixed: stack overflow while closing a live stream - Fixed: tv/radio playback after recording playback - Added: EPG genre string-to-id translation table (addons/pvr.team-mediaportal.tvserver/resources/genre_translation.xml) - Added: EPG genre string support - Added: Retrieve TV/Radio card settings from the backend - Added: support for TVServerXBMC v1.1.x.105-107 - Fixed: several memory leaks - Changed: sources adapted for PVR API changes v1.1.3.103: - Fixed: trigger timer and recording list update on changes from XBMC side - Fixed: check for empty recordings list - Fixed: check for empty timer list - Fixed: limit EPG request to the requested period - Fixed: use tuning details to retrieve the channel number from the backend - Added: support for TVServerXBMC v1.1.x.104 - Changed: sources adapted for PVR API changes v1.1.2.102: - Added: Channel group support - Fixed: live stream playback - Fixed: recording retrieval - Changed: sources adapted for PVR API changes v1.1.2.101: - Fixed: "Include radio" setting - Changed: cleanup unused settings v1.1.0.100 - Rewrite: timer code - Changed: BackendName - Fixed: Retrieve more details for timers (repeat, lifetime, ...) - Fixed: Playback of radio channels with a webstream URL (added via the MediaPortal TV-server) - Fixed: "Free-to-air only" setting - Support for TVServerXBMC v1.1.0.100 v1.1.0.98 - Log more information on socket related problems v1.1.0.97 - Fixed: Use uri decode for ip-address in hostname field v1.1.0.96 - Fixed: allow spaces in groupnames, recording names and timer titles - Fixed: GetTimerInfo - Add debug messages for empty channel list v1.1.0.95 - Fix time mismatch between XBMC and MediaPortal TV Server for new timers (scheduled recordings) - Send genre strings to XBMC for unrecognised genres. No colors in EPG, but at least the text is now ok. v1.1.0.90 - Faster channel switching (around 2 sec faster) - Decrease CPU uage when a buffer underrun occurs v1.1.0.75 - PVR client should abort connection when the TVServerXBMC version is too old v1.1.0.60 - Fix PVR client destroy pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/icon.png000066400000000000000000001526351346756700600261240ustar00rootroot00000000000000PNG  IHDR?1 pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxwdqW9v͋ͻX $f$E*$[eIz~ғDz(KeYHI1  ,8;ァ3;=ٙ֩o} "@D*䟆?PU\5j͞W=qKxxGq={a=vϭ^K,ݒsqC\Z/-8^Do4MALY}"`ֹ)t=!ⴇj)a;'`*^D'6 mNu9'1aoqyrzNQ ?u[_X,=?##%?{"[޹^Q,_'Pw2\L#Kׅ%@OŠ |S]$^L@_.""TX^ ^~#+׿]X_~J ;FTU+"oҞk `Xޅol C@!M/qyHq+s_ F:iz @[q(LU@<}_ǁGH%tYt[0 ' .fpZsU_$T*N P0ɽ+rr3oTSax|#B"&݅]=j?>"VzQDF,_g|tW<&"V8zA?J=%.=#<&-EX'c/[Dž#bg߸PwFe爋g(X.x8 5Q`#mճޗ?G&V;P}%rNJB8ο h,.P6㭜o|;,=_N{CB2c).N^xLp6/~Ϥ3Z$,Ww-L7^%w%|rZ`AυN!as~\=%2{L}s+ӳ`RBSE`Ѕ&+)e/Tzp׋%%.Lc˖.㒏@m <ѐ,"kl6Ƞ51aDR@`v`l! kX*,2)ZZ| \3AC."6MF0d  dAU""B#ElU%0\<Ћ%xx-\Ds3C[Y,} WYd9! C CΈDHHRdhbb+⸣ 6<Ӡ$RqNa`@Ӌ.$T ]|o^&/Ƀ&*h ԠA@)9k["l !!*ʀ Pթ(%:? Zɢޓ7}9ieE]")j"޼ACh  ICJUTD &c1Df2Nwz<|G0!Z U s3ĆngWvwE.ueBOٳxwѷV^ت/q5oX\>v#+)KvӇg9c'^{)N2V(,[>hEYUZ%@^Z`f(B `m_]{ݰ7m敛vN1QD“a2Β3?sd|C?:sd/lI偌[|6^ۯX%nd,RU?@ĉعcSwkOȮoO]z7`Ớcsm[m ™+hii(H^|g6빘*|TR ;A$q[5v5h?`*UmϐPE$sG\ןJ6tmWuQsD8KU؃PSrv!4iO8Sמgv UMr]]v罗\ysstl.SvU98{ hȘngWx९}b3KavCş6[x/n*jK5 ArAD46&cS}/?ם=UT1$! $ !9"&iƬED ڕ+VSC=8:~Gs[Eplwprc.K@{rQ|"N0x$°#^{=}k/,#)/z#W{&7lCDN0$"kM'Ɠ{bt3[z^Za; /@ʾ9;C` ظ)쎾Է?Gcđ5DCH*= ,8sVoa9NJ\ȘU+VS0.Z*\NQE$*ss 6u$?\p .pT$~Q/"18v>4_˙ï’׫Km7nIV ظ=g>=W4V\\U˒1D"<7Ћ?vFaebtI P%h)/FVXeFu9zOȳKGcG9AU0~;~g6]&.,UU)+*xo>QUQV9avY% b믺=]vZ5nz?㲻qå]YejeWk_}r߮cK~BD n߷~_lr'yP&Ty6Wx@ة͗}.fr4 )+ END}*=uB" ڗ" gŗ +*‰߶=?_.,U=JEC_XD 0 s,Ήs92e.8Kn(ZM{;{r&M;tLcd%l}/Ɲw'm 5W]?ښ\뒎g߉*ǒ!g(yλSNgDm4bCc^$0Z `[͖h$ @ĕl$J@d._Ο7F]ҩx݋g^BTAAEħC,"Nc 9vΉ˜K,6ׯyG +xKew+.mM^v{Z+|1t ]ƫ3/{Γ΀|-=7.[;nW~S"4P G",yҏPz[͖Ag6 @ApF"\`)Њ ;>/~{d =P3Qկ caǎx;f.K-74&玼YZ-:}?ՑksW %WܲzGv=ޝ9C(ںn|玷h2،cCHrav "Z-e6P&u& @6@q]v.Mn3w^_ܞu@B[v*~Q _e2fҌ3Ǚ˲̹]eI"92ʣ.i{"c]? YbK|W?ygh߿J]iE<<P՟,'#w|j՟!k)n* $il{ i~8Sq"qE#I֙9>nϵ;n܅r6\yzUm0"" u0/as.sssNfae^Of?+}?Ì)"dΛ~zġ_qgPtaffG^o}tz߳'m]-Y_nCk|kxw7ԑ5[V]{ 50 g3"&nl魜vN8{̎Y0g?oDh6Wb-{hfg:i"Fc51;J#=_0n~&KKj`9O33|U;aWy/5?w~%>@ 7WLlΞ05;=y@&!%tRF (pcZ6jjj,d>Ri4Fg禘YxD>Qu/:󋤏k 0RUpD92 @ݿs?G\7\BP|txbт#6ڱuy*AO &_/Zk,3 d5g[ٻA= F@ 3k!u>o{,i Ւ t TT'cNGYX3>?sEo^_ Ah%MY掾_x-fpT ?BwAp C =GOELXNyzcc@ \Y3UV#ϥ4Ͳl0dln~.se8Hq;S^EN^#Tӝ_ o,.r 4s+:ޣO~JDtͭ.…gAЃ.|jߋG_;gmXCBD4^w ɘ)Aa1F6nPƄd1E1 DZ!p7~Y \VUpXLWP*m5ZqKIptٙSOf쩠%a%Q5e7ݢo^hZfG-Jd@@TtTdC+JGADTAyv.M|u;~x-oͺ3V39SgbH jhպu~Ϟxdn N~dt&Q~@ &aVC$"H@dHuO=v`Ⱦ.Qsdd庉5'oX6G@\*gq+o_zwMUw a̷*@I{6v,8K2.+XtEK>~@7^rMK cDιC'3.L԰xtijfUQA7lfLzc U }HY2TtȘFkbت Dd-FQ[wȓaDL;s>  2:,{dUH@LD&j:q<?yda] ٸr ܾ{_~cs| Dvxs5FBOK},_iʑt_W9+7龿/YTg7S؞ǾtΝmc9ڜX=vnY֪HTS u?uo~|"(V{_{|ԡWۧ%3>Ī5.mշXYco}zp| 0SD|PFd^}o_}Svv?8{kO㱏n-}5v2Wv=v ((3uamZz}ĿyLioP@Ig^'ܟT^ĪuWޱ4VlN5q.soz?2z'K6:·~ S0${|wWfIw$ 0z77q|oㆉl܈FFiN_wSe5ۮw;~9LH"9=_e9fVe'N'2ס^a[ڸTxFsO܉CP=!K:_>CmfNoOmƍd~?O_s%Ăs'~ }xm6næ[FlS7_hɳ1_ھ'%⺆}Q3>C/<6~ʍ eذ螧_}[LҌ3R%h :W}.K-d?ϿprP <ݳBv>ӝ?RF3v9<pLJd%54N?}Kqij4=v`׃cƫnEҚX5wgNc$5cfSK_g?y& ݿO1YgDM܈'?4g< >]p@<2>@B׿gIO^ ԡ/YއS|wQ*D͑/=_؃~~aƻu]^$j:#+/WlkdȳO}0l[ڦp9K>չt[<*-̒"O0jvlM|LJ hѿot϶åxt~}lĦմd]2ᴔm5OʏsM<0~M+ةC{ ר5Xc7fKL2) ogN⶛Gƕd/>񂋾/6Z&B-㶷GQW05[/=7S(.Y{.Ѭg`ņ.PT<26o3H%J&7?[#C"P[thS^?^ysO;ڡr];n{gԀjt/`]ȝ5;+ܿ7asV^{v/j:+Y c|:R.čG*mշ}N{X| 槹sȀ37CW_>1׼0kK dN͎CWy?+6ld;;A@ߞ9W v?_q"/鏉Oް"pP=Uz=f؜^huWwsŋ3gsxtipI*MnԵ5Y?Cfsltܞ%sgztuc] {aryFE:g!";ԣ_q Ԥ}9jQ]8sb UzNhtFwhO=~*_ƍewUH|0E,{幷e\Z!TMG3Xzv0FNDQ&gD1s)We^-= G_ ,2=,34FΕzK \R 0tN{L&[ˋFa kra=g_QCgq _P.%fGUv6\,8eo9(quA, .ZfXh%zf.'&gYeCdªsiKIްr`lߨpc&8\>;ZK9_EB/oțD?*]<$3MV@ NF ROS!te=MA]q/a.:r5\@6BTiS-G͑aW+b]%,ys+R!,t̾;aN<p.ǖuigTh.^WV-lG&zbQc+|ksͧgzBTn*ׯ>g&g*ϒ!s,t1 SK94vӠl`Mir+wGrÍ!2h,4U]d\h,x?-lg~]_T%DkxO93pGEUWl~O֊5q#+Mt 7nǁ{MIgXJϝ20ewd"ꠟN;?olcIg`ȑwrwZ 7<^TԂ獥H) >, )%b !#&8 !-ign/tUۚ\{w$Z#"t0PΒ`%zc""g7S?mp._n?rS-tc疛y3xGsP^!TbqshՖ+%]Gw=W>Z;azTxu\p z8IZ"I$KqۭopmK|eNmj,++uHƸK"2.~cNf IeŰy5Xm*]RVs]*E8B.ĆE]1qһr='ɯ~a:Ȋ5]7m.Ec%K93h>?,C{ ,{ ?`6t2("SßaKMgOr\5U%4fe7=;{ߝ?id#7c+m^ul3;`|c;c̅{t*JsP}g (JM <z`9?y.[&noX!%$8XUsS(~: Szv2tеh 1&B41ƠDDMf5VU+evbp嫪OEO~d#P}'~GA"PB ""d51֠1@#ev:f'̈ @Xk20Jp1/E3=@N / χ eᙆ\? |?ZJ7֘QD UYD]XYYDEƴ(*(,j5"AUNT|R!n4V~km_Z_PvDXE 8h/='vهOGAmo5ʪ*9aafx3*ABBe@$RuL7/ETa'^}@9  *[εq}c7n{}?zߚ;~`KէU j4aVQQfPE@"Y>[@$ ChB!-bSq.KC$ \L*Cx /Gl}Rj`eJ> \*C\* 㻟y遏_{ߏ^nXiWϱw#C7ACզ[w@>UUQbSntRPQvRWl˥u h@d! *I%lO\^npy-oꦁm˞xk4Qgso|z5mR`ņ SȈVQEBTRΒVDRՠJUUT@@P~(%PbB|̧̜6Z@LuP7[LGz5"4 b/!ɽ/<~yuw]p1".3%%Ӈ#= V)҄3DQU'%!UQ(oJrֶGAʼnn a%xC(-e8}xh}nJX2R9cZJf3OƨȁqG.klMvN "2̝<ɯ}K'@d1CCNQ医6ŧ9Q dt/l(3SjT^& sڸ1j_?Hd@ipdfުC@䠬fWJrg8Gf>C{d+M7ymȸ2 ;U7P>l*x_;7gSUEhZ8@4@X_樀*{/h?!GG%*3V&DZXC[`O{Ҟ^,h3U}_M+(d H*rr'>-Wu;oXҨ55GSwS@DdD8NW;sHlSb` w+Af uT *ʠh K(s&U@TDEcKo#lleX(䃎a:!ߊDƐ!"D2P1/ETp7˺ Ja@<ᆟ /DħJŷC 0 ~Ķru)PX҃@F-e͝88w⠗"Z5&m$vg;.4Qf4A$ [Cƭ*섌EH5CB DB&be/@D@H a**]Qgq+nMRBoxA w:x;~EEf7G5Ǟ(w wӼ `15d ĥm =ʒ_p֙=3.?F}@@Bcc Ice3AW$01mD&" O!hED]&*N)瘎0Rn~)K;qc1C#͐` B~#+Hu*jz=۩Z1m3CVC+T ۠iRdC"XmnyjǪ 5Z[$X2%L-Vl"ߊ?Qfβ,euN8`~M[\LqZ@ReI̓0FPl+:V=z*1M?Z 2t bs((c-jX~ŕ*;!uhkȤhv,c8QaUV9 }hJzɃsZZҡ*Z2M{t WX}9>PKioΈwP ;3;V_abR}caQA2 YE&D4[T7ͪpG~N++#G+SgWLQˌ6b 8,u ˫ICQ F C4` |FwT{jKP ʉ{%Rc7VV8$hcLŁd޹TNw:W9Z Q 8U ro B$2"0gF+ҳ0_hC`fu՞Vp+jxRo7SX8^nBҜ+`"CH!4dA`]&itX4IJNڞ)Hu0𙱮|[!%ǗhW:0]-#Ɔ Q@kc"ڳ &P_\O k%'TC{qЋEs܆% *3嬢G[`؛ZtD{"KhQhPܢo{!$Kh !Yc-!&Li!} &"jƑEԹ16&S`)t`CXi3/BKXCm,`)]~3*j&"5R/h/vgʻ"t; LlLmd}TE;??%[ɑHTi؛VT H BH"K&B"땰Z$\M_,UV^X@(n5-dYU={"*J?BF4SUp \{M^+?p,1$D h%՘ lUEEB PDFEDd)ԉ %k)J!HY8y+/i[`P$ 6Q;UECH ,¢Y*NL#硂.iuՒXFUZF,d N@*VMQa``S7)~܀hX:t0(L}kyb Eʍ4bUɄb% D!cQf``u9!sa:գ~ {'Vsa,w:sl]\kpK2.'Ŗ -̟#KB4 0 oD غݕܣ?`ek"d 5d wYzkWx si'JjѺV QAQAhdJPHӮ9cDD$e^tV7U Y%N,+r#1r,%,#}aeK빿bPYD.]HʇE ӡXEPkhJw 1Eɉ5DPQ4s"8UPߞ0 65b[ua~dABOC2X7o0gR0BֻDfUc*ﻧ;I,JXR׊"W<^Z)$$?PГTsdжcom4zazϖ_k}YAIZzIiLnY"8#ȅB0@%iLhq $a4 ¬P;#l7(#B֯TP "21*s%U!byHH 5 $9QAUqi2/BPEc E[t`뭾xb]h 1}W'⧇0Tj1Bsv&ОA"BE@DDJЏ}ea믮s(!:hI0t{>s#!e,m+VXᬐ2?-#TS#8\ۆhElڃK*>E FC}H" `Hɵ`eqDAV"**h2xVLPY0jx 0F{;U5-H,Nz|bIׁ -[@ &cp&eҋ=u(icP$˺IP@O5YCc_XE1VSU$DQ`_ĠLrW4@B2HK*qhEUDe3+!XCfkT/9WX3g9 K*+i%k4^Bkldl#s J(y KRhTTɪ}*ĒzؾT̉D$4*إ.#Ec@TǾQjԻN/1 Ԫ+ *FCŋ ((Q$ A0*)0;Q@ /ԐI5,* BZQnj8CcheAiPj:4/#%DBH22jUP.@*( T ":"d,o K0< *KJBX~ϨNo* ), &URqKEP#zCQdǪ"e"3,ZTQfBX@pAL{Lmds W-q\2 `ө r ae` 2d#n$PY24sOҫ02RA"©DUg&"(j" $KD9]"@T*XwW Y&Tjڐ[lceq\ƙs40fsº=y;`ԫ  LkkYEazE[m'ĭ/i qs̥% E`ɉ5H9˼}*%"7U%ZN =Y} ȫ:XUlz5E]hF\*QgL1RК7byYjLhm[ I9avsD(d%54a"kʊZXJ6n4Zm"KA\~J_ڤk%JZX:冨9veڊ~E1]iĩm6P, ?bFcl+]Q@c$' k!48vjȐ1֢1EKi9"F!Fr'a\BA3SUPT)AE4يItLĩ8?MXJiQV/@ZA .)MıQP DX8!Ĥ*'7Q*KYg6j b)pl* "_D چ5Ql v:3s'QI5MS|}qEd*SXcbbB?]0DHDXZ5F|re̙K*71iلj.=UHA0TBX/4BduxsιT9C|qck'k-RZnKQ)/ju}>ߛBm30]RQTE)ѵk@k.6jVv撹&7]f#Ž W7P$4(YD ƶ1FQ5u8nAbrϻ"d|9C~alV"5AGa`֘v\ N+^QeB[&bMJ\w~ AE$  ("Y,WE%G!0B2Z"k%&2&ڞdTY:?7%hmFMn%ePgMVTb$SC'VaEeUEM\,.A,u~AjIC(1gc붮|k/ۀ P*|T6nE.p"EXߩGHVmlY2F}&S`eQ1֩qЙD!I TW[q b27͖ Ћ @,۳2ChɀADbf@e'ֈ0F#y"К0 *"YEBVW+5:ːQpΡr32E}Q+V =Vu\r_{o;3x-w1*N Z~vyk#?dԶ (s민udf O:c>P%ix$ *"OG!*ZbDʮ1ro Kv`cCbӰT*̩}[G,dV/k 0PqRU5droS^6p{PfK%^sG~5n={Y ΀H@Il8`$ox-쒡fTĞZ2Ӈzy/>ABT V!svxw Pz(:Jn*X ϵ1xL5yʈ@*HHhe0s&¥bFUXԁz+ 0I}<;+`z$:?"0SijGPI~F$;U#p=U^cp_uս??tnlE e1*(gy D#HLlMlHu Dέ]6!dn`cwzc{Dov!QdRt(_uqԋp.r )U5R{$: >Aa\ ,n6޻KRECrOJ+ 2 =|ŷ}oj։m\#tQ( `E7[ƒ5fU$#ۯz.Lxy~4_K;ި.;<S&h@aa^h5vup#c}ONY?$]Oty45ѓF ib$Ej+ zݔ@?~@rќVlzB 2Z׺r  c"n}Vĥ*9׺oɗ XU}ORVvL{i˫v\'C7%ozg&j^r}!aܿs}Hb2 ّu7}W<ċ_WF s@,ۼw~h7K "gMQPD"P0^Pr&LX/ Tl_Sڰl{z! yAZ 5ƒ a1(xi s ;CPּcP4< {EE@s53e JxsG6\-̀RU7 ;g7i{FT&n5'V_}kWl1R8sIibDc2Vo68K~W3sfWq巻G^?q;s%m)jDѕ'\bck612e곐'x~2 3s{|l"%H` l<22aLچNXX@9p쿞jyy%0A<?O;a%/D.t>mߒ*NOy3׾_ˆm_}mqI;mr4e;G!#rw'N0DKvoډv%*BۈG'lc g^{3#RZ,9ߏ Ԩ' AD Z截& 1N!Y@ H4Ҭy-EMkdld#TtvdX{J I% bD0q(,)Y8TG|Gh-<kZW%ѩۯ49Ӕ,jFkEa1b [2T=_XwZG7^GBeK53B,Џ(>($}UPY`$UΔscQeaaiI!J eT@d1 x6j $GqLLA#I]q N!dC6xR' AȲn&)"Y+JDE & 5 ,J9춐!tâG|kOG]9|fmgisoS_kTŴFǶ?^@tQE-#ٴ=s7pc-/T}lBB9LCA0K.x:@tJ0A"L Ad  6Z_ދK?*y-EI=(-;>k&w@y+,HaB=9Xk5A!*DEե,JI΋41e&Bl<t߾o~F-ǩfmac3Ƣ1DJ2")[eeZ+Ey/^}6[E 7!E5P=1$by"$g%Y"eA,cDAxkTT"G!#~tG5#gN ԇ׆ 3d#CDD"h"cGF'n^.UAd'Nےeu fMW\;$h|ϜY9NEUq{[m3ϕ%nfjdde3\sXkEpMh>\ w^%{Ea'CcF0ԴZ i*Rg')6ZkX㕧$_@LVE5e7>i'q3DOKD&؄1OU.SUU٫5~G*K  i$&&"d @8;90uX5+WT"ԕ YKd8m= Z^6'֮&\XgTa96Zv*: k1 z fyrS/}Ez **pGu"KAZ2o YN%s߱!v\.sLj& Pa`uӪ)9oMBE8^\T0+@VpN8CFD54DIY1&("c= ɹDy` j̕>Ŋ^]d.̳?Vlj09~0fW>ϑ9koRɀ>" Qs=2$ȥH^uy?s1kH؁r^R LGa""q&";vY|!H "XRBX, 2d"1hĉs"/]*9CA"c,mZ!̹Nw֏tEQ\ʜ:?`"!YMP^ើ[ՂLԌ5GӜu!,3*] Jгa'>rإj\&<"xe(9뷏$;d"~K灿D"֏*7cc?(>.=4Z^{O~}l?EQ.7=9 @aVcI Iv:iw4i;KsƲCUapš0ַSrCǜ25[&:̩ro`]GP6C{yOWJo ձ\\7JrރXT<. ?A**JyNSzѢBp`XKZמ9ɵn_YD,?êiU`^flyd) )0 9v.:Z*$b#V2}mSQڞ}擿9l;PPg QO|S{(-$T;;0k$1X%2޻d\`mE-}p>Ov#Q0cVk,K U+0⼆JٷwģxA@$S8LyBIn 4|"i I+VE؉ ++K[PJncE8LbU  n_ 'TNy}͍U_|K/|p*dαWȚƊ6jz4䙘-f_zFvO<~%$$9ڰ;q?TvlL`F8nDԴ&MPؒ%0ܜA= X=KȈP#YqiXbc=wTt v]c,qYRBeR=GEЀ]/Zd':8JX:뉹{7Kn;E9Bh+>/Uv.s9bk|Țd( 5fh00oZ(?zD>kϜ>=v0b4VUX9o~) gDANV)v  DQQlleKU4գ{}ֵCUDxW^h1q܊lѰJ=lM,N"ccMC)ɯ61bÇJ.G=C*.Ec'7]~uwjv:>[E$4dGƑ羙LU`f{D0gNyo]6Fs@bՂnSf&'Og?qF xR(0:s9%'lJ-H=(PP+UvEH3nz:Bι%Ҿmn9_VT JllLU hXTO_\@4$1^z6x#ʠMV%SQE_U4ѵW_vӪKo߰6Lk@UZrTȲ%dk[ӯuf$[QbOٕ3Vmab˕Qs4PB ֲc ++:VD[̨N94hWRG*GL,jҊ+q֔rFC- h=E ˍµ.wŠ)!J.-(QiՒ8VNmʒ9dp2.ua"@"^);4`ђC(~vqMDfTh1s9״K5mcŖ jCzAUg5Pcxzt./ꀹ)#VY^,S@,ZPƲ+QF $6(Q.hE;M#j+ԕe0?*E"\OP+`R,=UYVXq8Ds: )%*gʦԏKXqVZ$$DP**d #/ԻPkc5BD$[^瀖eB,S8|יPbuCuJ4w+t R 9O*[#&HE.ؙ$/oD1!5,XF|KYqZ;UAg⥺>;Н&y*qTUf΁p,7Xw"d_H֝Ǡٝ5k>ӇvOioQBD~9:)ANԷɘ{9kC#+֮v͗ O=\m#- KD\;<{ݙFHd+q|Q Zy]|QQl-߫ʠ`īϿL1&q֛޺8Kx>ǘ͉Z]WS2 p>0 Z@[)Us-JUY%>/+GN*m7s;~(1c'zEjr_mXuW{YvL>˦.]gvsvԱKDىVly/.;F͓^|kQΒ[g h#/=ޝ>^TP,p]7?I۳Ǝ]ĥ/ڡR ͿbM|9A^Om)ʂM:/*P@D ;OK/X HP "HooQ4l֙Nz~ϭ!KWn=}¿Q:?37{#…ju?_~Alslbk7ĦKLu.{Ooi{L4qɶy T]w~opm>yWJ_3("U%qCE8K'7^ZK:ÈH첵E=4Uh1;gإc D2O7jmXV؎HeϠ\H+{*Rb(XlGV7Flz)Haǜr#}OLd!%{bCkm5A^9e˺@*bDENMID\WBU.Z Գ,1Wwoظ!̜^fǵG_z 8|u}d֙Cԩl2ΒWܼ<qIc EQ̫}fuwFˋ F%#k66i{~5N}JA/}wDַ}o9KUj*Eb@/\ZwM.(8j a@mTÌm4GMdY`;k3d'O^w-u7\sתmWZMuW".P-k0׫CB dm!HT{9bܼ QG"**g5$2d"A da6LAT\YCA1jhShn(QY01FD]'=E_޳u kV\6n[~MUO=6nJ>]%zȑ[㫷]u'q`.g#m~ձG^|{wǔo+Y,UGqUۯZcToKjL).>?tݛmkT9KWn1?3oE;"p5F' ',x"* Oϛܢ(ewlq֗s|;=^ڏNLZoca,Yc@o!z(aKP0'9IT@(ђv3!aЂ&!l< {hTT9T2"K@ 쀀wd"50@3sxûោ~ptɍŹD+."SvUAޚ ]Y'^{7ĪKPv;'mͺ{g4~d,B @,:i{VFk| oG' 0}쀲_,@{b 鯯q &o~_{s i4G"#c褉z9FT00 5 "d !XKuҥ%x6*:vsRh#g3NhI8@TUF~;?C~dz5{(B&i~#ye7~<=WͩOrB '{@=54K7^ךWv{Gͫ_:ژXإW{B} #*L\wF,܉=ψJw@a 7V4&ք]dn_}%"P xc_0q^ND; VUȺɶ:~[H_ʕ+'^NmRP/=ʠ,55q7`(,aBRK8`"(D䵠LN:rqK^w%rbv#"Tx>cPfQ[a!{Qy|3}nOf(n/ODEr|s_+P6&cQv ox/Ο:JTǚ#-z[lK;n{֛* _Lnر;_~XOA=X4pv[+<}xxJpw#SlQ\{'.Y:g(& bhkO=/չS v,6ƒAձxeo)x}H&%.xikc/=q&IZ,O R4ܲx2 A9Ç"~| _@D0!CXE@6Krz.[pQPgDQ.5svlznk/~~j~q%st.6b@vAR,|:b_hcCv7;w?3(Zn}i|`={,5YuL6Z{gc+V>f獾X_[_T ? /R*.K_}m= wql,|eѵfo+אT(MNyZ8ӬZQ(yfv)(=@m&F,s8B+uḊ|W>&y͎xɒQw?|(m3ⲉK~{hĥ8Zg;q./1^(ty&g2ߞ;ޚ\{gRod#>%KVƪUi4F-g>;o_8xw~Bc bveeqY2fګn w({OiX3v+-7̧v?Dczm=/Ւb}Q ,M@PTe"ŁTyY-+;ho@Uzܡ*(^:K}?A~h7|S@dTxGA4/GlAc#7=~_,5*x9Vt.dzk?w u Qv%ny _#{B4i#VӢ1(("GWiMo4Ut@D:s?Gׯ@5ǚimGFW#/>\:ASЙ:B6G$0W;1*i1ˮ5'&j>2fpp{ qٖ[޶Oy7hI 9 (~1Ϛ&(" yQbJc>T#BEV"> #Vo'بS!@wݪToC~D&hl͝8_? EkHEly>׊(^}K`M7mŭٜ9#d#@_yRwZ4ZUy(?YfuosX,HZ(ʒ,Jl˝Vn8FZ8N'@< $@ Hہ8m[mYj(JTXto~9{ݒcD]9{QKᑅӵG??n_Pr@ںv?];~|ŏv6Xlo/Zy NγTPLd3/}3g/ݻ7]޹1;W=״h :d)騝N,AoF; eGOt^{{h,,vnn{6 wl~@L_ש1[|ogbމ>w6t79E6!Ipcq|< 14.(sEϧq&=*uja߲k{3U 3$lGE8ґ ",!TldL1rJQ)W Q;׶$ J4:ۇ16̎2;Lq GA~e{fT28fB,Mx m",|7hȘH bks#;0WU6d:s#z[n]= aH H읥@ ⧦9FāgFτL/{S#!< Hu%1("}T} $#Oү;q_Y)qJ&)C JW@ BF=>ј 2UV9 BuaףVA߰Hz5$0zi ;eI%Am+21V+VLJ@^Їt)H^5fѳ0yE8w(*T.eڌN {Oh&D*DB6 y]A((ɒw,I]rP 1@Ako|b"b!s$!FUdE =6 I91 R yXplH8vb=3Ib !w޷|OLKqs 1{/Wq*I<@h{bZdַ"ҶS";JS2=$QEcz@i,.A)# C6hS)b^ ^Bp0U )1a8N 2n'jTOK>#;Ȍt/DQ@|K"W)إ`6q&3?Yf%76rP`m$aJ5a&KRpG2 pTK6Cc"de[!$ DVMEi߶΃R~^%y-~Z((`Mߒi O"3pO=Dd,e ^8H1"Y*$"!"$2#Ph/  /e%-R(O7" TJbVKij?%AP(dPj!zkv ,%W噬d(kh;3A0a2fTŪ ~5E޳+" ~X[02 z'ȒS!Dw޳xc}.-ʁ,A%^KK]ih#\;3sxb[ tf`A/@b Jh@@}d ր@$ ^XcZ 3"Ǒba"]#(*IΒBW(7ӉZq:e꣈G|a`]blK #S,oѦSH8W KB@B%Jaz m'zS[92{B Ƿ| ^`LWA1>kle4ѳg n܂2uQ-z=3{g~ǩm;hȈ3#@1wwR+MZ6A)f@1+,yRKh5 *b};B֨b EoC;sҮO^:YB`$qAblYrB' iEl"(XψHxQGƀ0U%cċck yQi}ZPe;U0d f2 ж1iQaaJD(GBbegtQPCN"np!9xRE9$Sa$2G7FS @DD8${y8BXF!D!P$n%* qzL蛔A(2 PAv=QcMQab~>Le!kb aىݹMK!(=!UN%+%]61fD$[ 0M9dH4DxG&c4ÔX,4^)ľe&2T8 1[k{o^Ug݄̈́3)Y Xfz S 2>p23޵-EP|+ޣ1aHJvJFZh*km "r P4LMZD«T o-o nhN { ^ ܖacAd{ {A`@q\ B Q (A *wkI)>(@UHS#Uuur،[TX#7Б%^!kQm ~(2fuK7[#cEOMG7ީK>pY? 2֍wvngd/}T\?__:@L/{x| jᵇ#OvvI¶^8ܼloϿp"ю]X8o ?5]_vhs.ꩵ_~w@ċ_}JC2xwl-/§:8n_d6-W#w{q&Wwo9Fc@ĻvWۗM=<ת# "h+;\9ʅg[? :$#ɣԋǎzoT}GG,_Hf,?fD@Syfr#h#fKg_M7>މDZ#6on_;˵ef#L}Г4Ux @SzK>m[Ckz*W}߿.xKjlr% غʥo7Ǟ`W&;K?^>i7gnϼV۷~xٶ֞^6[`4±.,\-;Wts/w?޿^\{7Wjsns7EቋӣW'7'&o1q!hc/ڧ,į,ľ%cW~ōv7n]w-`pdM_<rr$ Ԥt\y\-93?9ڷUn'+_Aу+G jqZ2 >Joof;/|_'?U-<:޸9^1ݾ'_o{w?|~oG/xlZ@A4Ⱦm7}G7فҍL[:?јH*&׿ o޸1]7d};`7۹f+m_1}[W~dzaxqxL7~[s8dȴ7rL.3YYniUݯ)S?78 3ňTgZHq7 m3nQf`VD`J> Yuzz?Ҳf3#SWff#_ nɤ4`TF2"cQ浟NvDzfKDoYr-EU)|9` \3K'fQT}_xw7]c{ Y0U]3n6Zyf7X}+iօ !)cݢ5 dT >Ր[/n}LO?'|ruИzUdO1hgBRL_4ڸ{W.oU"T"P}P@5"w-K %`w̭K{7p 9 kB1kcP&@cm]Ql_'UYi)1zIYDyfzstj1X=_ff<]CmܺDfaS .ʹY84ugd?A;#cl|;xNG.~n~+-goS_7OI(gz`e"Z;y oHiCYM'MDDch9),L8n^~|M57Ag|;{lF;hm;ab k|0/DB2㭻׾tW~tckN` M=ۺ8_/;7/7WNO|oO~U_4u1db%EAG{+h0"h4(Ju5&;Vi HэӟYyS\{kpV؞?kבo&e$-K "s*]e s;ٿW{''oGyq/vo^޻K54 @r?g{vH&k|ߓtm2Dxw`Ȭ=s+.7vs]4V\ky5D~*s0 )@0pqGS A=9o(05VO~geK3pѽ~dv<޸x;7=4[-n9x3r\>A2㽫? -d,}X/`eŵ:)7H[slx3X ƻwvۖ n/o?K n!$U"+[Ւ6^'2VE@h:Uu$+nKg 62]V#J`)-m&Z3NGιdv _9.,GT+X݇Ʉ4ofgOjjkvRx kcoܪpuO뀴xdk&t 57;gLUWұfoky?oعڙclw C"8X;f#7Srɋ%=,鯞vSr4] "4^>9}(1JX5$쭣Xhsy^9[:MW;ڜG$p4{+'Ѷo;]_;NdGXV~zq;nwB.5x f.C 2{VNپJZ:{n6@aoVcQv) @譜ngf`6zry]!^E*MS([_ ,Ϯz7ӂ_>r4Hs ;/`=X dfV:TEvlehM'_/S(UoQyq۠0A" [" jbRVj-i^ hl,+YؑB~d*"d~=aePj;faHdv*'2I˾E"| Y97A"pe2E/^00r($T:-{oCkEA0@L cIOYU*+fu$0=OI]|h.`3z#/8gE4n0#% G1̨csSVų_=UG n5 "_BZ*.bxK~lzȂCaZ|J]83 @m\{sI-<樒Ax]-,AbF !3"F ל="SrMijF zGUU_ѽpgV6Z$pHHޤ%7FxAWQ؇z!B 'lڪeNI³n1+;V4Atqě"$zS:"o,{Hޕ"^:0`0*s( ' Fhqz<ׄrQ10@Ep A57,S& ^! Xb " aeq"EK"FۥeLڬF @4}J$rP"t&^B!O J! (@XVߑ#@hDzػ6h(rhȄ.Lѡ$[*2 B ʼ;Z#tY*b$G<" !) _/.nÖg{1{NUO1*$ :}=ډ-jM6 c]a(.! ߜg061KȜRҁO%K3DqGb-~ИjB3xf K\ȺhG7tk3gZL>D$H l4`jNYP Kג v([MpVWΕ*E5dY-Dlv-bE%QWp:N;"uݴ娪@(\O (X٪"xvM]#!B:L v‰۞̬1VʺH:my"etŋ>BR%fK XR6M8m -EGu,  J(*%82U|<8^P/mYأ#aOɩ1 sOR]O'q]~0gpfKvg]zKPS+$l`*T6%l-sNV+P"S0i Tb s "YETx[`$*Ol2섕¯edn3y\~@-BIV cQIAJz'qbPC8Ex,dTd][' %$zDW̱͈Z K oU2+fЧ#SX<U6a^9"H)EB37(fBUL y]$* %&B6b@]󯌒.4<w:EQITI:hdIR(޵FIAbQR,dL-9@D`@,BJ`% Sd⓴-8Ú{?$<%3;SZBTH0!IcM;s4I( +%*=2/\I7T [ zZ[kG~&"ƪG< pi.Kژx}9\?GdjBE3>QI4构v)%ap3n]I2daG PRW69"H8GN`cvq5=WJri`p8-ZBݜ_PVRϊ4GT8Pi{F@mԠB/~"s3v@z_?܎ʦ# l1TpL̗\E'9+ԾSIw*.,(r:].7Րہ[b 2T `.$hF['F8%*u%OShc; CN ;* ČE`PzU!]S dDxulWjaBSec,#LSdHBk!uja!#HfBMx( G$CQHqC HeDMC`BVaRX'bRhMXcOD-O@[b 0,D yMG\?AAqb BB'"8hXDB yIcxdDDJ`5,W8`Si_)A'Em3'6̅7dS"4;Ɔ w!kdN-8Ю34aY:AI>QONW# p4(^v%|Q` r5?Zm,M "1œD@Z; z~,0 F'QhJ@¡RݣB` 1C@nЎ5#F )= JЕ35ξ!O DRC!^R\98M4Yo]S)0ʣx%xU l f BVd-C9}PJ,G(#$7UTbwVl1>3rƴn4VR8$`Lֶv{^(&M1XW^dDN6X<:da zv;1f)M|sQHcO|zFKTz+t5t dPYpt5 GP' *%i ܣ osvb5mB!0w.-wʃSXez!itus3<1b[.n$OU;ƣJZ{@DӳX9,\@\t]rBa(~&}.?1c;?A},Y;jƌ.i;ocC鞆WRS?"⋓P viYU)Hh9ek2ΥKh$|8P,yI\FB9:/*w8Y7AL'dR:b¨60Mn6iK!M r!1#w!L< X¥A B] tlIENDB`pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/000077500000000000000000000000001346756700600264645ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/genre_translation.xml000066400000000000000000000243141346756700600327300ustar00rootroot00000000000000 movie/drama (general) detective/thriller adventure/western/war science fiction/fantasy/horror comedy soap/melodram/folkloric romance serious/classical/religious/historical movie/drama adult movie/drama news/current affairs (general) news/weather report news magazine documentary discussion/interview/debate show/game show (general) game show/quiz/contest variety show talk show sports (general) special events sports magazine football/soccer tennis/squash team sports athletics motor sport water sport winter sport equestrian martial sports childrens's/youth program (general) pre-school children's program entertainment (6-14 year old) entertainment (10-16 year old) information/education/school program cartoon/puppets music/ballet/dance (general) rock/pop serious music/classic music folk/traditional music jazz musical/opera ballet arts/culture (without music, general) performing arts fine arts religion popular culture/traditional arts literature film/cinema experimental film/video broadcasting/press new media arts/culture magazine fashion social/political issues/economics (general) magazines/reports/documentary education/science/factual topics (general) nature/animals/environment technology/natural science medicine/physiology/psychology foreign countries/expeditions social/spiritual science further education languages leisure hobbies (general) tourism/travel handicraft motoring fitness & health cooking advertisement/shopping gardening original language black & white unpublished live broadcast pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/000077500000000000000000000000001346756700600302475ustar00rootroot00000000000000resource.language.af_za/000077500000000000000000000000001346756700600346605ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000143131346756700600367130ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.af_za# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Afrikaans (South Africa) (http://www.transifex.com/projects/p/kodi-main/language/af_ZA/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: af_ZA\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Gasheernaam" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi byvoegsel Poort" msgctxt "#30002" msgid "Free-to-air only" msgstr "Vry-op-lug alleenlik" msgctxt "#30003" msgid "Include Radio" msgstr "Sluit Radio in" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Vinnige kanaal oorskakeling (moenie tydskuif stop)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Konnekteer tydverstreke(s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Voer slegs TV Kanale van groep in" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Voer slegs Radio Kanale van groep in" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Skakel gasheernaam om na IP-adres" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Lees genre stringe (stadig)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Wag tyd na kanaal ingestem het (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Stel ou reeks opneem dialoog in staat" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Verstek opname leeftyd" msgctxt "#30015" msgid "Streaming method" msgstr "Stroom metode" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows gebruiker rekening (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows wagwoord (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Gebruik RTSP stroming" msgctxt "#30040" msgid "Connection" msgstr "Konneksie" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Terugspeel" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Jou TVServerKodi weergawe '%s' is te oud. Opgradeer asb na '%s' of hoër!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Jou TVServerKodi weergawe is te oud. Opgradeer asb na '%s' of hoër!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Opname terugspeel het gefaal. Leë URL of lêernaam." msgctxt "#30060" msgid "All cards are busy" msgstr "Alle kaarte is besig" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanaal is vermeng" msgctxt "#30062" msgid "No video or audio detected" msgstr "Geen video of oudio bespeur" msgctxt "#30063" msgid "No signal detected" msgstr "Geen sein bespeur" msgctxt "#30064" msgid "Unknown error" msgstr "Onbekende fout" msgctxt "#30065" msgid "Unable to start graph" msgstr "Kan nie grafiek begin nie" msgctxt "#30066" msgid "Unknown channel" msgstr "Onbekende kanaal" msgctxt "#30067" msgid "No tuning details" msgstr "Geen instem besonderhede" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanaal is nie aan enige kaart toegeken" msgctxt "#30069" msgid "Card is disabled" msgstr "Kaart is gedeaktiveer" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Konneksie aan slaaf het gefaal" msgctxt "#30071" msgid "Not the owner" msgstr "Nie die eienaar" msgctxt "#30072" msgid "Graph building failed" msgstr "Grafiek bou het gefaal" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW Enkodeerder afwesig" msgctxt "#30074" msgid "No free disk space" msgstr "Geen oop skyf spasie" msgctxt "#30075" msgid "No PMT found" msgstr "Geen PMT gevind" msgctxt "#30100" msgid "Schedule settings" msgstr "Skedule verstellings" msgctxt "#30101" msgid "Frequency" msgstr "Frekwensie" msgctxt "#30102" msgid "Airtime" msgstr "Lugtyd" msgctxt "#30103" msgid "Channels" msgstr "Kanale" msgctxt "#30104" msgid "Keep" msgstr "Hou" msgctxt "#30105" msgid "Record minutes before start" msgstr "Neem minute op voor begin" msgctxt "#30106" msgid "Record minutes after end" msgstr "Neem minute op na einde" msgctxt "#30110" msgid "Record Once" msgstr "Neem een maal op" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Neem daagliks op (Hierdie program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Neem weekliks op" msgctxt "#30113" msgid "Record Weekends" msgstr "Neem naweke op" msgctxt "#30114" msgid "Record Weekdays" msgstr "Neem Weeksdae op" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Neem elke slag op hierdie kanaal op" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Neem elke slag op elke kanaal op" msgctxt "#30117" msgid "Record every week at this time" msgstr "Neem elke week op hierdie tyd op" msgctxt "#30118" msgid "Record every day at this time" msgstr "Neem elke dag op hierdie tyd op" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Neem weekliks op hierdie kanaal op" msgctxt "#30120" msgid "This time" msgstr "Hierdie keer" msgctxt "#30121" msgid "Anytime" msgstr "Enige tyd" msgctxt "#30122" msgid "Manual" msgstr "Handrolies" msgctxt "#30125" msgid "This Channel" msgstr "Hierdie kanaal" msgctxt "#30126" msgid "Any Channel" msgstr "Enige kanaal" msgctxt "#30130" msgid "Until space needed" msgstr "Tot spasie benodig" msgctxt "#30131" msgid "Until watched" msgstr "Tot gekyk" msgctxt "#30132" msgid "Days" msgstr "Dae" msgctxt "#30133" msgid "Always" msgstr "Altyd" msgctxt "#30134" msgid "1 week" msgstr "1 week" msgctxt "#30135" msgid "Backend default" msgstr "Agterkant bestek" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi bestek" msgctxt "#30137" msgid "%d weeks" msgstr "%d weke" msgctxt "#30138" msgid "1 month" msgstr "1 maand" msgctxt "#30139" msgid "%d months" msgstr "%d maande" msgctxt "#30140" msgid "1 year" msgstr "1 jaar" resource.language.am_et/000077500000000000000000000000001346756700600346655ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000042601346756700600367200ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.am_et# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Amharic (Ethiopia) (http://www.transifex.com/projects/p/kodi-main/language/am_ET/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: am_ET\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgctxt "#30003" msgid "Include Radio" msgstr "ሬዲዮ ማካተቻ" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "ግንኙነት ጊዜው አልፏል (ሰ)" msgctxt "#30040" msgid "Connection" msgstr "ግንኙነት " msgctxt "#30042" msgid "Playback" msgstr "መልሶ ማጫወቻ" msgctxt "#30060" msgid "All cards are busy" msgstr "ሁሉም ካርዶች በ ስራ ላይ ናቸው" msgctxt "#30062" msgid "No video or audio detected" msgstr "ምንም አይነት ቪዲዮ ወይንም ድምፅ አልተገኘም " msgctxt "#30063" msgid "No signal detected" msgstr "ምንም አይነት ምልክት አልተገኘም " msgctxt "#30064" msgid "Unknown error" msgstr "ያልታወቀ ስህተት " msgctxt "#30066" msgid "Unknown channel" msgstr "ያልታወቀ ጣቢያ " msgctxt "#30071" msgid "Not the owner" msgstr "ባለቤት አይደሉም " msgctxt "#30074" msgid "No free disk space" msgstr "ባዶ የዲስክ ቦታ የለም " msgctxt "#30103" msgid "Channels" msgstr "ጣቢያዎች" msgctxt "#30104" msgid "Keep" msgstr "ማስቀመጫ" msgctxt "#30121" msgid "Anytime" msgstr "በማንኛውም ጊዜ" msgctxt "#30122" msgid "Manual" msgstr "በእጅ " msgctxt "#30126" msgid "Any Channel" msgstr "ማንኛውም ጣቢያ" msgctxt "#30132" msgid "Days" msgstr "ቀኖች " msgctxt "#30133" msgid "Always" msgstr "ሁልጊዜ" msgctxt "#30134" msgid "1 week" msgstr "1 ሳምንት" msgctxt "#30138" msgid "1 month" msgstr "1 ወር" msgctxt "#30140" msgid "1 year" msgstr "1 አመት" resource.language.ar_sa/000077500000000000000000000000001346756700600346655ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000022571346756700600367240ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.ar_sa# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Arabic (Saudi Arabia) (http://www.transifex.com/projects/p/kodi-main/language/ar_SA/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ar_SA\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "مهلة(ات) الاتصال" msgctxt "#30012" msgid "Default recording lifetime" msgstr "عمر التسجيل الافتراضي" msgctxt "#30042" msgid "Playback" msgstr "توليف" msgctxt "#30103" msgid "Channels" msgstr "محطات" msgctxt "#30122" msgid "Manual" msgstr "يدوي" msgctxt "#30132" msgid "Days" msgstr "أيام" msgctxt "#30133" msgid "Always" msgstr "دائما" resource.language.az_az/000077500000000000000000000000001346756700600347045ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000023331346756700600367360ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.az_az# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Azerbaijani (Azerbaijan) (http://www.transifex.com/projects/p/kodi-main/language/az_AZ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "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ü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" resource.language.be_by/000077500000000000000000000000001346756700600346605ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000105361346756700600367160ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.be_by# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Belarusian (Belarus) (http://www.transifex.com/projects/p/kodi-main/language/be_BY/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: be_BY\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostname" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi plugin Port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Free-to-air only" msgctxt "#30003" msgid "Include Radio" msgstr "Include Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Fast channel switching (don't stop timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Connect timeout (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Import only TV Channels from group" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Import only Radio Channels from group" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convert hostname to IP-adress" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Read genre strings (slow)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Wait time after tuning a channel (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Стандартны час захоўваньня запісу" msgctxt "#30015" msgid "Streaming method" msgstr "Streaming method" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows user account (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows password (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Use RTSP streaming" msgctxt "#30040" msgid "Connection" msgstr "Connection" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Playback" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Recording playback failed. Empty URL of filename." msgctxt "#30060" msgid "All cards are busy" msgstr "All cards are busy" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Channel is scrambled" msgctxt "#30062" msgid "No video or audio detected" msgstr "No video or audio detected" msgctxt "#30063" msgid "No signal detected" msgstr "No signal detected" msgctxt "#30064" msgid "Unknown error" msgstr "Unknown error" msgctxt "#30065" msgid "Unable to start graph" msgstr "Unable to start graph" msgctxt "#30066" msgid "Unknown channel" msgstr "Невядомы канал" msgctxt "#30067" msgid "No tuning details" msgstr "No tuning details" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Channel is not mapped to any card" msgctxt "#30069" msgid "Card is disabled" msgstr "Card is disabled" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Connection to slave failed" msgctxt "#30071" msgid "Not the owner" msgstr "Not the owner" msgctxt "#30072" msgid "Graph building failed" msgstr "Graph building failed" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW Encoder missing" msgctxt "#30074" msgid "No free disk space" msgstr "No free disk space" msgctxt "#30075" msgid "No PMT found" msgstr "No PMT found" msgctxt "#30101" msgid "Frequency" msgstr "Frequency" msgctxt "#30102" msgid "Airtime" msgstr "Airtime" msgctxt "#30103" msgid "Channels" msgstr "Channels" msgctxt "#30104" msgid "Keep" msgstr "Keep" msgctxt "#30122" msgid "Manual" msgstr "Ручны" msgctxt "#30132" msgid "Days" msgstr "Дзён" msgctxt "#30133" msgid "Always" msgstr "Заўжды" resource.language.bg_bg/000077500000000000000000000000001346756700600346405ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000202411346756700600366700ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.bg_bg# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Bulgarian (Bulgaria) (http://www.transifex.com/projects/p/kodi-main/language/bg_BG/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: bg_BG\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Име на хоста на Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Порт за добавката на Kodi за Mediaportal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Само безплатните канали" msgctxt "#30003" msgid "Include Radio" msgstr "Включване и на радиата" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Бързо превключване на канали (без спиране на изместването във времето)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Време на изчакване при свързване (сек)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Внасяне само на ТВ каналите от групата" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Внасяне само на радио каналите от групата" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Превръщане на името на хоста в IP адрес" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Справочник: получаване на имената на жанровете (бавно)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Време за изчакване след настройка на канал (мсек)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Прозорец за запис на стари сериали" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Стандартен срок за съхранение на записите" msgctxt "#30015" msgid "Streaming method" msgstr "Метод на поточно излъчване" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Потребител на Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Парола за Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Ползване на потоци RTSP" msgctxt "#30040" msgid "Connection" msgstr "Връзка" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Възпроизвеждане" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Версията на добавката „TVServerKodi“ – „%s“, е твърде стара. Моля, обновете я до „%s“ или по-нова!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Версията на добавката „TVServerKodi“ е твърде стара. Моля, обновете я до „%s“ или по-нова!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Записът не може да бъде възпроизведен: празна връзки или име на файл." msgctxt "#30060" msgid "All cards are busy" msgstr "Всички карти са заети" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Каналът е кодиран" msgctxt "#30062" msgid "No video or audio detected" msgstr "Няма открит видео или аудио поток" msgctxt "#30063" msgid "No signal detected" msgstr "Няма сигнал" msgctxt "#30064" msgid "Unknown error" msgstr "Неизвестна грешка" msgctxt "#30065" msgid "Unable to start graph" msgstr "Неуспешно стартиране на графиката" msgctxt "#30066" msgid "Unknown channel" msgstr "Неизвестен канал" msgctxt "#30067" msgid "No tuning details" msgstr "Няма данни за настройка" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Каналът не е свързан към нито една карта" msgctxt "#30069" msgid "Card is disabled" msgstr "Картата е изключена" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Неуспешно свързване с подчиненото устройство" msgctxt "#30071" msgid "Not the owner" msgstr "Не е собственик" msgctxt "#30072" msgid "Graph building failed" msgstr "Неуспешно изграждане на графиката" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Липсва програмен шифратор" msgctxt "#30074" msgid "No free disk space" msgstr "Няма свободно място на диска" msgctxt "#30075" msgid "No PMT found" msgstr "Липсва таблица за свързване на каналите" msgctxt "#30100" msgid "Schedule settings" msgstr "Настройки на графика" msgctxt "#30101" msgid "Frequency" msgstr "Честота" msgctxt "#30102" msgid "Airtime" msgstr "Време на излъчване" msgctxt "#30103" msgid "Channels" msgstr "Канали" msgctxt "#30104" 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 "Всеки път" msgctxt "#30122" msgid "Manual" msgstr "Ръчно" msgctxt "#30125" msgid "This Channel" msgstr "Този канал" msgctxt "#30126" msgid "Any Channel" msgstr "Всеки канал" msgctxt "#30130" msgid "Until space needed" msgstr "Докато няма недостиг на място" msgctxt "#30131" msgid "Until watched" msgstr "Докато не бъде изгледано" msgctxt "#30132" msgid "Days" msgstr "Дни" msgctxt "#30133" msgid "Always" msgstr "Винаги" msgctxt "#30134" msgid "1 week" msgstr "1 седмица" msgctxt "#30135" msgid "Backend default" msgstr "По подразбиране за сървъра" 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 година" resource.language.bs_ba/000077500000000000000000000000001346756700600346465ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000017511346756700600367030ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.bs_ba# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Bosnian (Bosnia and Herzegovina) (http://www.transifex.com/projects/p/kodi-main/language/bs_BA/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: bs_BA\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgctxt "#30042" msgid "Playback" msgstr "Reprodukcija" msgctxt "#30103" msgid "Channels" msgstr "Kanali" msgctxt "#30122" msgid "Manual" msgstr "Ručno" msgctxt "#30132" msgid "Days" msgstr "dana" msgctxt "#30133" msgid "Always" msgstr "Uvijek" resource.language.ca_es/000077500000000000000000000000001346756700600346525ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000152441346756700600367110ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.ca_es# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Catalan (Spain) (http://www.transifex.com/projects/p/kodi-main/language/ca_ES/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ca_ES\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nom d'amfitrió de MediaPortal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Port del connector MediaPortal de Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Només emissions lliures" msgctxt "#30003" msgid "Include Radio" msgstr "Inclou la ràdio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Canvi de canal ràpid (no aturis els salts en el temps)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Temps d'expiració de la connexió (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importa només canals de TV del grup" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importa només canals de ràdio del grup" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Converteix el nom d'amfitrió a adreça IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Llegeix cadenes de gènere (lent)" msgctxt "#30010" 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 "#30012" msgid "Default recording lifetime" msgstr "Durada predeterminada de l'enregistrament" msgctxt "#30015" msgid "Streaming method" msgstr "Mètode de transmissió en línia" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Compte d'usuari de Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Contrasenya de Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Utilitza la transmissió en línia RTSP" msgctxt "#30040" msgid "Connection" msgstr "Connexió" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Reproducció" 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. 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. Si us plau, actualitzeu a '%s' o superior!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Ha fallat la reproducció de l'enregistrament. L'URL del fitxer és buida." msgctxt "#30060" msgid "All cards are busy" msgstr "Totes les targetes estan ocupades" msgctxt "#30061" msgid "Channel is scrambled" msgstr "El canal és codificat" msgctxt "#30062" msgid "No video or audio detected" msgstr "No s'ha detectat cap vídeo o àudio" msgctxt "#30063" msgid "No signal detected" msgstr "No s'ha detectat senyal" msgctxt "#30064" msgid "Unknown error" msgstr "Error desconegut" msgctxt "#30065" msgid "Unable to start graph" msgstr "No s'ha pogut iniciar el gràfic" msgctxt "#30066" msgid "Unknown channel" msgstr "Canal desconegut" msgctxt "#30067" msgid "No tuning details" msgstr "Cap detall de sintonització" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "El canal no està assignat a cap targeta" msgctxt "#30069" msgid "Card is disabled" msgstr "La targeta està inhabilitada" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Ha fallat la connexió a l'esclau" msgctxt "#30071" msgid "Not the owner" msgstr "No és el propietari" msgctxt "#30072" msgid "Graph building failed" msgstr "Ha fallat la construcció del gràfic" msgctxt "#30073" msgid "SW Encoder missing" msgstr "No es troba el codificador SW " msgctxt "#30074" msgid "No free disk space" msgstr "No queda espai de disc" msgctxt "#30075" msgid "No PMT found" msgstr "No es troba el PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "Opcions de les programacions" msgctxt "#30101" msgid "Frequency" msgstr "Freqüència" msgctxt "#30102" msgid "Airtime" msgstr "Emissió" msgctxt "#30103" msgid "Channels" msgstr "Canals" msgctxt "#30104" msgid "Keep" msgstr "Mantenir" msgctxt "#30105" msgid "Record minutes before start" msgstr "Minuts enregistrats abans de començar" msgctxt "#30106" msgid "Record minutes after end" msgstr "Minuts enregistrats després de finalitzar" msgctxt "#30110" msgid "Record Once" msgstr "Enregistra un cop" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Enregistra diàriament (aquest programa)" msgctxt "#30112" msgid "Record Weekly" msgstr "Enregistra setmanalment" msgctxt "#30113" msgid "Record Weekends" 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" msgstr "Aquest cop" msgctxt "#30121" msgid "Anytime" msgstr "En qualsevol moment" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "Aquest canal" msgctxt "#30126" msgid "Any Channel" msgstr "Qualsevol canal" msgctxt "#30130" msgid "Until space needed" msgstr "Fins que necessiti espai" msgctxt "#30131" msgid "Until watched" msgstr "Fins vist" msgctxt "#30132" msgid "Days" msgstr "Dies" msgctxt "#30133" msgid "Always" msgstr "Sempre" msgctxt "#30134" msgid "1 week" msgstr "1 setmana" msgctxt "#30135" msgid "Backend default" 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" resource.language.cs_cz/000077500000000000000000000000001346756700600347015ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000152421346756700600367360ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.cs_cz# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/kodi-main/language/cs_CZ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: cs_CZ\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Název hostitele Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Port doplňku Kodi Mediaportal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Pouze nekódované" msgctxt "#30003" msgid "Include Radio" msgstr "Zahrnout rádio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Rychlé přepínání kanálů (nezastavovat časový posun)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Časový limit spojení (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importovat pouze televizní kanály ze skupiny" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importovat pouze rozhlasové stanice ze skupiny" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Převést název hostitele na adresu IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Televizní program: číst informace o žánru (pomalé)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Doba čekání po naladění kanálu (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Povolit staré dialogové okno pro nahrávání seriálů" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Výchozí doba platnosti nahrávání" msgctxt "#30015" msgid "Streaming method" msgstr "Metoda streamování" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Uživatelský účet Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Heslo Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Použít streamování RTSP" msgctxt "#30040" msgid "Connection" msgstr "Spojení" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Přehrávání" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Nainstalovaná verze TVServerKodi „%s“ je příliš stará. Přejděte na „%s“ nebo novější." msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Vámi používaná verze TVServerKodi je příliš stará. Přejděte na „%s“ a novější!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Přehrávání nahrávky se nezdařilo. Nevyplněná URL adresa nebo název souboru." msgctxt "#30060" msgid "All cards are busy" msgstr "Všechny karty jsou zaneprázdněny" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanál je šifrovaný" msgctxt "#30062" msgid "No video or audio detected" msgstr "Nebylo rozpoznáno žádné video nebo audio" msgctxt "#30063" msgid "No signal detected" msgstr "Nebyl rozpoznán žádný signál" msgctxt "#30064" msgid "Unknown error" msgstr "Neznámá chyba" msgctxt "#30065" msgid "Unable to start graph" msgstr "Nelze spustit graf" msgctxt "#30066" msgid "Unknown channel" msgstr "Neznámý kanál" msgctxt "#30067" msgid "No tuning details" msgstr "Žádné podrobnosti ladění" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanál není namapován k žádné kartě" msgctxt "#30069" msgid "Card is disabled" msgstr "Karta je zakázána" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Připojení k podřízenému zařízení se nezdařilo" msgctxt "#30071" msgid "Not the owner" msgstr "Není vlastník" msgctxt "#30072" msgid "Graph building failed" msgstr "Sestavení grafu se nezdařilo" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Chybí softwarový kodér" msgctxt "#30074" msgid "No free disk space" msgstr "Žádné volné místo na disku" msgctxt "#30075" msgid "No PMT found" msgstr "Nenalezeno žádné PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "Nastavení plánu" msgctxt "#30101" msgid "Frequency" msgstr "Frekvence" msgctxt "#30102" msgid "Airtime" msgstr "Čas vysílání" msgctxt "#30103" msgid "Channels" msgstr "Kanály" msgctxt "#30104" msgid "Keep" msgstr "Ponechat" msgctxt "#30105" msgid "Record minutes before start" msgstr "Počet nahraných minut před začátkem" msgctxt "#30106" msgid "Record minutes after end" msgstr "Počet nahraných minut po konci" msgctxt "#30110" msgid "Record Once" msgstr "Jednorázově nahrát" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Nahrávat denně (tento program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Nahrávat týdně" msgctxt "#30113" msgid "Record Weekends" msgstr "Nahrávat o víkendech" msgctxt "#30114" msgid "Record Weekdays" msgstr "Nahrávat ve všední dny" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Nahrávat pokaždé na tomto kanálu" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Nahrávat pokaždé na všech kanálech" msgctxt "#30117" msgid "Record every week at this time" msgstr "Nahrávat každý týden v tento čas" msgctxt "#30118" msgid "Record every day at this time" msgstr "Nahrávat každý den v tento čas" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Nahrávat týdně na tomto kanále" msgctxt "#30120" msgid "This time" msgstr "Tento čas" msgctxt "#30121" msgid "Anytime" msgstr "Kdykoli" msgctxt "#30122" msgid "Manual" msgstr "Ručně" msgctxt "#30125" msgid "This Channel" msgstr "Tento kanál" msgctxt "#30126" msgid "Any Channel" msgstr "Jakýkoliv kanál" msgctxt "#30130" msgid "Until space needed" msgstr "Dokud není potřeba místo" msgctxt "#30131" msgid "Until watched" msgstr "Dokud není zhlédnuto" msgctxt "#30132" msgid "Days" msgstr "dní" msgctxt "#30133" msgid "Always" msgstr "Vždy" msgctxt "#30134" msgid "1 week" msgstr "1 týden" msgctxt "#30135" msgid "Backend default" msgstr "Výchozí nástavení serveru" msgctxt "#30136" msgid "Kodi default" msgstr "Výchozí nastavení Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d týdnů" msgctxt "#30138" msgid "1 month" msgstr "1 měsíc" msgctxt "#30139" msgid "%d months" msgstr "%d měsíců" msgctxt "#30140" msgid "1 year" msgstr "1 rok" resource.language.cy_gb/000077500000000000000000000000001346756700600346635ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000125331346756700600367200ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.cy_gb# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Welsh (United Kingdom) (http://www.transifex.com/projects/p/kodi-main/language/cy_GB/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: cy_GB\n" "Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Enw Gweinydd Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Porth ategyn Mediaportal Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Am ddim i'w ffrydio yn unig" msgctxt "#30003" msgid "Include Radio" msgstr "Cynnwys Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Newid sianel yn gyflym (peidio atal newid amser)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Cysylltu seibiau" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Mewnforio Sianeli Teledu o'r grŵp yn unig" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Mewnforio Sianeli Radio o'r grŵp yn unig" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Trosi enw gwesteiwr i gyfeiriad IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Amserlenni Rhaglenni Electronig: Darllen llinynnau math (araf)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Amser aros wedi tiwnio sianel (m'au)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Cadw recordiad am" msgctxt "#30015" msgid "Streaming method" msgstr "Dull ffrydio" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Cyfrif defnyddiwr Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Cyfrinair Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Defnyddio ffrydio RTSP" msgctxt "#30040" msgid "Connection" msgstr "Cysylltiad" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Chwarae" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Mae eich fersiwn TVServerKodi '%s' yn rhy hen. Diweddarwch i '%s' neu'n uwch!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Mae eich fersiwn TVServerKodi yn rhy hen. Diweddarwch i '%s' neu'n uwch!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Methwyd chwarae'r recordiad. URL neu enw ffeil gwag." msgctxt "#30060" msgid "All cards are busy" msgstr "Mae'r holl gardiau'n brysur" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Mae'r sianel yn gaeëdig" msgctxt "#30062" msgid "No video or audio detected" msgstr "Heb ganfod fideo na sain" msgctxt "#30063" msgid "No signal detected" msgstr "Heb ganfod signal" msgctxt "#30064" msgid "Unknown error" msgstr "Gwall anhysbys" msgctxt "#30065" msgid "Unable to start graph" msgstr "Methu cychwyn y graff" msgctxt "#30066" msgid "Unknown channel" msgstr "Sianel anhysbys" msgctxt "#30067" msgid "No tuning details" msgstr "Dim manylion tiwnio" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Nid yw'r sianel wedi'i mapio i unrhyw gerdyn" msgctxt "#30069" msgid "Card is disabled" msgstr "Analluogwyd y cerdyn" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Methodd y cysylltiad i'r gwas" msgctxt "#30071" msgid "Not the owner" msgstr "Nid y perchennog" msgctxt "#30072" msgid "Graph building failed" msgstr "Methodd adeiladu'r graff" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Amgodiwr SW ar goll" msgctxt "#30074" msgid "No free disk space" msgstr "Dim lle ar y ddisg" msgctxt "#30075" msgid "No PMT found" msgstr "Heb ganfod PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "Gosodiadau amseru" msgctxt "#30101" msgid "Frequency" msgstr "Amlder" msgctxt "#30102" msgid "Airtime" msgstr "Ar awyr" msgctxt "#30103" msgid "Channels" msgstr "Sianeli" msgctxt "#30104" msgid "Keep" msgstr "Cadw" msgctxt "#30105" msgid "Record minutes before start" msgstr "Munudau recordio cyn cychwyn" msgctxt "#30106" msgid "Record minutes after end" msgstr "Munudau recordio ar ôl gorffen" msgctxt "#30110" msgid "Record Once" msgstr "Recordio'n Unwaith" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Recordio'n Ddyddiol (y rhaglen hon)" msgctxt "#30112" msgid "Record Weekly" msgstr "Recordio'n Wythnosol" msgctxt "#30113" msgid "Record Weekends" msgstr "Recordio ar y Penwythnos" msgctxt "#30120" msgid "This time" msgstr "Y tro yma" msgctxt "#30121" msgid "Anytime" msgstr "Unrhyw bryd" msgctxt "#30122" msgid "Manual" msgstr "Gyda llaw" msgctxt "#30125" msgid "This Channel" msgstr "Y Sianel Hon" msgctxt "#30126" msgid "Any Channel" msgstr "Unrhyw Sianel" msgctxt "#30130" msgid "Until space needed" msgstr "Nes bod angen y lle" msgctxt "#30132" msgid "Days" msgstr "Diwrnod" msgctxt "#30133" msgid "Always" msgstr "Bob tro" msgctxt "#30135" msgid "Backend default" msgstr "Ôl-ragosodiad" msgctxt "#30136" msgid "Kodi default" msgstr "Rhagosodiad Kodi" resource.language.da_dk/000077500000000000000000000000001346756700600346425ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000126511346756700600367000ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.da_dk# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Danish (Denmark) (http://www.transifex.com/projects/p/kodi-main/language/da_DK/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: da_DK\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Værtsnavn" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi plug-in port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Kun ukodet" msgctxt "#30003" msgid "Include Radio" msgstr "Inkluder Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Hurtig kanalskift (stop ikke tidsforskydning)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Tidsfrist for forbindelse (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importer kun TV-kanaler fra gruppe" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importer kun Radio-kanaler fra gruppe" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Konverter værtsnavn til IP-adresse" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Læs genrestrenge (langsom)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Ventetid efter valg af kanal (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Standard levetid for optagelse" msgctxt "#30015" msgid "Streaming method" msgstr "Stream-metode" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows brugerkonto (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows kodeord (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Brug RTSP stream" msgctxt "#30040" msgid "Connection" msgstr "Forbindelse" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Afspilning" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Din TVserverKodi version '%s' er for gammel. Opgrader venligst til '%s' eller højere!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Din TVserverKodi version er for gammel. Opgrader venligst til '%s' eller højere!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Afspilning af optagelsen mislykkedes. Fjern filnavn fra URL" msgctxt "#30060" msgid "All cards are busy" msgstr "Alle kort er optagede" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanalen er kodet" msgctxt "#30062" msgid "No video or audio detected" msgstr "Ingen video eller lyd fundet" msgctxt "#30063" msgid "No signal detected" msgstr "Intet signal fundet" msgctxt "#30064" msgid "Unknown error" msgstr "Ukendt fejl" msgctxt "#30065" msgid "Unable to start graph" msgstr "Grafen kan ikke startes" msgctxt "#30066" msgid "Unknown channel" msgstr "Ukendt kanal" msgctxt "#30067" msgid "No tuning details" msgstr "Ingen tuning-detaljer" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanalen er ikke defineret til nogen kort" msgctxt "#30069" msgid "Card is disabled" msgstr "Kortet er deaktiveret" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Forbindelsen til slave mislykkedes" msgctxt "#30071" msgid "Not the owner" msgstr "Ikke ejeren" msgctxt "#30072" msgid "Graph building failed" msgstr "Grafopbygning mislykkedes" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW indkoder mangler" msgctxt "#30074" msgid "No free disk space" msgstr "Ingen ledig diskplads" msgctxt "#30075" msgid "No PMT found" msgstr "Ingen PMT fundet" msgctxt "#30100" msgid "Schedule settings" msgstr "Planlægningsindstillinger" msgctxt "#30101" msgid "Frequency" msgstr "Hyppighed" msgctxt "#30102" msgid "Airtime" msgstr "Sendetid" msgctxt "#30103" msgid "Channels" msgstr "Kanaler" msgctxt "#30104" msgid "Keep" msgstr "Behold" msgctxt "#30105" msgid "Record minutes before start" msgstr "Optag minutter før start" msgctxt "#30106" msgid "Record minutes after end" msgstr "Optag minutter før slut" msgctxt "#30110" msgid "Record Once" msgstr "Optag en gang" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Optag daglig (Dette program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Optag ugentlig" msgctxt "#30113" msgid "Record Weekends" msgstr "Optag weekender" msgctxt "#30120" msgid "This time" msgstr "Dette tidspunkt" msgctxt "#30121" msgid "Anytime" msgstr "Vilkårligt tidspunkt" msgctxt "#30122" msgid "Manual" msgstr "Manuelt" msgctxt "#30125" msgid "This Channel" msgstr "Denne kanal" msgctxt "#30126" msgid "Any Channel" msgstr "Hvilken som helst kanal" msgctxt "#30130" msgid "Until space needed" msgstr "Indtil fri plads nødvendig" msgctxt "#30131" msgid "Until watched" msgstr "Indtil set" msgctxt "#30132" msgid "Days" msgstr "Dage" msgctxt "#30133" msgid "Always" msgstr "Altid" msgctxt "#30134" msgid "1 week" msgstr "1 uge" msgctxt "#30135" msgid "Backend default" msgstr "Backend standard" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi standard" msgctxt "#30140" msgid "1 year" msgstr "1 år" resource.language.de_de/000077500000000000000000000000001346756700600346405ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000146201346756700600366740ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.de_de# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: German (Germany) (http://www.transifex.com/projects/p/kodi-main/language/de_DE/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: de_DE\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostname oder IP-Adresse" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Plugin Kodi Port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Nur frei empfangbare Kanäle" msgctxt "#30003" msgid "Include Radio" msgstr "Radiokanäle mit einbeziehen" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Schneller Kanalwechsel (ohne Unterbrechung von Timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Verbindungszeitüberschreitung (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importiere nur TV Kanäle aus Gruppe" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importiere nur Radiokanäle aus Gruppe" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Übersetze Hostname zu IP-Adresse" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Genre Texte lesen (langsam)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Wartezeit, nachdem ein Kanal abgestimmt worden ist (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Aktiviere Dialog für alte Serien-Aufnahmen" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Standard-Verfallsdauer für Aufnahmen" msgctxt "#30015" msgid "Streaming method" msgstr "Streaming-Methode" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows Benutzername (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows Passwort (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "RTSP Stream verwenden" msgctxt "#30040" msgid "Connection" msgstr "Verbindung" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Wiedergabe" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Dein TVServerKodi plugin '%s' ist zu alt. Du brauchst mindestens Version '%s'!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Dein TVServerKodi plugin ist zu alt. Du brauchst mindestens Version '%s'!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Aufnahme-Wiedergabe fehlgeschlagen. Leere URL oder Dateiname." msgctxt "#30060" msgid "All cards are busy" msgstr "Alle TV-Adapter in Benutzung" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Verschlüsselter Kanal" msgctxt "#30062" msgid "No video or audio detected" msgstr "Kein Bild oder Ton erkannt" msgctxt "#30063" msgid "No signal detected" msgstr "Kein Signal" msgctxt "#30064" msgid "Unknown error" msgstr "Unbekannter Fehler" msgctxt "#30065" msgid "Unable to start graph" msgstr "Kurve kann nicht gestartet werden" msgctxt "#30066" msgid "Unknown channel" msgstr "Unbekannter Kanal" msgctxt "#30067" msgid "No tuning details" msgstr "Keine Tuning-Details" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanal ist keiner Karte zugeordnet" msgctxt "#30069" msgid "Card is disabled" msgstr "Karte deaktiviert" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Verbindung zum Slave fehlgeschlagen" msgctxt "#30071" msgid "Not the owner" msgstr "Nicht der Besitzer" msgctxt "#30072" msgid "Graph building failed" msgstr "Kurvenerstellung fehlgeschlagen" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Software-Encoder fehlt" msgctxt "#30074" msgid "No free disk space" msgstr "Kein Speicherplatz frei" msgctxt "#30075" msgid "No PMT found" msgstr "Keine PMT gefunden" msgctxt "#30100" msgid "Schedule settings" msgstr "Zeitplaneinstellungen" msgctxt "#30101" msgid "Frequency" msgstr "Frequenz" msgctxt "#30102" msgid "Airtime" msgstr "Sendezeit" msgctxt "#30103" msgid "Channels" msgstr "Sender" msgctxt "#30104" msgid "Keep" msgstr "Behalten" msgctxt "#30105" msgid "Record minutes before start" msgstr "Aufnahmeminuten vor dem Beginn" msgctxt "#30106" msgid "Record minutes after end" msgstr "Aufnahmeminuten nach dem Ende" msgctxt "#30110" msgid "Record Once" msgstr "Einmal aufnehmen" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Täglich aufnehmen (Dieses Programm)" msgctxt "#30112" msgid "Record Weekly" msgstr "Wöchentlich aufnehmen" msgctxt "#30113" msgid "Record Weekends" msgstr "An Wochenenden aufnehmen" msgctxt "#30114" msgid "Record Weekdays" msgstr "Werktage aufnehmen" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Auf diesem Kanal immer aufnehmen" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Jedesmal auf jedem Kanal aufnehmen" msgctxt "#30117" msgid "Record every week at this time" msgstr "Jede Woche um diese Zeit aufnehmen" msgctxt "#30118" msgid "Record every day at this time" msgstr "Jeden Tag um diese Zeit aufnehmen" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Wöchentlich auf diesem Kanal aufnehmen" msgctxt "#30120" msgid "This time" msgstr "Diese Zeit" msgctxt "#30121" msgid "Anytime" msgstr "Jederzeit" msgctxt "#30122" msgid "Manual" msgstr "Manuell" msgctxt "#30125" msgid "This Channel" msgstr "Dieser Kanal" msgctxt "#30126" msgid "Any Channel" msgstr "Beliebiger Sender" msgctxt "#30130" msgid "Until space needed" msgstr "Bis Speicherplatz benötigt wird" msgctxt "#30131" msgid "Until watched" msgstr "Bis angeshen" msgctxt "#30132" msgid "Days" msgstr "Tage" msgctxt "#30133" msgid "Always" msgstr "Immer" msgctxt "#30134" msgid "1 week" msgstr "1 Woche" msgctxt "#30135" msgid "Backend default" msgstr "Backend-Standard" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi default" msgctxt "#30137" msgid "%d weeks" msgstr "%d Wochen" msgctxt "#30138" msgid "1 month" msgstr "1 Monat" msgctxt "#30139" msgid "%d months" msgstr "%d Monate" msgctxt "#30140" msgid "1 year" msgstr "1 Jahr" resource.language.el_gr/000077500000000000000000000000001346756700600346705ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000166421346756700600367320ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.el_gr# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Greek (Greece) (http://www.transifex.com/projects/p/kodi-main/language/el_GR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: el_GR\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Όνομα Υπολογιστή του Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Θύρα του plugin Mediaportal του Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Ελεύθερα κανάλια μόνο" msgctxt "#30003" msgid "Include Radio" msgstr "Συμπερίληψη Ραδιοφώνου" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Γρήγορο ζάπινγκ (χωρίς χρονική μετατόπιση)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Χρονικό όριο σύνδεσης (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Εισαγωγή μόνο Τηλεοπτικών Καναλιών από το γκρουπ" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Εισαγωγή μόνο Ραδιοφωνικών Σταθμών από το γκρουπ" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Μετατροπή Ονόματος Υπολογιστή σε διεύθυνση IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Ανάγνωση είδους (αργό)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Χρόνος αναμονής μετά τη ρύθμιση ενός καναλιού (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Προεπιλεγμένος χρόνος ζωής εγγραφής" msgctxt "#30015" msgid "Streaming method" msgstr "Μέθοδος ροής" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Όνομα Χρήστη Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Κωδικός Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Χρήση ροής RTSP" msgctxt "#30040" msgid "Connection" msgstr "Σύνδεση" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Αναπαραγωγή" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Η έκδοση '%s' του TVServerKodi είναι πολύ παλιά. Αναβαθμίστε στην '%s' ή ανώτερη!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Η έκδοση του TVServerKodi είναι πολύ παλιά. Αναβαθμίστε στην '%s' ή ανώτερη!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Αποτυχία αναπαραγωγής εγγραφής. Κενή URL του ονόματος αρχείου." msgctxt "#30060" msgid "All cards are busy" msgstr "Όλες οι κάρτες είναι απασχολημένες" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Κωδικοποιημένο κανάλι" msgctxt "#30062" msgid "No video or audio detected" msgstr "Δεν εντοπίστηκε εικόνα ή ήχος" msgctxt "#30063" msgid "No signal detected" msgstr "Δεν εντοπίστηκε σήμα" msgctxt "#30064" msgid "Unknown error" msgstr "Άγνωστο σφάλμα" msgctxt "#30065" msgid "Unable to start graph" msgstr "Αδυναμία εκκίνησης γραφήματος" msgctxt "#30066" msgid "Unknown channel" msgstr "Άγνωστο κανάλι" msgctxt "#30067" msgid "No tuning details" msgstr "Καθόλου λεπτομέρειες συντονισμού" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Το κανάλι δεν είναι αντιστοιχισμένο σε καμία κάρτα" msgctxt "#30069" msgid "Card is disabled" msgstr "Η κάρτα είναι απενεργοποιημένη" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Αποτυχία σύνδεσης στο slave" msgctxt "#30071" msgid "Not the owner" msgstr "Δεν είναι ο ιδιοκτήτης" msgctxt "#30072" msgid "Graph building failed" msgstr "Αποτυχία δημιουργίας γραφήματος" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Λείπει ο SW κωδικοποιητής" msgctxt "#30074" msgid "No free disk space" msgstr "Ανεπαρκής αποθηκευτικός χώρος" msgctxt "#30075" msgid "No PMT found" msgstr "Δε βρέθηκε κανένα PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "Ρυθμίσεις προγραμματισμού" msgctxt "#30101" msgid "Frequency" msgstr "Συχνότητα" msgctxt "#30102" msgid "Airtime" msgstr "Ώρα προβολής" msgctxt "#30103" msgid "Channels" msgstr "Κανάλια" msgctxt "#30104" 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 "#30120" msgid "This time" msgstr "Αυτή τη φορά" msgctxt "#30121" msgid "Anytime" msgstr "Κάθε φορά" msgctxt "#30122" msgid "Manual" msgstr "Χειροκίνητα" msgctxt "#30125" msgid "This Channel" msgstr "Αυτό το Κανάλι" msgctxt "#30126" msgid "Any Channel" msgstr "Κάθε Κανάλι" msgctxt "#30130" msgid "Until space needed" msgstr "Μέχρι να απαιτηθεί χώρος" msgctxt "#30132" msgid "Days" msgstr "Μέρες" msgctxt "#30133" msgid "Always" msgstr "Πάντα" msgctxt "#30134" msgid "1 week" msgstr "1 εβδομάδα" msgctxt "#30135" msgid "Backend default" msgstr "Προεπιλογή backend" 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 χρόνο" resource.language.en_au/000077500000000000000000000000001346756700600346675ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000122161346756700600367220ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.en_au# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: English (Australia) (http://www.transifex.com/projects/p/kodi-main/language/en_AU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: en_AU\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostname" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi plugin Port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Free-to-air only" msgctxt "#30003" msgid "Include Radio" msgstr "Include Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Fast channel switching (don't stop timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Connect timeout (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Import only TV Channels from group" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Import only Radio Channels from group" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convert hostname to IP-adress" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Read genre strings (slow)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Wait time after tuning a channel (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Default recording lifetime" msgctxt "#30015" msgid "Streaming method" msgstr "Streaming method" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows user account (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows password (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Use RTSP streaming" msgctxt "#30040" msgid "Connection" msgstr "Connection" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Playback" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Recording playback failed. Empty URL of filename." msgctxt "#30060" msgid "All cards are busy" msgstr "All cards are busy" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Channel is scrambled" msgctxt "#30062" msgid "No video or audio detected" msgstr "No video or audio detected" msgctxt "#30063" msgid "No signal detected" msgstr "No signal detected" msgctxt "#30064" msgid "Unknown error" msgstr "Unknown error" msgctxt "#30065" msgid "Unable to start graph" msgstr "Unable to start graph" msgctxt "#30066" msgid "Unknown channel" msgstr "Unknown channel" msgctxt "#30067" msgid "No tuning details" msgstr "No tuning details" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Channel is not mapped to any card" msgctxt "#30069" msgid "Card is disabled" msgstr "Card is disabled" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Connection to slave failed" msgctxt "#30071" msgid "Not the owner" msgstr "Not the owner" msgctxt "#30072" msgid "Graph building failed" msgstr "Graph building failed" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW Encoder missing" msgctxt "#30074" msgid "No free disk space" msgstr "No free disk space" msgctxt "#30075" msgid "No PMT found" msgstr "No PMT found" msgctxt "#30100" msgid "Schedule settings" msgstr "Schedule settings" msgctxt "#30101" msgid "Frequency" msgstr "Frequency" msgctxt "#30102" msgid "Airtime" msgstr "Airtime" msgctxt "#30103" msgid "Channels" msgstr "Channels" msgctxt "#30104" msgid "Keep" msgstr "Keep" msgctxt "#30105" msgid "Record minutes before start" msgstr "Record minutes before start" msgctxt "#30106" msgid "Record minutes after end" msgstr "Record minutes after end" msgctxt "#30110" msgid "Record Once" msgstr "Record Once" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Record Daily (This program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Record Weekly" msgctxt "#30113" msgid "Record Weekends" msgstr "Record Weekends" msgctxt "#30120" msgid "This time" msgstr "This time" msgctxt "#30121" msgid "Anytime" msgstr "Anytime" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "This Channel" msgctxt "#30126" msgid "Any Channel" msgstr "Any Channel" msgctxt "#30130" msgid "Until space needed" msgstr "Until space needed" msgctxt "#30132" msgid "Days" msgstr "Days" msgctxt "#30133" msgid "Always" msgstr "Always" msgctxt "#30135" msgid "Backend default" msgstr "Backend default" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi default" resource.language.en_gb/000077500000000000000000000000001346756700600346525ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000123161346756700600367060ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.en_gb# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/kodi-main/language/en_GB/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: en_GB\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #Settings labels msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "" msgctxt "#30002" msgid "Free-to-air only" msgstr "" msgctxt "#30003" msgid "Include Radio" msgstr "" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "" msgctxt "#30012" msgid "Default recording lifetime" msgstr "" #empty strings from id 30013 to 30014 msgctxt "#30015" msgid "Streaming method" msgstr "" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "" #empty strings from id 30019 to 30039 #Category labels msgctxt "#30040" msgid "Connection" msgstr "" msgctxt "#30041" msgid "MediaPortal" msgstr "" msgctxt "#30042" msgid "Playback" 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!" msgstr "" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "" #empty strings from id 30053 to 30059 #Status message strings corresponding to the MediaPortal TvResult enum msgctxt "#30060" msgid "All cards are busy" msgstr "" msgctxt "#30061" msgid "Channel is scrambled" msgstr "" msgctxt "#30062" msgid "No video or audio detected" msgstr "" msgctxt "#30063" msgid "No signal detected" msgstr "" msgctxt "#30064" msgid "Unknown error" msgstr "" msgctxt "#30065" msgid "Unable to start graph" msgstr "" msgctxt "#30066" msgid "Unknown channel" msgstr "" msgctxt "#30067" msgid "No tuning details" msgstr "" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "" msgctxt "#30069" msgid "Card is disabled" msgstr "" msgctxt "#30070" msgid "Connection to slave failed" msgstr "" msgctxt "#30071" msgid "Not the owner" msgstr "" msgctxt "#30072" msgid "Graph building failed" msgstr "" msgctxt "#30073" msgid "SW Encoder missing" msgstr "" msgctxt "#30074" msgid "No free disk space" msgstr "" msgctxt "#30075" msgid "No PMT found" msgstr "" #empty strings from id 30076 to 30099 #Schedule settings dialog msgctxt "#30100" msgid "Schedule settings" msgstr "" msgctxt "#30101" msgid "Frequency" msgstr "" msgctxt "#30102" msgid "Airtime" msgstr "" msgctxt "#30103" msgid "Channels" msgstr "" msgctxt "#30104" msgid "Keep" msgstr "" msgctxt "#30105" msgid "Record minutes before start" msgstr "" msgctxt "#30106" msgid "Record minutes after end" msgstr "" #empty strings from id 30107 to 30109 #Frequency: 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 "" #Airtime: msgctxt "#30120" msgid "This time" msgstr "" msgctxt "#30121" msgid "Anytime" msgstr "" msgctxt "#30122" msgid "Manual" msgstr "" #empty strings from id 30123 to 30124 #Channel: msgctxt "#30125" msgid "This Channel" msgstr "" msgctxt "#30126" msgid "Any Channel" msgstr "" #empty strings from id 30127 to 30129 #Keep: msgctxt "#30130" msgid "Until space needed" msgstr "" msgctxt "#30131" msgid "Until watched" msgstr "" msgctxt "#30132" msgid "Days" msgstr "" msgctxt "#30133" msgid "Always" msgstr "" msgctxt "#30134" msgid "1 week" msgstr "" #Pre/post record time msgctxt "#30135" msgid "Backend default" msgstr "" msgctxt "#30136" msgid "Kodi default" msgstr "" msgctxt "#30137" msgid "%d weeks" msgstr "" msgctxt "#30138" msgid "1 month" msgstr "" msgctxt "#30139" msgid "%d months" msgstr "" msgctxt "#30140" msgid "1 year" msgstr "" resource.language.en_nz/000077500000000000000000000000001346756700600347115ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000141551346756700600367500ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.en_nz# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: English (New Zealand) (http://www.transifex.com/projects/p/kodi-main/language/en_NZ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: en_NZ\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostname" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi plugin Port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Free-to-air only" msgctxt "#30003" msgid "Include Radio" msgstr "Include Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Fast channel switching (don't stop timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Connect timeout (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Import only TV Channels from group" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Import only Radio Channels from group" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convert hostname to IP-adress" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Read genre strings (slow)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Wait time after tuning a channel (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Enable old series recording dialogue" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Default recording lifetime" msgctxt "#30015" msgid "Streaming method" msgstr "Streaming method" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows user account (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows password (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Use RTSP streaming" msgctxt "#30040" msgid "Connection" msgstr "Connection" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Playback" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Recording playback failed. Empty URL of filename." msgctxt "#30060" msgid "All cards are busy" msgstr "All cards are busy" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Channel is scrambled" msgctxt "#30062" msgid "No video or audio detected" msgstr "No video or audio detected" msgctxt "#30063" msgid "No signal detected" msgstr "No signal detected" msgctxt "#30064" msgid "Unknown error" msgstr "Unknown error" msgctxt "#30065" msgid "Unable to start graph" msgstr "Unable to start graph" msgctxt "#30066" msgid "Unknown channel" msgstr "Unknown channel" msgctxt "#30067" msgid "No tuning details" msgstr "No tuning details" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Channel is not mapped to any card" msgctxt "#30069" msgid "Card is disabled" msgstr "Card is disabled" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Connection to slave failed" msgctxt "#30071" msgid "Not the owner" msgstr "Not the owner" msgctxt "#30072" msgid "Graph building failed" msgstr "Graph building failed" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW Encoder missing" msgctxt "#30074" msgid "No free disk space" msgstr "No free disk space" msgctxt "#30075" msgid "No PMT found" msgstr "No PMT found" msgctxt "#30100" msgid "Schedule settings" msgstr "Schedule settings" msgctxt "#30101" msgid "Frequency" msgstr "Frequency" msgctxt "#30102" msgid "Airtime" msgstr "Airtime" msgctxt "#30103" msgid "Channels" msgstr "Channels" msgctxt "#30104" msgid "Keep" msgstr "Keep" msgctxt "#30105" msgid "Record minutes before start" msgstr "Record minutes before start" msgctxt "#30106" msgid "Record minutes after end" msgstr "Record minutes after end" msgctxt "#30110" msgid "Record Once" msgstr "Record Once" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Record Daily (This program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Record Weekly" msgctxt "#30113" msgid "Record Weekends" msgstr "Record Weekends" msgctxt "#30114" msgid "Record Weekdays" msgstr "Record Weekdays" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Record every time on this channel" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Record every time on every channel" msgctxt "#30117" msgid "Record every week at this time" msgstr "Record every week at this time" msgctxt "#30118" msgid "Record every day at this time" msgstr "Record every day at this time" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Record weekly on this channel" msgctxt "#30120" msgid "This time" msgstr "This time" msgctxt "#30121" msgid "Anytime" msgstr "Anytime" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "This Channel" msgctxt "#30126" msgid "Any Channel" msgstr "Any Channel" msgctxt "#30130" msgid "Until space needed" msgstr "Until space needed" msgctxt "#30131" msgid "Until watched" msgstr "Until watched" msgctxt "#30132" msgid "Days" msgstr "Days" msgctxt "#30133" msgid "Always" msgstr "Always" msgctxt "#30134" msgid "1 week" msgstr "1 week" msgctxt "#30135" msgid "Backend default" msgstr "Backend default" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi default" msgctxt "#30137" msgid "%d weeks" msgstr "%d weeks" msgctxt "#30138" msgid "1 month" msgstr "1 month" msgctxt "#30139" msgid "%d months" msgstr "%d months" msgctxt "#30140" msgid "1 year" msgstr "1 year" resource.language.en_us/000077500000000000000000000000001346756700600347115ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000141561346756700600367510ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.en_us# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: English (United States) (http://www.transifex.com/projects/p/kodi-main/language/en_US/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: en_US\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "MediaPortal Hostname" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "MediaPortal Kodi plugin Port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Free-to-air only" msgctxt "#30003" msgid "Include Radio" msgstr "Include Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Fast channel switching (don't stop timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Connect timeout (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Import only TV Channels from group" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Import only Radio Channels from group" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convert hostname to IP-address" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Read genre strings (slow)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Wait time after tuning a channel (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Enable old series recording dialog" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Default recording lifetime" msgctxt "#30015" msgid "Streaming method" msgstr "Streaming method" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows user account (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows password (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Use RTSP streaming" msgctxt "#30040" msgid "Connection" msgstr "Connection" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Playback" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Recording playback failed. Empty URL of filename." msgctxt "#30060" msgid "All cards are busy" msgstr "All cards are busy" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Channel is scrambled" msgctxt "#30062" msgid "No video or audio detected" msgstr "No video or audio detected" msgctxt "#30063" msgid "No signal detected" msgstr "No signal detected" msgctxt "#30064" msgid "Unknown error" msgstr "Unknown error" msgctxt "#30065" msgid "Unable to start graph" msgstr "Unable to start graph" msgctxt "#30066" msgid "Unknown channel" msgstr "Unknown channel" msgctxt "#30067" msgid "No tuning details" msgstr "No tuning details" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Channel is not mapped to any card" msgctxt "#30069" msgid "Card is disabled" msgstr "Card is disabled" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Connection to slave failed" msgctxt "#30071" msgid "Not the owner" msgstr "Not the owner" msgctxt "#30072" msgid "Graph building failed" msgstr "Graph building failed" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW Encoder missing" msgctxt "#30074" msgid "No free disk space" msgstr "No free disk space" msgctxt "#30075" msgid "No PMT found" msgstr "No PMT found" msgctxt "#30100" msgid "Schedule settings" msgstr "Schedule settings" msgctxt "#30101" msgid "Frequency" msgstr "Frequency" msgctxt "#30102" msgid "Airtime" msgstr "Airtime" msgctxt "#30103" msgid "Channels" msgstr "Channels" msgctxt "#30104" msgid "Keep" msgstr "Keep" msgctxt "#30105" msgid "Record minutes before start" msgstr "Record minutes before start" msgctxt "#30106" msgid "Record minutes after end" msgstr "Record minutes after end" msgctxt "#30110" msgid "Record Once" msgstr "Record Once" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Record Daily (This program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Record Weekly" msgctxt "#30113" msgid "Record Weekends" msgstr "Record Weekends" msgctxt "#30114" msgid "Record Weekdays" msgstr "Record Weekdays" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Record every time on this channel" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Record every time on every channel" msgctxt "#30117" msgid "Record every week at this time" msgstr "Record every week at this time" msgctxt "#30118" msgid "Record every day at this time" msgstr "Record every day at this time" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Record weekly on this channel" msgctxt "#30120" msgid "This time" msgstr "This time" msgctxt "#30121" msgid "Anytime" msgstr "Anytime" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "This Channel" msgctxt "#30126" msgid "Any Channel" msgstr "Any Channel" msgctxt "#30130" msgid "Until space needed" msgstr "Until space needed" msgctxt "#30131" msgid "Until watched" msgstr "Until watched" msgctxt "#30132" msgid "Days" msgstr "Days" msgctxt "#30133" msgid "Always" msgstr "Always" msgctxt "#30134" msgid "1 week" msgstr "1 week" msgctxt "#30135" msgid "Backend default" msgstr "Backend default" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi default" msgctxt "#30137" msgid "%d weeks" msgstr "%d weeks" msgctxt "#30138" msgid "1 month" msgstr "1 month" msgctxt "#30139" msgid "%d months" msgstr "%d months" msgctxt "#30140" msgid "1 year" msgstr "1 year" resource.language.eo/000077500000000000000000000000001346756700600342035ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000013451346756700600362370ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.eo# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Esperanto (http://www.transifex.com/projects/p/kodi-main/language/eo/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: eo\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30132" msgid "Days" msgstr "Days" resource.language.es_ar/000077500000000000000000000000001346756700600346715ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000130521346756700600367230ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.es_ar# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Spanish (Argentina) (http://www.transifex.com/projects/p/kodi-main/language/es_AR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: es_AR\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nombre del servidor Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Puerto de plugin Kodi MediaPortal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Sólo transmisiones gratuitas (Free-to-air)" msgctxt "#30003" msgid "Include Radio" msgstr "Incluir Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Cambio rápido de canales (No paran timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Límite de tiempo para conectar (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importar sólo canales de TV del grupo" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importar sólo canales de Radio del grupo" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convertir nombre de host a dirección IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "GEP: 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 "#30012" msgid "Default recording lifetime" msgstr "Tiempo de vida de las grabaciones por defecto" msgctxt "#30015" msgid "Streaming method" msgstr "Método de transmisión" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Cuenta de usuario de Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Contraseña de Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Utilizar RTSP streaming" msgctxt "#30040" msgid "Connection" msgstr "Conexión" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Reproducción" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Su versión TVServerKodi '%s' es demasiado antigua. ¡Por favor actualiza 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. ¡Por favor actualiza a '%s' o superior!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Error en la reproducción de la grabación. URL vacía de 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 codificado" msgctxt "#30062" msgid "No video or audio detected" msgstr "Audio o video no detectado" msgctxt "#30063" msgid "No signal detected" msgstr "Ninguna señal detectada" msgctxt "#30064" msgid "Unknown error" msgstr "Error desconocido" msgctxt "#30065" msgid "Unable to start graph" msgstr "No se puede iniciar el gráfico" msgctxt "#30066" msgid "Unknown channel" msgstr "Canal desconocido" msgctxt "#30067" msgid "No tuning details" msgstr "No hay detalles de sintonización" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Canal no asignado a ninguna tarjeta" msgctxt "#30069" msgid "Card is disabled" msgstr "Tarjeta deshabilitada" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Error de conexión al esclavo" msgctxt "#30071" msgid "Not the owner" msgstr "No eres el propietario" msgctxt "#30072" msgid "Graph building failed" msgstr "Error al construir los gráficos" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Falta codificador del SW" msgctxt "#30074" msgid "No free disk space" msgstr "No hay espacio libre en disco" msgctxt "#30075" msgid "No PMT found" msgstr "No PMT encontrado" msgctxt "#30100" msgid "Schedule settings" msgstr "Ajuste de horarios" msgctxt "#30101" msgid "Frequency" msgstr "Frecuencia" msgctxt "#30102" msgid "Airtime" msgstr "Hora de emisión" msgctxt "#30103" msgid "Channels" msgstr "Canales" msgctxt "#30104" msgid "Keep" msgstr "Mantener" msgctxt "#30105" msgid "Record minutes before start" msgstr "Minutos a grabar antes del inicio" msgctxt "#30106" msgid "Record minutes after end" msgstr "Minutos a grabar después del final" msgctxt "#30110" msgid "Record Once" msgstr "Grabar una vez" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Grabar diariamente (este programa)" msgctxt "#30112" msgid "Record Weekly" msgstr "Grabar semanalmente" msgctxt "#30113" msgid "Record Weekends" msgstr "Grabar fines de semana" msgctxt "#30120" msgid "This time" msgstr "A esta hora" msgctxt "#30121" msgid "Anytime" msgstr "En cualquier horario" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "En este canal" msgctxt "#30126" msgid "Any Channel" msgstr "En cualquier canal" msgctxt "#30130" msgid "Until space needed" msgstr "Hasta que se necesite el espacio" msgctxt "#30132" msgid "Days" msgstr "Días" msgctxt "#30133" msgid "Always" msgstr "Siempre" msgctxt "#30135" msgid "Backend default" msgstr "Backend por defecto" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi por defecto" resource.language.es_es/000077500000000000000000000000001346756700600346765ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000150031346756700600367260ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.es_es# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Spanish (Spain) (http://www.transifex.com/projects/p/kodi-main/language/es_ES/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: es_ES\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nombre del servidor Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Puerto de plugin Kodi MediaPortal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Sólo retransmisiones en abierto" msgctxt "#30003" msgid "Include Radio" msgstr "Incluir Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Cambio rápido de canales (No para timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Límite de tiempo para conectar (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importar sólo grupos de canales de TV" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importar sólo grupos de canales de Radio" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convertir nombre de host a 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 "Habilitar la ventana de grabación de series antiguas" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Tiempo de vida de las grabaciones por defecto" msgctxt "#30015" msgid "Streaming method" msgstr "Método de transmisión" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Cuenta de usuario de Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Contraseña de Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Utilizar transmisión RTSP" msgctxt "#30040" msgid "Connection" msgstr "Conexión" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Reproducción" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "La versión '%s' de TVServerKodi es demasiado antigua. ¡Por favor 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. ¡Por favor actualice a '%s' o superior!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Error en la reproducción de la grabación. URL vacía de 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 codificado" msgctxt "#30062" msgid "No video or audio detected" msgstr "Audio o vídeo no detectado" msgctxt "#30063" msgid "No signal detected" msgstr "Ninguna señal detectada" msgctxt "#30064" msgid "Unknown error" msgstr "Error desconocido" msgctxt "#30065" msgid "Unable to start graph" msgstr "No se puede iniciar el gráfico" msgctxt "#30066" msgid "Unknown channel" msgstr "Canal desconocido" msgctxt "#30067" msgid "No tuning details" msgstr "No hay detalles de sintonización" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Canal no asignado a ninguna tarjeta" msgctxt "#30069" msgid "Card is disabled" msgstr "Tarjeta deshabilitada" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Error de conexión al esclavo" msgctxt "#30071" msgid "Not the owner" msgstr "No eres el propietario" msgctxt "#30072" msgid "Graph building failed" msgstr "Error al construir los gráficos" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Falta codificador SW" msgctxt "#30074" msgid "No free disk space" msgstr "No hay espacio libre en disco" msgctxt "#30075" msgid "No PMT found" msgstr "No PMT encontrado" msgctxt "#30100" msgid "Schedule settings" msgstr "Ajustes de programaciones" msgctxt "#30101" msgid "Frequency" msgstr "Frecuencia" msgctxt "#30102" msgid "Airtime" msgstr "Hora de emisión" msgctxt "#30103" msgid "Channels" msgstr "Canales" msgctxt "#30104" msgid "Keep" msgstr "Mantener" msgctxt "#30105" msgid "Record minutes before start" msgstr "Minutos grabados antes de empezar" msgctxt "#30106" msgid "Record minutes after end" msgstr "Minutos grabados tras finalizar" msgctxt "#30110" msgid "Record Once" msgstr "Grabar una vez" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Grabar Diariamente (Este programa)" msgctxt "#30112" msgid "Record Weekly" msgstr "Grabar Semanalmente" msgctxt "#30113" 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" 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 necesite espacio" msgctxt "#30131" msgid "Until watched" msgstr "Hasta que lo vea" msgctxt "#30132" msgid "Days" msgstr "Días" msgctxt "#30133" msgid "Always" msgstr "Siempre" msgctxt "#30134" msgid "1 week" msgstr "1 semana" msgctxt "#30135" msgid "Backend default" msgstr "Backend por defecto" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi por defecto" 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" resource.language.es_mx/000077500000000000000000000000001346756700600347135ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000150171346756700600367500ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.es_mx# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Spanish (Mexico) (http://www.transifex.com/projects/p/kodi-main/language/es_MX/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "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 "#30012" msgid "Default recording lifetime" msgstr "Tiempo de grabacion predeterminada" 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)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Contraseña de Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Usar streaming RTSP" msgctxt "#30040" 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" msgctxt "#30063" msgid "No signal detected" msgstr "Señal no detectada" msgctxt "#30064" msgid "Unknown error" msgstr "Error desconocido" msgctxt "#30065" msgid "Unable to start graph" msgstr "No es posible comenzar el gráfico" msgctxt "#30066" 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" msgctxt "#30074" 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" msgctxt "#30102" msgid "Airtime" msgstr "Tiempo de transimisión" msgctxt "#30103" msgid "Channels" msgstr "Canales" msgctxt "#30104" 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" 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" resource.language.et_ee/000077500000000000000000000000001346756700600346615ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000103741346756700600367170ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.et_ee# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Estonian (Estonia) (http://www.transifex.com/projects/p/kodi-main/language/et_EE/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: et_EE\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal hosti nimi" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi plugina port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Ainult vabalevi" msgctxt "#30003" msgid "Include Radio" msgstr "Hõlma raadio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Kiire kanali vahetus (ära peata ajanihutust)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Ühenduse aegumine (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Impordi grupist ainult TV kanalid" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Impordi grupist ainult raadio kanalid" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Muuda hosti nimi IP aadressiks" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Loe žanri stringid (aeglane)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Viivitus peale häälestust (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Tavaline salvestuse hoiuaeg" msgctxt "#30015" msgid "Streaming method" msgstr "Voogesitus meetod" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windowsi kasutaja konto (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windowsi salasõna (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Kasuta RTSP voogesitust" msgctxt "#30040" msgid "Connection" msgstr "Ühendus" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Taasesitus" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Sinu TVServerKodi versioon '%s' on liiga vana. Palun uuenda '%s' või uuema peale!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Sinu TVServerKodi versioon on liiga vana. Palun uuenda '%s' või uuema peale!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Salvestise esitlus nurjus. Tühi URL või faili nimi." msgctxt "#30060" msgid "All cards are busy" msgstr "Kõik kaardid on hõivatud" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanal on krüpteeritud" msgctxt "#30062" msgid "No video or audio detected" msgstr "Videot ega heli ei leitud" msgctxt "#30063" msgid "No signal detected" msgstr "Signaali ei leitud" msgctxt "#30064" msgid "Unknown error" msgstr "Tundmatu tõrge" msgctxt "#30065" msgid "Unable to start graph" msgstr "Graafiku käivitamine nurjus" msgctxt "#30066" msgid "Unknown channel" msgstr "Tundmatu kanal" msgctxt "#30067" msgid "No tuning details" msgstr "Häälestuse detailid puuduvad" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanal pole ühegi kaardiga seotud" msgctxt "#30069" msgid "Card is disabled" msgstr "Kaart on keelatud" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Alluvaga ühenduse loomine nurjus" msgctxt "#30071" msgid "Not the owner" msgstr "Ei ole omanik" msgctxt "#30072" msgid "Graph building failed" msgstr "Graafiku ehitamine nurjus" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Tarkvaraline kodeerija puudub" msgctxt "#30074" msgid "No free disk space" msgstr "Kettaruum on otsas" msgctxt "#30075" msgid "No PMT found" msgstr "PMT ei leitud" msgctxt "#30102" msgid "Airtime" msgstr "Eetriaeg" msgctxt "#30103" msgid "Channels" msgstr "Kanalid" msgctxt "#30104" msgid "Keep" msgstr "Jäta alles" msgctxt "#30122" msgid "Manual" msgstr "Käsitsi" msgctxt "#30132" msgid "Days" msgstr "päeva" msgctxt "#30133" msgid "Always" msgstr "Alati" resource.language.eu_es/000077500000000000000000000000001346756700600347005ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000024641346756700600367370ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.eu_es# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Basque (Spain) (http://www.transifex.com/projects/p/kodi-main/language/eu_ES/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: eu_ES\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30003" msgid "Include Radio" msgstr "Irratia Barne Hartu" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Konexioa denboraz kanpo" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Grabazioen lehenetsitako bizitze denbora" msgctxt "#30042" msgid "Playback" msgstr "Erreprodukzioa" msgctxt "#30064" msgid "Unknown error" msgstr "Errore ezezaguna" msgctxt "#30101" msgid "Frequency" msgstr "Maiztasuna" msgctxt "#30103" msgid "Channels" msgstr "Kateak" msgctxt "#30104" msgid "Keep" msgstr "Mantendu" msgctxt "#30122" msgid "Manual" msgstr "Eskuz" msgctxt "#30132" msgid "Days" msgstr "Egunak" msgctxt "#30133" msgid "Always" msgstr "Beti" resource.language.fa_af/000077500000000000000000000000001346756700600346345ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000040411346756700600366640ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.fa_af# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Persian (Afghanistan) (http://www.transifex.com/projects/p/kodi-main/language/fa_AF/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fa_AF\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "نام سرور مدیا پورتال" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "پورت پلاگین مدیاپورتال Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "فقط کانالهلی Free" msgctxt "#30003" msgid "Include Radio" msgstr "در برگرفتن رادیو" msgctxt "#30040" msgid "Connection" msgstr "اتصال" msgctxt "#30041" msgid "MediaPortal" msgstr "مدیاپورتال" msgctxt "#30042" msgid "Playback" msgstr "باز پخش" msgctxt "#30060" msgid "All cards are busy" msgstr "همه کارتها مشغول هستند" msgctxt "#30062" msgid "No video or audio detected" msgstr "بدون صدا و یا تصویر" msgctxt "#30063" msgid "No signal detected" msgstr "بدون سیگنال" msgctxt "#30064" msgid "Unknown error" msgstr "خطای ناشناخته" msgctxt "#30065" msgid "Unable to start graph" msgstr "ناتوانی در اجرای گراف" msgctxt "#30066" msgid "Unknown channel" msgstr "کانال ناشناخته" msgctxt "#30069" msgid "Card is disabled" msgstr "کارت غیر فعال است" msgctxt "#30073" msgid "SW Encoder missing" msgstr "انکودر نرافزاری پیدا نشد" msgctxt "#30074" msgid "No free disk space" msgstr "فضای خالی کافی در دیسک وجود ندارد." msgctxt "#30133" msgid "Always" msgstr "همیشه" resource.language.fa_ir/000077500000000000000000000000001346756700600346605ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000020601346756700600367070ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.fa_ir# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Persian (Iran) (http://www.transifex.com/projects/p/kodi-main/language/fa_IR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fa_IR\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30012" msgid "Default recording lifetime" msgstr "زمان اعتبار پیش فرض ضبط" msgctxt "#30042" msgid "Playback" msgstr "پخش" msgctxt "#30103" msgid "Channels" msgstr "کانال ها" msgctxt "#30104" msgid "Keep" msgstr "حفظ شود" msgctxt "#30122" msgid "Manual" msgstr "دستی" msgctxt "#30132" msgid "Days" msgstr "روز" msgctxt "#30133" msgid "Always" msgstr "همیشه" resource.language.fi_fi/000077500000000000000000000000001346756700600346545ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000147631346756700600367200ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.fi_fi# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Finnish (Finland) (http://www.transifex.com/projects/p/kodi-main/language/fi_FI/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fi_FI\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal-palvelimen nimi" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportalin Kodi-lisäosan portti" msgctxt "#30002" msgid "Free-to-air only" msgstr "Vain salaamattomat kanavat" msgctxt "#30003" msgid "Include Radio" msgstr "Sisällytä radiokanavat" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Kanavan pikavaihto (älä keskeytä ajansiirtoa)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Yhteyden aikakatkaisu (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Tuo ainoastaan tv-kanavat ryhmästä" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Tuo ainoastaan radiokanavat ryhmästä" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Muunna palvelimen nimi IP-osoitteeksi" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Ohjelmaopas: Lue lajityyppi merkkijonoista (hidas)" msgctxt "#30010" 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 "#30012" msgid "Default recording lifetime" msgstr "Tallennuksen oletuselinikä" msgctxt "#30015" msgid "Streaming method" msgstr "Suoratoistotapa" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows-käyttäjätunnus (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows-salasana (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Käytä RTSP-suoratoistoa" msgctxt "#30040" msgid "Connection" msgstr "Yhteys" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Toisto" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" 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 "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 "Tallennuksen toistaminen epäonnistui. Tyhjä URL-osoite tai tiedostonimi." msgctxt "#30060" msgid "All cards are busy" msgstr "Ei vapaita virittimiä." msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanava on salattu" msgctxt "#30062" msgid "No video or audio detected" msgstr "Videota tai ääntä ei havaittu" msgctxt "#30063" msgid "No signal detected" msgstr "Signaalia ei havaittu" msgctxt "#30064" msgid "Unknown error" msgstr "Tuntematon virhe" msgctxt "#30065" msgid "Unable to start graph" msgstr "Kuvaajan näyttäminen ei onnistunut" msgctxt "#30066" msgid "Unknown channel" msgstr "Tuntematon kanava" msgctxt "#30067" msgid "No tuning details" msgstr "Ei viritystietoja" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanavaa ei ole osoitettu yhdellekään virittimelle." msgctxt "#30069" msgid "Card is disabled" msgstr "Viritin on poistettu käytöstä." msgctxt "#30070" msgid "Connection to slave failed" msgstr "Yhteys työläiseen epäonnistui." msgctxt "#30071" msgid "Not the owner" msgstr "Ei omistaja" msgctxt "#30072" msgid "Graph building failed" msgstr "Kuvaajan muodostaminen epäonnistui" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Ohjelmistokodekki puuttuu" msgctxt "#30074" msgid "No free disk space" msgstr "Vapaata levytilaa ei ole" msgctxt "#30075" msgid "No PMT found" msgstr "PMT:tä ei löytynyt" msgctxt "#30100" msgid "Schedule settings" msgstr "Ajastimen asetukset" msgctxt "#30101" msgid "Frequency" msgstr "Taajuus" msgctxt "#30102" msgid "Airtime" msgstr "Esitysaika" msgctxt "#30103" msgid "Channels" msgstr "Kanavat" msgctxt "#30104" msgid "Keep" msgstr "Säilytä" msgctxt "#30105" msgid "Record minutes before start" msgstr "Tallennuksen alkuun lisättävä aika" msgctxt "#30106" msgid "Record minutes after end" msgstr "Tallennuksen loppuun lisättävä aika" msgctxt "#30110" msgid "Record Once" msgstr "Tallenna kerran" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Tallenna päivittäin (tämä ohjelma)" msgctxt "#30112" msgid "Record Weekly" msgstr "Tallenna viikoittain" msgctxt "#30113" 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" msgctxt "#30121" msgid "Anytime" msgstr "Milloin tahansa" msgctxt "#30122" msgid "Manual" msgstr "Manuaalisesti" msgctxt "#30125" msgid "This Channel" msgstr "Tämä kanava" msgctxt "#30126" msgid "Any Channel" msgstr "Mikä tahansa kanava" msgctxt "#30130" msgid "Until space needed" msgstr "Kunnes tarvetta lisätilalle" msgctxt "#30131" msgid "Until watched" msgstr "Kunnes katsottu" msgctxt "#30132" msgid "Days" msgstr "päivää" msgctxt "#30133" msgid "Always" msgstr "Aina" msgctxt "#30134" msgid "1 week" msgstr "1 viikko" msgctxt "#30135" msgid "Backend default" msgstr "Taustaosan oletus" msgctxt "#30136" msgid "Kodi default" msgstr "Kodin oletus" msgctxt "#30137" msgid "%d weeks" msgstr "%d viikkoa" msgctxt "#30138" msgid "1 month" msgstr "1 kuukausi" msgctxt "#30139" msgid "%d months" msgstr "%d kuukautta" msgctxt "#30140" msgid "1 year" msgstr "1 vuosi" resource.language.fo_fo/000077500000000000000000000000001346756700600346705ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000016761346756700600367330ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.fo_fo# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Faroese (Faroe Islands) (http://www.transifex.com/projects/p/kodi-main/language/fo_FO/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fo_FO\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Vanlig livitíð av upptøku" msgctxt "#30042" msgid "Playback" msgstr "Avspæling" msgctxt "#30103" msgid "Channels" msgstr "Rás" msgctxt "#30132" msgid "Days" msgstr "Dagar" msgctxt "#30133" msgid "Always" msgstr "Altíð" resource.language.fr_ca/000077500000000000000000000000001346756700600346525ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000155501346756700600367110ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.fr_ca# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: French (Canada) (http://www.transifex.com/projects/p/kodi-main/language/fr_CA/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fr_CA\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nom d’hôte de MediaPortal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Port du plugiciel Kodi de MediaPortal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Diffusé en clair uniquement" msgctxt "#30003" msgid "Include Radio" msgstr "Inclure la radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Changement rapide des chaînes (ne pas arrêter le décalage temporel)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Temporisation de connexion (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importer seulement les chaînes télé du groupe" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importer seulement les chaînes radio du groupe" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convertir le nom d’hôte en adresse IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "GÉP : lire les données de genre (lent)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Temps d’attente après changement de chaîne (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Activer l’ancienne boîte de dialogue d’enregistrement des séries" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Durée de vie par défaut des enregistrements" msgctxt "#30015" msgid "Streaming method" msgstr "Méthode de diffusion en continu" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Compte utilisateur Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Mot de passe Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Utiliser la diffusion en continu RTSP" msgctxt "#30040" msgid "Connection" msgstr "Connexion" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Lecture" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Votre version « %s » de TVServerKodi est trop ancienne. Veuillez mettre à niveau vers « %s » ou ultérieure !" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Votre version de TVServerKodi est trop ancienne. Veuillez mettre à niveau vers « %s » ou ultérieure !" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "La lecture de l’enregistrement a échoué. L’URL du nom de fichier est vide." msgctxt "#30060" msgid "All cards are busy" msgstr "Toutes les cartes sont occupées" msgctxt "#30061" msgid "Channel is scrambled" msgstr "La chaîne est brouillée" msgctxt "#30062" msgid "No video or audio detected" msgstr "Aucun vidéo ou audio détecté" msgctxt "#30063" msgid "No signal detected" msgstr "Aucun signal détecté" msgctxt "#30064" msgid "Unknown error" msgstr "Erreur inconnue" msgctxt "#30065" msgid "Unable to start graph" msgstr "Impossible de démarrer le graphe" msgctxt "#30066" msgid "Unknown channel" msgstr "Chaîne inconnue" msgctxt "#30067" msgid "No tuning details" msgstr "Aucun détail de syntonisation" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "La chaîne n’est mappée à aucune carte" msgctxt "#30069" msgid "Card is disabled" msgstr "La carte est désactivée" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Échec de connexion à l’esclave" msgctxt "#30071" msgid "Not the owner" msgstr "Pas le propriétaire" msgctxt "#30072" msgid "Graph building failed" msgstr "Échec de montage des graphes" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Encodeur SW manquant" msgctxt "#30074" msgid "No free disk space" msgstr "Aucun espace disque libre" msgctxt "#30075" msgid "No PMT found" msgstr "Aucun PMT n’a été trouvé" msgctxt "#30100" msgid "Schedule settings" msgstr "Paramètres de programmation" msgctxt "#30101" msgid "Frequency" msgstr "Fréquence" msgctxt "#30102" msgid "Airtime" msgstr "Heure de diffusion" msgctxt "#30103" msgid "Channels" msgstr "Chaînes" msgctxt "#30104" msgid "Keep" msgstr "Garder" msgctxt "#30105" msgid "Record minutes before start" msgstr "Enregistrer quelques minutes avant le début" msgctxt "#30106" msgid "Record minutes after end" msgstr "Enregistrer quelques minutes après la fin" msgctxt "#30110" msgid "Record Once" msgstr "Enregistrer une fois" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Enregistrer quotidiennement (ce programme)" msgctxt "#30112" msgid "Record Weekly" msgstr "Enregistrer hebdomadairement" msgctxt "#30113" msgid "Record Weekends" msgstr "Enregistrer les fins de semaine" msgctxt "#30114" msgid "Record Weekdays" msgstr "Enregistrer les jours de la semaine" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Enregistrer toutes les fois sur cette chaîne" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Enregistrer toutes les fois sur toutes les chaînes" msgctxt "#30117" msgid "Record every week at this time" msgstr "Enregistrer toutes les semaines à cette heure" msgctxt "#30118" msgid "Record every day at this time" msgstr "Enregistrer tous les jours à cette heure" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Enregistrer hebdomadairement sur cette chaîne" msgctxt "#30120" msgid "This time" msgstr "Cette heure" msgctxt "#30121" msgid "Anytime" msgstr "N’importe quand" msgctxt "#30122" msgid "Manual" msgstr "Manuel" msgctxt "#30125" msgid "This Channel" msgstr "Cette chaîne" msgctxt "#30126" msgid "Any Channel" msgstr "N’importe quelle chaîne" msgctxt "#30130" msgid "Until space needed" msgstr "Jusqu’à ce que l’espace soit requis" msgctxt "#30131" msgid "Until watched" msgstr "Jusqu’à ce qu’il soit visionné" msgctxt "#30132" msgid "Days" msgstr "Jours" msgctxt "#30133" msgid "Always" msgstr "Toujours" msgctxt "#30134" msgid "1 week" msgstr "1 semaine" msgctxt "#30135" msgid "Backend default" msgstr "Valeur par défaut de la dorsale" msgctxt "#30136" msgid "Kodi default" msgstr "Valeur par défaut de Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d semaines" msgctxt "#30138" msgid "1 month" msgstr "1 mois" msgctxt "#30139" msgid "%d months" msgstr "%d mois" msgctxt "#30140" msgid "1 year" msgstr "1 an" resource.language.fr_fr/000077500000000000000000000000001346756700600346765ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000154551346756700600367410ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.fr_fr# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: French (France) (http://www.transifex.com/projects/p/kodi-main/language/fr_FR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fr_FR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nom d'hôte Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Port de l'extension Mediaportal pour Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Chaînes gratuites uniquement" msgctxt "#30003" msgid "Include Radio" msgstr "Inclure la radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Commutation rapide de chaînes (ne stoppe pas le différé)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Temporisation de connexion (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importer seulement les chaînes TV du groupe" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importer seulement les chaînes radio du groupe" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convertir le nom d'hôte en adresse IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Guide TV : Lecture des genres (lent)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Temporisation après réglage de la chaîne (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Activer la fenêtre d'enregistrement des anciennes séries TV" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Durée prédéfinie de conservation des enregistrements" msgctxt "#30015" msgid "Streaming method" msgstr "Méthode de diffusion par flux" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Compte utilisateur Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Mot de passe Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Utiliser la diffusion par flux RTSP" msgctxt "#30040" msgid "Connection" msgstr "Connexion" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Lecture" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Cette version « %s » de TVServerKodi est obsolète. Merci de la mettre à niveau vers « %s » ou supérieur !" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Cette version de TVServerKodi est obsolète. Merci de la mettre à niveau vers « %s » ou supérieur !" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Échec en lecture de l'enregistrement. Effacer l'adresse URL ou le nom de fichier." msgctxt "#30060" msgid "All cards are busy" msgstr "Toutes les cartes sont occupées." msgctxt "#30061" msgid "Channel is scrambled" msgstr "La chaîne est cryptée." msgctxt "#30062" msgid "No video or audio detected" msgstr "Aucune vidéo ou audio détectée" msgctxt "#30063" msgid "No signal detected" msgstr "Aucun signal détecté" msgctxt "#30064" msgid "Unknown error" msgstr "Erreur inconnue" msgctxt "#30065" msgid "Unable to start graph" msgstr "Impossible de démarrer le chaînage" msgctxt "#30066" msgid "Unknown channel" msgstr "Chaîne inconnue" msgctxt "#30067" msgid "No tuning details" msgstr "Aucun détail de réglage" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "La chaîne n'est mappée sur aucune carte" msgctxt "#30069" msgid "Card is disabled" msgstr "Carte désactivée" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Connexion impossible au périphérique asservi" msgctxt "#30071" msgid "Not the owner" msgstr "N'est pas le propriétaire" msgctxt "#30072" msgid "Graph building failed" msgstr "Erreur de création du chaînage" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Encodeur SW manquant" msgctxt "#30074" msgid "No free disk space" msgstr "Plus assez d'espace disque libre" msgctxt "#30075" msgid "No PMT found" msgstr "Aucune table de programme trouvée" msgctxt "#30100" msgid "Schedule settings" msgstr "Paramètres de la programmation" msgctxt "#30101" msgid "Frequency" msgstr "Fréquence" msgctxt "#30102" msgid "Airtime" msgstr "Temps d'antenne" msgctxt "#30103" msgid "Channels" msgstr "Chaînes " msgctxt "#30104" msgid "Keep" msgstr "Conserver" msgctxt "#30105" msgid "Record minutes before start" msgstr "Minutes enregistrées avant le démarrage" msgctxt "#30106" msgid "Record minutes after end" msgstr "Minutes enregistrées après la fin" msgctxt "#30110" msgid "Record Once" msgstr "Enregistrer une seule fois" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Enregistrement journalier (ce programme)" msgctxt "#30112" msgid "Record Weekly" msgstr "Enregistrement hebdomadaire" msgctxt "#30113" msgid "Record Weekends" msgstr "Enregistrement les week-ends" msgctxt "#30114" msgid "Record Weekdays" msgstr "Enregistrer les jours de la semaine" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Enregistrer chaque fois sur cette chaîne" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Enregistrer chaque fois sur toutes les chaînes" msgctxt "#30117" msgid "Record every week at this time" msgstr "Enregistrer à ce moment toutes les semaines" msgctxt "#30118" msgid "Record every day at this time" msgstr "Enregistrer à ce moment tous les jours" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Enregistrer 1 fois par semaine sur cette chaîne" msgctxt "#30120" msgid "This time" msgstr "Cette fois-ci" msgctxt "#30121" msgid "Anytime" msgstr "À tout moment" msgctxt "#30122" msgid "Manual" msgstr "Manuel" msgctxt "#30125" msgid "This Channel" msgstr "Cette chaîne" msgctxt "#30126" msgid "Any Channel" msgstr "N'importe quelle chaîne" msgctxt "#30130" msgid "Until space needed" msgstr "Jusqu'à ce qu'il y ait besoin d'espace" msgctxt "#30131" msgid "Until watched" msgstr "Jusqu'à être vu" msgctxt "#30132" msgid "Days" msgstr "jours" msgctxt "#30133" msgid "Always" msgstr "Toujours" msgctxt "#30134" msgid "1 week" msgstr "1 semaine" msgctxt "#30135" msgid "Backend default" msgstr "Prédéfini par le serveur" msgctxt "#30136" msgid "Kodi default" msgstr "Prédéfini par Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d semaines" msgctxt "#30138" msgid "1 month" msgstr "1 mois" msgctxt "#30139" msgid "%d months" msgstr "%d mois" msgctxt "#30140" msgid "1 year" msgstr "1 an" resource.language.gl_es/000077500000000000000000000000001346756700600346715ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000150321346756700600367230ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.gl_es# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Galician (Spain) (http://www.transifex.com/projects/p/kodi-main/language/gl_ES/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: gl_ES\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nome do host de Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Extensión para o porto do Kodi de Mediaportal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Só emisións en aberto" msgctxt "#30003" msgid "Include Radio" msgstr "Incluír Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Troco rápido de canle (non deter o timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Tempo(s) de espera para conectar" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importar só os canles de TV dende o grupo" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importar só as canles de Radio dende o grupo" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convertir o nome de host ó enderezo IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Guía: ler as cadeas de xénero (lento)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Tempo de espera despois de sintonizar unha canle (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Habilitar dialogo de grabado de series vellas" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Tempo de vida predefinido das gravacións" msgctxt "#30015" msgid "Streaming method" msgstr "Metodo de transmisión" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Conta do usuario de Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Contrasinal de Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Usar transmisión RTSP" msgctxt "#30040" msgid "Connection" msgstr "Conexión" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Reprodución" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "A versión '%s' de TVServerKodi é demasiado vella, por favor actualiza á '%s' ou superior!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "A versión de TVServerKodi é demasiado vella, por favor actualiza á '%s' ou superior!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Fallou a gravación da reprodución, o URL do ficheiro está baleiro." msgctxt "#30060" msgid "All cards are busy" msgstr "Todas as tarxetas están ocupadas" msgctxt "#30061" msgid "Channel is scrambled" msgstr "A canle está codificada" msgctxt "#30062" msgid "No video or audio detected" msgstr "Non se atoparon vídeo ou son." msgctxt "#30063" msgid "No signal detected" msgstr "Non se detectou ningún sinal" msgctxt "#30064" msgid "Unknown error" msgstr "Erro descoñecido" msgctxt "#30065" msgid "Unable to start graph" msgstr "Non se puido iniciar o gráfico" msgctxt "#30066" msgid "Unknown channel" msgstr "Canle descoñecida" msgctxt "#30067" msgid "No tuning details" msgstr "Sen detalles da sintonización" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "A canle non está asignada a ningunha tarxeta" msgctxt "#30069" msgid "Card is disabled" msgstr "A tarxeta está deshabilitada" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Fallou a conexión co escravo" msgctxt "#30071" msgid "Not the owner" msgstr "Non é o propietario" msgctxt "#30072" msgid "Graph building failed" msgstr "Fallou a creación do gráfico" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Falta o codificador SW" msgctxt "#30074" msgid "No free disk space" msgstr "Sen espazo libre" msgctxt "#30075" msgid "No PMT found" msgstr "Non se atopou o PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "Axustes da programación" msgctxt "#30101" msgid "Frequency" msgstr "Frecuencia" msgctxt "#30102" msgid "Airtime" msgstr "Hora de emisión" msgctxt "#30103" msgid "Channels" msgstr "Canles" msgctxt "#30104" msgid "Keep" msgstr "Manter" msgctxt "#30105" msgid "Record minutes before start" msgstr "Minutos de gravación antes de comezar" msgctxt "#30106" msgid "Record minutes after end" msgstr "Minutos de gravación despois de rematar" msgctxt "#30110" msgid "Record Once" msgstr "Gravar unha vez" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Gravar diariamente (Este programa)" msgctxt "#30112" msgid "Record Weekly" msgstr "Gravar semanalmente" msgctxt "#30113" msgid "Record Weekends" msgstr "Gravar as fins de semana" msgctxt "#30114" msgid "Record Weekdays" msgstr "Días da Semana de Gravación" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Gravar tódalas veces nesta canle" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Gravar tódalas veces en tódalas canles" msgctxt "#30117" msgid "Record every week at this time" msgstr "Gravar cada semana a esta hora" msgctxt "#30118" msgid "Record every day at this time" msgstr "Gravar cada día a esta hora" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Gravar semanalmente nesta canle" msgctxt "#30120" msgid "This time" msgstr "A esta hora" msgctxt "#30121" msgid "Anytime" msgstr "Calquera hora" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "Esta Canle" msgctxt "#30126" msgid "Any Channel" msgstr "Calquera Canle" msgctxt "#30130" msgid "Until space needed" msgstr "Até que precise de espazo" msgctxt "#30131" msgid "Until watched" msgstr "Até que o vexa" msgctxt "#30132" msgid "Days" msgstr "Días" msgctxt "#30133" msgid "Always" msgstr "Sempre" msgctxt "#30134" msgid "1 week" msgstr "1 semana" msgctxt "#30135" msgid "Backend default" msgstr "Motor predefinido" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi predefinido" 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 ano" resource.language.he_il/000077500000000000000000000000001346756700600346605ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000155341346756700600367210ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.he_il# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Hebrew (Israel) (http://www.transifex.com/projects/p/kodi-main/language/he_IL/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: he_IL\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "שם מארח או כתובת IP" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "פורט" msgctxt "#30002" msgid "Free-to-air only" msgstr "ערוצי שידור חופשי בלבד" msgctxt "#30003" msgid "Include Radio" msgstr "כלול ערוצי רדיו" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "העברת ערוץ מהירה (ללא עצירת טיימשיפט)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "זמן מירבי לניסיון חיבור (שניות)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "יבא ערוצי טלוויזיה רק מקבוצת" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "יבא ערוצי רדיו רק מקבוצת" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "המרת שם מארח לכתובת IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "לוח שידורים: קרא מחרוזות סגנון (איטי)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "זמן המתנה לאחר העברת ערוץ (אלפית שנ')" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "אפשר דו-שיח הקלטת סדרות ישנות" msgctxt "#30012" msgid "Default recording lifetime" msgstr "אורך חיי הקלטה ברירת מחדל" msgctxt "#30015" msgid "Streaming method" msgstr "שיטת הזרמה" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "חשבון משתמש חלונות (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "סיסמת חלונות (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "השתמש בהזרמת RTSP" msgctxt "#30040" msgid "Connection" msgstr "חיבור" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "ניגון" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "גרסה '%s' של TVServerKodi ישנה מדי. יש לעדכן לגרסה '%s' ומעלה!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "גרסת TVServerKodi ישנה מדי. יש לעדכן לגרסה '%s' ומעלה!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "ניגון ההקלטה נכשל. כתובת הקובץ ריקה." msgctxt "#30060" msgid "All cards are busy" msgstr "כל המקלטים תפוסים" msgctxt "#30061" msgid "Channel is scrambled" msgstr "הערוץ מוצפן" msgctxt "#30062" msgid "No video or audio detected" msgstr "לא זוהה וידאו או שמע" msgctxt "#30063" msgid "No signal detected" msgstr "אין קליטה" msgctxt "#30064" msgid "Unknown error" msgstr "שגיאה לא מוכרת" msgctxt "#30065" msgid "Unable to start graph" msgstr "לא ניתן לפתוח תרשים" msgctxt "#30066" msgid "Unknown channel" msgstr "ערוץ לא מוכר" msgctxt "#30067" msgid "No tuning details" msgstr "אין נתוני קליטה" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "הערוץ לא ממופה לאף מקלט" msgctxt "#30069" msgid "Card is disabled" msgstr "המקלט מנוטרל" msgctxt "#30070" msgid "Connection to slave failed" msgstr "החיבור לשרת משני נכשל" msgctxt "#30071" msgid "Not the owner" msgstr "לא הבעלים" msgctxt "#30072" msgid "Graph building failed" msgstr "בניית תרשים נכשלה" msgctxt "#30073" msgid "SW Encoder missing" msgstr "מקודד תוכנה חסר" msgctxt "#30074" msgid "No free disk space" msgstr "אין מקום פנוי בדיסק הקשיח" msgctxt "#30075" msgid "No PMT found" msgstr "לא נמצא PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "הגדרות תזמונים" msgctxt "#30101" msgid "Frequency" msgstr "תדירות" msgctxt "#30102" msgid "Airtime" msgstr "זמן שידור" msgctxt "#30103" msgid "Channels" msgstr "ערוצים" msgctxt "#30104" 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 "בכל זמן" msgctxt "#30122" msgid "Manual" msgstr "ידני" msgctxt "#30125" msgid "This Channel" msgstr "הערוץ הזה" msgctxt "#30126" msgid "Any Channel" msgstr "כל ערוץ" msgctxt "#30130" msgid "Until space needed" msgstr "עד שנפח האחסון נדרש" msgctxt "#30131" msgid "Until watched" msgstr "עד שנצפה" msgctxt "#30132" msgid "Days" msgstr "ימים" msgctxt "#30133" msgid "Always" msgstr "תמיד" msgctxt "#30134" msgid "1 week" msgstr "שבוע יחיד" msgctxt "#30135" msgid "Backend default" msgstr "ברירת מחדל של שרת אחורי" msgctxt "#30136" msgid "Kodi default" msgstr "ברירת מחדל של Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d שבועות" msgctxt "#30138" msgid "1 month" msgstr "חודש אחד" msgctxt "#30139" msgid "%d months" msgstr "%d חודשים" msgctxt "#30140" msgid "1 year" msgstr "שנה אחת" resource.language.hi_in/000077500000000000000000000000001346756700600346665ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000014201346756700600367140ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.hi_in# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Hindi (India) (http://www.transifex.com/projects/p/kodi-main/language/hi_IN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: hi_IN\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30042" msgid "Playback" msgstr "प्लेबैक" msgctxt "#30122" msgid "Manual" msgstr "मैनुअल" resource.language.hr_hr/000077500000000000000000000000001346756700600347025ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000150041346756700600367330ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.hr_hr# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Croatian (Croatia) (http://www.transifex.com/projects/p/kodi-main/language/hr_HR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: hr_HR\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "MediaPortal naziv računala" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Ulaz Mediaportal Kodi dodatka" msgctxt "#30002" msgid "Free-to-air only" msgstr "Samo nekôdirani" msgctxt "#30003" msgid "Include Radio" msgstr "Uključi radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Brzo prebacivanje programa (ne zaustavljaj vremensko premotavanje)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Istek vremena povezivanja (sek)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Uvezi samo TV programe iz grupe" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Uvezi samo radijske programe iz grupe" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Pretvori naziv računala u IP adresu" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: čitaj slogove žanra (sporije)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Vrijeme čekanja nakon pronalaska programa (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Omogući dijalog snimanja starijih serija" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Zadano trajanje snimanja" msgctxt "#30015" msgid "Streaming method" msgstr "Način stremanja" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows korisnički račun (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows lozinka (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Koristi RTSP stremanje" msgctxt "#30040" msgid "Connection" msgstr "Povezivanje" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Reprodukcija" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Vaš Kodi TV poslužitelj inačice '%s' je zastario. Nadogradite ga na '%s' ili noviju inačicu!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Inačica vašeg Kodi TV poslužitelja je prestara. Nadogradite ga na '%s' ili noviju inačicu!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Reprodukcija snimke nije uspjela. Prazan URL naziva datoteke." msgctxt "#30060" msgid "All cards are busy" msgstr "Sve kartice su zauzete" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Program je kôdiran" msgctxt "#30062" msgid "No video or audio detected" msgstr "Slika ili zvuk nisu otkriveni" msgctxt "#30063" msgid "No signal detected" msgstr "Signal nije pronađen" msgctxt "#30064" msgid "Unknown error" msgstr "Nepoznata greška" msgctxt "#30065" msgid "Unable to start graph" msgstr "Nemoguće pokretanje grafa" msgctxt "#30066" msgid "Unknown channel" msgstr "Nepoznati program" msgctxt "#30067" msgid "No tuning details" msgstr "Nema pojedinosti o prijemu" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Program nije dodijeljen niti jednoj kartici" msgctxt "#30069" msgid "Card is disabled" msgstr "Kartica je onemogućena" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Povezivanje na alternativnu karticu nije uspjelo" msgctxt "#30071" msgid "Not the owner" msgstr "Nije vlasnik" msgctxt "#30072" msgid "Graph building failed" msgstr "Neuspjela izgradnja grafa" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Nedostaje softverski enkôder" msgctxt "#30074" msgid "No free disk space" msgstr "Nema slobodnog prostora na disku" msgctxt "#30075" msgid "No PMT found" msgstr "PMT nije pronađen" msgctxt "#30100" msgid "Schedule settings" msgstr "Postavke rasporeda" msgctxt "#30101" msgid "Frequency" msgstr "Frekvencija" msgctxt "#30102" msgid "Airtime" msgstr "Vrijeme emitiranja" msgctxt "#30103" msgid "Channels" msgstr "Programi" msgctxt "#30104" msgid "Keep" msgstr "Zadrži" msgctxt "#30105" msgid "Record minutes before start" msgstr "Dodatne minute prije početka snimanja" msgctxt "#30106" msgid "Record minutes after end" msgstr "Dodatne minute snimanja nakon završetka" msgctxt "#30110" msgid "Record Once" msgstr "Snimi jednom" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Dnevno snimaj (ovaj program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Tjedno snimaj" msgctxt "#30113" msgid "Record Weekends" msgstr "Snimaj vikendom" msgctxt "#30114" msgid "Record Weekdays" msgstr "Snimaj radnim danima" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Snimaj svaki puta na ovom programu" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Snimaj svaki puta na svakom programu" msgctxt "#30117" msgid "Record every week at this time" msgstr "Snimaj svaki tjedan u ovo vrijeme" msgctxt "#30118" msgid "Record every day at this time" msgstr "Snimaj svaki dan u ovo vrijeme" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Snimaj tjedno na ovom programu" msgctxt "#30120" msgid "This time" msgstr "Ovaj puta" msgctxt "#30121" msgid "Anytime" msgstr "Bilo koje vrijeme" msgctxt "#30122" msgid "Manual" msgstr "Ručno" msgctxt "#30125" msgid "This Channel" msgstr "Ovaj program" msgctxt "#30126" msgid "Any Channel" msgstr "Bilo koji program" msgctxt "#30130" msgid "Until space needed" msgstr "Dok ima slobodnog prostora" msgctxt "#30131" msgid "Until watched" msgstr "Dok se ne pogleda" msgctxt "#30132" msgid "Days" msgstr "Dana" msgctxt "#30133" msgid "Always" msgstr "Uvijek" msgctxt "#30134" msgid "1 week" msgstr "1 tjedan" msgctxt "#30135" msgid "Backend default" msgstr "Zadani pozadinski softver" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi zadan" msgctxt "#30137" msgid "%d weeks" msgstr "%d tjedna" msgctxt "#30138" msgid "1 month" msgstr "1 mjesec" msgctxt "#30139" msgid "%d months" msgstr "%d mjeseca" msgctxt "#30140" msgid "1 year" msgstr "1 godina" resource.language.hu_hu/000077500000000000000000000000001346756700600347105ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000151751346756700600367520ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.hu_hu# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Hungarian (Hungary) (http://www.transifex.com/projects/p/kodi-main/language/hu_HU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: hu_HU\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "MediaPortal Kiszolgálónév" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "MediaPortal Kodi kiegészítő port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Csak szabadon-fogható" msgctxt "#30003" msgid "Include Radio" msgstr "Tartalmaz rádiót" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Gyors csatornaváltás (nem szakítja meg az időeltolás 'Timeshift' funkció használatát)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "A kapcsolat időtúllépése (mp.)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "TV csatornák importálása csoportokba" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Rádió csatornák importálása csoportokba" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Kiszolgálónév konvertálása IP címre" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Olvassa a műfaj karakterláncokat (lassú)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Csatornahangolás utáni várakozási idő (milliszekundum)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "A régi sorozatfelvétel párbeszédablak engedélyezése" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Alapértelmezett felvétel élettartam" msgctxt "#30015" msgid "Streaming method" msgstr "Sugárzási módszer" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows felhasználói fiók (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows jelszó (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Használja az RTSP átvitelt" msgctxt "#30040" msgid "Connection" msgstr "Kapcsolat" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Lejátszás" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "A TVServerKodi verzió '%s' túl régi. Kérem frissítse a '%s' vagy újabbra!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "A TVServerKodi verziója túl régi. Kérem frissítse a '%s' vagy újabbra!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Felvétel lejátszása nem sikerült. Üres URL, vagy fájlnév." msgctxt "#30060" msgid "All cards are busy" msgstr "Minden kártyahely foglalt" msgctxt "#30061" msgid "Channel is scrambled" msgstr "A csatorna kódolva van" msgctxt "#30062" msgid "No video or audio detected" msgstr "Nincs észlelhető kép vagy hang" msgctxt "#30063" msgid "No signal detected" msgstr "Nincs jel" msgctxt "#30064" msgid "Unknown error" msgstr "Ismeretlen hiba" msgctxt "#30065" msgid "Unable to start graph" msgstr "Nem lehet elindítani a dekódolást" msgctxt "#30066" msgid "Unknown channel" msgstr "Ismeretlen csatorna" msgctxt "#30067" msgid "No tuning details" msgstr "Nincsenek hangolással kapcsolatos részletek" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "A csatorna nincs hozzárendelve semmilyen kártyához" msgctxt "#30069" msgid "Card is disabled" msgstr "A kártya le van tiltva" msgctxt "#30070" msgid "Connection to slave failed" msgstr "A csatlakozás nem sikerült" msgctxt "#30071" msgid "Not the owner" msgstr "Nem tulajdonos" msgctxt "#30072" msgid "Graph building failed" msgstr "Dekódoló felépítése nem sikerült" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Szoftveres kódoló hiányzik" msgctxt "#30074" msgid "No free disk space" msgstr "Nincs elég hely a lemezen" msgctxt "#30075" msgid "No PMT found" msgstr "Nem találtam programleképezési táblát" msgctxt "#30100" msgid "Schedule settings" msgstr "Ütemezési beállítások" msgctxt "#30101" msgid "Frequency" msgstr "Frekvencia" msgctxt "#30102" msgid "Airtime" msgstr "Adásidő" msgctxt "#30103" msgid "Channels" msgstr "Csatornák" msgctxt "#30104" msgid "Keep" msgstr "Megtart" msgctxt "#30105" msgid "Record minutes before start" msgstr "Hány perc kerüljön felvételre a kezdés előtt" msgctxt "#30106" msgid "Record minutes after end" msgstr "Hány perc kerüljön felvételre a befejezés után" msgctxt "#30110" msgid "Record Once" msgstr "Felvétel egyszer" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Napi felvétel (Ez a program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Heti felvétel" msgctxt "#30113" msgid "Record Weekends" msgstr "Hétvégi felvétel" msgctxt "#30114" msgid "Record Weekdays" msgstr "Hétköznapi felvétel" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Felvétel mindig ezen a csatornán" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Felvétel mindig, minden csatornán" msgctxt "#30117" msgid "Record every week at this time" msgstr "Felvétel minden héten ekkor" msgctxt "#30118" msgid "Record every day at this time" msgstr "Felvétel minden nap ekkor" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Felvétel hetente ezen a csatornán" msgctxt "#30120" msgid "This time" msgstr "Ebben az időben" msgctxt "#30121" msgid "Anytime" msgstr "Bármikor" msgctxt "#30122" msgid "Manual" msgstr "Kézi" msgctxt "#30125" msgid "This Channel" msgstr "Ez a csatorna" msgctxt "#30126" msgid "Any Channel" msgstr "Bármely Csatorna" msgctxt "#30130" msgid "Until space needed" msgstr "Amig van szabad hely" msgctxt "#30131" msgid "Until watched" msgstr "Amig megnézetlen" msgctxt "#30132" msgid "Days" msgstr "nap" msgctxt "#30133" msgid "Always" msgstr "Mindig" msgctxt "#30134" msgid "1 week" msgstr "1 hét" msgctxt "#30135" msgid "Backend default" msgstr "Háttérszolgáltatás alapértelmezett" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi alapértelmezett" msgctxt "#30137" msgid "%d weeks" msgstr "%d hét" msgctxt "#30138" msgid "1 month" msgstr "1 hónap" msgctxt "#30139" msgid "%d months" msgstr "%d hónap" msgctxt "#30140" msgid "1 year" msgstr "1 év" resource.language.hy_am/000077500000000000000000000000001346756700600346755ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000016021346756700600367250ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.hy_am# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Armenian (Armenia) (http://www.transifex.com/projects/p/kodi-main/language/hy_AM/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: hy_AM\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30003" msgid "Include Radio" msgstr "Ռադիոն ներառյալ" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Միացման վերջնաժամկետը (վ)" msgctxt "#30122" msgid "Manual" msgstr "Ձեռքով" resource.language.id_id/000077500000000000000000000000001346756700600346505ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000124461346756700600367100ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.id_id# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Indonesian (Indonesia) (http://www.transifex.com/projects/p/kodi-main/language/id_ID/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: id_ID\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Hostname Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Port plugin Mediaportal Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Siaran yang gratis saja." msgctxt "#30003" msgid "Include Radio" msgstr "Mencakup Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Pemindahan kanal secara cepat (Jangan hentikan timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Timeout koneksi (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Hanya impor kanal TV dari group" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Hanya impor kanal radio dari group" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Rubah hostname menjadi alamat IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Baca informasi genre (lambat)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Waktu tunggu setelah setel sebuah kanal (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Lama perekaman default" msgctxt "#30015" msgid "Streaming method" msgstr "Metode aliran" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Akun pengguna WIndows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Kata sandi Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Gunakan pengaliran RTSP" msgctxt "#30040" msgid "Connection" msgstr "Koneksi" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Playback" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi anda dengan versi '%s' terlalu tua. Silahkan upgrade ke versi '%s' atau di atasnya!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Versi TVServerKodi anda terlalu tua. Silahkan upgrade ke versi '%s' atau di atasnya!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Pemutaran rekaman gagal. URL berkas kosong." msgctxt "#30060" msgid "All cards are busy" msgstr "Semua kartu sedang sibuk." msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanal diacak" msgctxt "#30062" msgid "No video or audio detected" msgstr "Tidak ada video atau audio terdeteksi" msgctxt "#30063" msgid "No signal detected" msgstr "Tidak ada sinyal terdeteksi" msgctxt "#30064" msgid "Unknown error" msgstr "Error tidak dikenal" msgctxt "#30065" msgid "Unable to start graph" msgstr "Tidak dapat memulai grafis" msgctxt "#30066" msgid "Unknown channel" msgstr "Kanal tidak dikenal" msgctxt "#30067" msgid "No tuning details" msgstr "Tidak ada detil penyetelan" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanal tidak terpetakan ke satu kartupun" msgctxt "#30069" msgid "Card is disabled" msgstr "Kartu dinon-aktifkan" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Koneksi ke slave gagal." msgctxt "#30071" msgid "Not the owner" msgstr "Bukan pemilik" msgctxt "#30072" msgid "Graph building failed" msgstr "Pembangunan grafis gagal" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW Encoder tidak ditemukan" msgctxt "#30074" msgid "No free disk space" msgstr "Tidak ada spasi disk kosong" msgctxt "#30075" msgid "No PMT found" msgstr "PMT tidak ditemukan" msgctxt "#30100" msgid "Schedule settings" msgstr "Pengaturan jadwal" msgctxt "#30101" msgid "Frequency" msgstr "Frekuensi" msgctxt "#30102" msgid "Airtime" msgstr "Waktu siar" msgctxt "#30103" msgid "Channels" msgstr "Saluran" msgctxt "#30104" msgid "Keep" msgstr "Pertahankan" msgctxt "#30105" msgid "Record minutes before start" msgstr "Waktu rekam sebelum mulai" msgctxt "#30106" msgid "Record minutes after end" msgstr "Waktu rekam setelah selesai" msgctxt "#30110" msgid "Record Once" msgstr "Rekam sekali" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Rekam harian (program ini)" msgctxt "#30112" msgid "Record Weekly" msgstr "Rekam mingguan" msgctxt "#30113" msgid "Record Weekends" msgstr "Rekam akhir minggu" msgctxt "#30120" msgid "This time" msgstr "Saat ini" msgctxt "#30121" msgid "Anytime" msgstr "Kapan saja" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "Kanal ini" msgctxt "#30126" msgid "Any Channel" msgstr "Kanal mana saja" msgctxt "#30130" msgid "Until space needed" msgstr "Sampai ruang yang dibutuhkan" msgctxt "#30132" msgid "Days" msgstr "Hari" msgctxt "#30133" msgid "Always" msgstr "Selalu" msgctxt "#30135" msgid "Backend default" msgstr "Backend bawaan" msgctxt "#30136" msgid "Kodi default" msgstr "Bawaan Kodi" resource.language.is_is/000077500000000000000000000000001346756700600347065ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000147221346756700600367450ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.is_is# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Icelandic (Iceland) (http://www.transifex.com/projects/p/kodi-main/language/is_IS/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: is_IS\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Hýsingarnafn Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Gátt fyrir Mediaportal Kodi viðbót" msgctxt "#30002" msgid "Free-to-air only" msgstr "Aðeins Fríar útsendingar" msgctxt "#30003" msgid "Include Radio" msgstr "Hafa úrvarp með" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Hröð skipting á milli rása (Stöðvar ekki tímahliðrun)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Tengitími útrennur (ms)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Flytja aðeins inn Sjónvarpsrásir frá hópi" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Flytja aðeins inn Útvarpsrásir frá hópi" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Breyta Hýsingarnafni í IP-vistfang" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Rafrænir sjónvarpsvísar (EPG): Lesa flokkunarstreng (hægvirkt)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Biðtími eftir rásaval (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Virkja glugga um upptökur á eldri þáttaröðum" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Sjálfgefinn líftími upptöku" msgctxt "#30015" msgid "Streaming method" msgstr "Aðferð við streymingu" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows notandanafn (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows lykilorð (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Nota RTSP-streymingu" msgctxt "#30040" msgid "Connection" msgstr "Tenging" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Afspilun" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi útgáfa '%s' er of gömul. Vinsamlega uppfærðu í '%s' eða nýrra!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi útgáfan er of gömul. Vinsamlega uppfærðu í '%s' eða nýrra!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Afskilun upptöku mistókst. Tómt netfang á skráarnafn. " msgctxt "#30060" msgid "All cards are busy" msgstr "Öll kort eru upptekin" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Rás er rugluð" msgctxt "#30062" msgid "No video or audio detected" msgstr "Finnst hvorki hljóð né mynd." msgctxt "#30063" msgid "No signal detected" msgstr "Ekkert merki fannst" msgctxt "#30064" msgid "Unknown error" msgstr "Óþekkt villa" msgctxt "#30065" msgid "Unable to start graph" msgstr "Gat ekki ræst graf" msgctxt "#30066" msgid "Unknown channel" msgstr "Óþekkt rás" msgctxt "#30067" msgid "No tuning details" msgstr "Engar upplýsingar um rásastillingu" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Rás er ekki möppuð við neitt kort" msgctxt "#30069" msgid "Card is disabled" msgstr "Kort er óvirkt" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Tenging við þjón mistókst" msgctxt "#30071" msgid "Not the owner" msgstr "Ekki eigandi" msgctxt "#30072" msgid "Graph building failed" msgstr "Ekki tókst að byggja graf" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Vantar hugbúnaðarkóðara" msgctxt "#30074" msgid "No free disk space" msgstr "Ekkert diskapláss" msgctxt "#30075" msgid "No PMT found" msgstr "Engin PMT fannst" msgctxt "#30100" msgid "Schedule settings" msgstr "Stillingar upptökuplans" msgctxt "#30101" msgid "Frequency" msgstr "Tíðni" msgctxt "#30102" msgid "Airtime" msgstr "Sýnt" msgctxt "#30103" msgid "Channels" msgstr "Rásir" msgctxt "#30104" msgid "Keep" msgstr "Halda" msgctxt "#30105" msgid "Record minutes before start" msgstr "Taka upp mínútur fyrir upphaf" msgctxt "#30106" msgid "Record minutes after end" msgstr "Taka upp mínútur eftir enda" msgctxt "#30110" msgid "Record Once" msgstr "Taka upp einu sinni" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Dagleg upptaka (þennan dagskrárlið)" msgctxt "#30112" msgid "Record Weekly" msgstr "Taka upp vikulega" msgctxt "#30113" msgid "Record Weekends" msgstr "Taka upp um helgar" msgctxt "#30114" msgid "Record Weekdays" msgstr "Taka upp á virkum dögum" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Taka upp í hvert skipti á þessari rás" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Taka upp í hvert skipti á öllum rásum" msgctxt "#30117" msgid "Record every week at this time" msgstr "Taka upp í hverri viku á þessum tíma" msgctxt "#30118" msgid "Record every day at this time" msgstr "Taka upp daglega á þessum tíma" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Taka upp vikulega á þessari rás" msgctxt "#30120" msgid "This time" msgstr "Í þetta skipti" msgctxt "#30121" msgid "Anytime" msgstr "Hvenær sem er" msgctxt "#30122" msgid "Manual" msgstr "Handvirkt" msgctxt "#30125" msgid "This Channel" msgstr "Á þessari rás" msgctxt "#30126" msgid "Any Channel" msgstr "Allar Rásir" msgctxt "#30130" msgid "Until space needed" msgstr "Þangað til vantar pláss" msgctxt "#30131" msgid "Until watched" msgstr "Þar til búið er að horfa á" msgctxt "#30132" msgid "Days" msgstr "Dagar" msgctxt "#30133" msgid "Always" msgstr "Alltaf" msgctxt "#30134" msgid "1 week" msgstr "1 vika" msgctxt "#30135" msgid "Backend default" msgstr "Sjálfgefið fyrir bakenda" msgctxt "#30136" msgid "Kodi default" msgstr "Sjálfgefið fyrir Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d vikur" msgctxt "#30138" msgid "1 month" msgstr "1 mánuður" msgctxt "#30139" msgid "%d months" msgstr "%d mánuðir" msgctxt "#30140" msgid "1 year" msgstr "1 ár" resource.language.it_it/000077500000000000000000000000001346756700600347105ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000150641346756700600367470ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.it_it# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Italian (Italy) (http://www.transifex.com/projects/p/kodi-main/language/it_IT/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: it_IT\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nome del server Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Porta del plugin Mediaportal di Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Solo free-to-air" msgctxt "#30003" msgid "Include Radio" msgstr "Includi Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Cambio veloce canale (non ferma il timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Timeout per la connessione (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importa solo canali TV dal gruppo" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importa solo canali radio dal gruppo" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Converti il nome del server nell'indirizzo IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Leggi le stringhe dei generi (lento)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Tempo di attesa dopo il tuning di un canale (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Abilita la richiesta di registrazione delle vecchie serie" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Durata predefinita delle registrazioni" msgctxt "#30015" msgid "Streaming method" msgstr "Metodologia di streaming" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Account utente Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Password Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Usa streaming RTSP" msgctxt "#30040" msgid "Connection" msgstr "Connessione" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Riproduzione" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "La tua versione di TVServerKodi '%s' è troppo vecchia. Per favore, aggiornala a '%s' o superiore!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "La tua versione di TVServerKodi è troppo vecchia. Per favore, aggiornala a '%s' o superiore!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "La riproduzione della registrazione non è riuscita. URL o nome del file vuoti." msgctxt "#30060" msgid "All cards are busy" msgstr "Tutte le card sono occupate" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Il canale è criptato" msgctxt "#30062" msgid "No video or audio detected" msgstr "Nessun video o audio trovato" msgctxt "#30063" msgid "No signal detected" msgstr "Nessun segnale trovato" msgctxt "#30064" msgid "Unknown error" msgstr "Errore sconosciuto" msgctxt "#30065" msgid "Unable to start graph" msgstr "Impossibile inizializzare grafico" msgctxt "#30066" msgid "Unknown channel" msgstr "Canale sconosciuto" msgctxt "#30067" msgid "No tuning details" msgstr "Nessun dettaglio di sintonizzazione" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Il canale non è mappata in nessuna card" msgctxt "#30069" msgid "Card is disabled" msgstr "La card è disabilitata" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Connessione allo slave fallita" msgctxt "#30071" msgid "Not the owner" msgstr "Non sei il proprietario" msgctxt "#30072" msgid "Graph building failed" msgstr "Costruzione del grafico fallita" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Encoder SW mancante" msgctxt "#30074" msgid "No free disk space" msgstr "Spazio disco esaurito" msgctxt "#30075" msgid "No PMT found" msgstr "Nessun PMT trovato" msgctxt "#30100" msgid "Schedule settings" msgstr "Impostazioni di programmazione" msgctxt "#30101" msgid "Frequency" msgstr "Frequenza" msgctxt "#30102" msgid "Airtime" msgstr "Ora di trasmissione" msgctxt "#30103" msgid "Channels" msgstr "Canali" msgctxt "#30104" msgid "Keep" msgstr "Mantieni" msgctxt "#30105" msgid "Record minutes before start" msgstr "Registra minuti prima dell'inizio" msgctxt "#30106" msgid "Record minutes after end" msgstr "Registra minuti dopo la fine" msgctxt "#30110" msgid "Record Once" msgstr "Registra una volta" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Registra giornalmente (questo programma)" msgctxt "#30112" msgid "Record Weekly" msgstr "Registra settimanalmente" msgctxt "#30113" msgid "Record Weekends" msgstr "Registra nei weekend" msgctxt "#30114" msgid "Record Weekdays" msgstr "Registra nei giorni lavorativi" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Registra ogni volta su questo canale" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Registra ogni volta su ogni canale" msgctxt "#30117" msgid "Record every week at this time" msgstr "Registra ogni settimana in questo orario" msgctxt "#30118" msgid "Record every day at this time" msgstr "Registra ogni giorno in questo orario" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Registra settimanalmente su questo canale" msgctxt "#30120" msgid "This time" msgstr "Questa volta" msgctxt "#30121" msgid "Anytime" msgstr "Qualunque ora" msgctxt "#30122" msgid "Manual" msgstr "Manuale" msgctxt "#30125" msgid "This Channel" msgstr "Questo Canale" msgctxt "#30126" msgid "Any Channel" msgstr "Qualunque canale" msgctxt "#30130" msgid "Until space needed" msgstr "Fino a spazio necessario" msgctxt "#30131" msgid "Until watched" msgstr "Fino a che viene guardato" msgctxt "#30132" msgid "Days" msgstr "Giorni" msgctxt "#30133" msgid "Always" msgstr "Sempre" msgctxt "#30134" msgid "1 week" msgstr "una settimana" msgctxt "#30135" msgid "Backend default" msgstr "Predefinito del backend" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi default" msgctxt "#30137" msgid "%d weeks" msgstr "%d settimane" msgctxt "#30138" msgid "1 month" msgstr "un mese" msgctxt "#30139" msgid "%d months" msgstr "%d mesi" msgctxt "#30140" msgid "1 year" msgstr "un anno" resource.language.ja_jp/000077500000000000000000000000001346756700600346635ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000136421346756700600367220ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.ja_jp# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Japanese (Japan) (http://www.transifex.com/projects/p/kodi-main/language/ja_JP/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ja_JP\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Medialportal ホスト名" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi プラグインのポート番号" msgctxt "#30002" msgid "Free-to-air only" msgstr "Free-to-air のみ" msgctxt "#30003" msgid "Include Radio" msgstr "ラジオも含める" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "高速チャンネルスイッチ(タイムシフトは止めない)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "接続タイムアウト (秒)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "グループから TV チャンネルのみインポート" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "グループからラジオチャンネルのみインポート" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "ホスト名を IP アドレスに変換" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: ジャンル名を読み込む (低速)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "チャンネルをあわせたあとの待ち時間 (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "規定の録画期間" msgctxt "#30015" msgid "Streaming method" msgstr "ストリーミング方式" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows ユーザーアカウント (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows パスワード (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "RTSP ストリーミングを使用" msgctxt "#30040" msgid "Connection" msgstr "接続" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "再生" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi バージョン '%s' は古すぎます。'%s' かそれ以上のバージョンにアップグレードしてください!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi バージョンが古すぎます。'%s' かそれ以上のバージョンにアップグレードしてください!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "録画の再生に失敗しました。ファイル名の URL が空です。" msgctxt "#30060" msgid "All cards are busy" msgstr "全カードが使用中です" msgctxt "#30061" msgid "Channel is scrambled" msgstr "チャンネルがスクランブル化されています" msgctxt "#30062" msgid "No video or audio detected" msgstr "映像や音声が検出されませんでした" msgctxt "#30063" msgid "No signal detected" msgstr "信号が検出されませんでした" msgctxt "#30064" msgid "Unknown error" msgstr "不明なエラー" msgctxt "#30065" msgid "Unable to start graph" msgstr "グラフを開始できません" msgctxt "#30066" msgid "Unknown channel" msgstr "不明なチャンネル" msgctxt "#30067" msgid "No tuning details" msgstr "チューニング情報がありません" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "チャンネルがどのカードにも割り当てられていません" msgctxt "#30069" msgid "Card is disabled" msgstr "カードが無効になっています" msgctxt "#30070" msgid "Connection to slave failed" msgstr "スレーブへの接続に失敗" msgctxt "#30071" msgid "Not the owner" msgstr "オーナーではありません" msgctxt "#30072" msgid "Graph building failed" msgstr "グラフ作成に失敗" msgctxt "#30073" msgid "SW Encoder missing" msgstr "ソフトウェアエンコーダーがありません" msgctxt "#30074" msgid "No free disk space" msgstr "ディスクの空き領域がありません" msgctxt "#30075" msgid "No PMT found" msgstr "PMT がみつかりません" msgctxt "#30100" msgid "Schedule settings" msgstr "スケジュール設定" msgctxt "#30101" msgid "Frequency" msgstr "周波数" msgctxt "#30102" msgid "Airtime" msgstr "放送時刻" msgctxt "#30103" msgid "Channels" msgstr "チャンネル" msgctxt "#30104" 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 "#30120" msgid "This time" msgstr "この時間" msgctxt "#30121" msgid "Anytime" msgstr "いつでも" msgctxt "#30122" msgid "Manual" msgstr "マニュアル" msgctxt "#30125" msgid "This Channel" msgstr "このチャンネル" msgctxt "#30126" msgid "Any Channel" msgstr "すべてのチャンネル" msgctxt "#30130" msgid "Until space needed" msgstr "容量がいっぱいになるまで" msgctxt "#30132" msgid "Days" msgstr "日" msgctxt "#30133" msgid "Always" msgstr "いつも" msgctxt "#30135" msgid "Backend default" msgstr "バックエンドの規定値" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi の規定値" resource.language.ko_kr/000077500000000000000000000000001346756700600347055ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000145431346756700600367450ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.ko_kr# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Korean (Korea) (http://www.transifex.com/projects/p/kodi-main/language/ko_KR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ko_KR\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal 호스트네임" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi 플러그인 포트" msgctxt "#30002" msgid "Free-to-air only" msgstr "무료채널만" msgctxt "#30003" msgid "Include Radio" msgstr "라디오 포함" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "빠른 채널 전환 (타임시프트 중지 안 함)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "연결시간 제한 (초)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "그룹에서 TV 채널만 가져 옴" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "그룹에서 라디오 채널만 가져 옴" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "호스트네임을 IP 주소로 변환" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: 장르 가져오기 (느림)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "채널 튜닝 후 대기시간 (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "오래된 시리즈 대화 녹화 활성화" msgctxt "#30012" msgid "Default recording lifetime" msgstr "기본 녹화 보존 기간" msgctxt "#30015" msgid "Streaming method" msgstr "스트리밍 방식" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows 사용자 계정 (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows 비밀번호 (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "RTSP 스트리밍 사용" msgctxt "#30040" msgid "Connection" msgstr "연결" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "재생" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi '%s' 의 버전이 너무 낮습니다. '%s' 이상의 버전으로 업그레이드 하십시오!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi의 버전이 너무 낮습니다. '%s' 이상의 버전으로 업그레이드 하십시오!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "재생 녹화 실패. 파일 이름에서 URL을 삭제하십시오." msgctxt "#30060" msgid "All cards are busy" msgstr "모든 카드가 사용중입니다" msgctxt "#30061" msgid "Channel is scrambled" msgstr "스크램블 채널입니다" msgctxt "#30062" msgid "No video or audio detected" msgstr "비디오 및 오디오 감지 안됨" msgctxt "#30063" msgid "No signal detected" msgstr "신호 없음" msgctxt "#30064" msgid "Unknown error" msgstr "알 수 없는 오류" msgctxt "#30065" msgid "Unable to start graph" msgstr "그래프 시작할 수 없음" msgctxt "#30066" msgid "Unknown channel" msgstr "알 수 없는 채널!" msgctxt "#30067" msgid "No tuning details" msgstr "상세 튜닝 없음" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "채널에 할당된 카드 없음" msgctxt "#30069" msgid "Card is disabled" msgstr "카드 사용 안 함" msgctxt "#30070" msgid "Connection to slave failed" msgstr "슬레이브 연결 실패" msgctxt "#30071" msgid "Not the owner" msgstr "소유자 아님" msgctxt "#30072" msgid "Graph building failed" msgstr "그래프 생성 실패" msgctxt "#30073" msgid "SW Encoder missing" msgstr "소프트웨어 인코더 없음" msgctxt "#30074" msgid "No free disk space" msgstr "디스크 빈 공간 없음" msgctxt "#30075" msgid "No PMT found" msgstr "PMT 없음" msgctxt "#30100" msgid "Schedule settings" msgstr "예약 설정" msgctxt "#30101" msgid "Frequency" msgstr "주파수" msgctxt "#30102" msgid "Airtime" msgstr "방영시간" msgctxt "#30103" msgid "Channels" msgstr "채널" msgctxt "#30104" 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 "항상" msgctxt "#30122" msgid "Manual" msgstr "수동" msgctxt "#30125" msgid "This Channel" msgstr "이 채널만" msgctxt "#30126" msgid "Any Channel" msgstr "모든 채널" msgctxt "#30130" msgid "Until space needed" msgstr "저장공간이 더 필요할 때까지" msgctxt "#30131" msgid "Until watched" msgstr "시청할 때까지" msgctxt "#30132" msgid "Days" msgstr "일" msgctxt "#30133" msgid "Always" msgstr "항상" msgctxt "#30134" msgid "1 week" msgstr "1주" msgctxt "#30135" msgid "Backend default" msgstr "백엔드 기본" 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년" resource.language.lt_lt/000077500000000000000000000000001346756700600347165ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000151321346756700600367510ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.lt_lt# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Lithuanian (Lithuania) (http://www.transifex.com/projects/p/kodi-main/language/lt_LT/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: lt_LT\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal kompiuterio vardas" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi įskiepio prievadas" msgctxt "#30002" msgid "Free-to-air only" msgstr "Tik laisvai transliuojami" msgctxt "#30003" msgid "Include Radio" msgstr "Įtraukti radiją" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Greitas kanalų perjungimas (nenutraukite laiko poslinkio)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Prisijungimui skirtas laikas (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importuoti tik TV kanalus iš grupės" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importuoti tik radijo kanalus iš grupės" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Konvertuoti kompiuterio vardą į IP adresą" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Skaityti žanrų eilutes (lėtai)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Laukimo laikas po kanalo nustatymo (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Įjungti seną serialų įrašymo dialogą" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Numatytasis įrašo saugojimo laikas" msgctxt "#30015" msgid "Streaming method" msgstr "Transliacijos būdas" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows vartotojo paskyra (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows slaptažodis (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Naudoti RTSP transliaciją" msgctxt "#30040" msgid "Connection" msgstr "Prisijungimas" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Atkūrimas" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Jūsų TVServerKodi versija '%s' yra per sena. Prašome atnaujinti į '%s' arba naujesnę!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Jūsų TVServerKodi versija yra per sena. Prašome atnaujinti į '%s' arba naujesnę!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Įrašo atkūrimas nepavyko. Tuščias URL arba failo vardas." msgctxt "#30060" msgid "All cards are busy" msgstr "Visos kortelės yra užimtos" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanalas yra nestabilus" msgctxt "#30062" msgid "No video or audio detected" msgstr "Vaizdas ir garsas neaptiktas" msgctxt "#30063" msgid "No signal detected" msgstr "Signalas neaptiktas" msgctxt "#30064" msgid "Unknown error" msgstr "Nežinoma klaida" msgctxt "#30065" msgid "Unable to start graph" msgstr "Nepavyksta pradėti grafikos" msgctxt "#30066" msgid "Unknown channel" msgstr "Nežinomas kanalas" msgctxt "#30067" msgid "No tuning details" msgstr "Nėra derinimo informacijos" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanalas nesusietas su jokia kortele" msgctxt "#30069" msgid "Card is disabled" msgstr "Kortelė atjungta" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Prijungimas prie 'slave' nepavyko" msgctxt "#30071" msgid "Not the owner" msgstr "Nesate savininkas" msgctxt "#30072" msgid "Graph building failed" msgstr "Grafiko sudarymas nepavyko" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Trūksta SW koduotuvo" msgctxt "#30074" msgid "No free disk space" msgstr "Nėra laisvos vietos diske" msgctxt "#30075" msgid "No PMT found" msgstr "PMT nerastas" msgctxt "#30100" msgid "Schedule settings" msgstr "Tvarkaraščio nustatymai" msgctxt "#30101" msgid "Frequency" msgstr "Dažnis" msgctxt "#30102" msgid "Airtime" msgstr "Transliacijos laikas" msgctxt "#30103" msgid "Channels" msgstr "Kanalai" msgctxt "#30104" msgid "Keep" msgstr "Išlaikyti" msgctxt "#30105" msgid "Record minutes before start" msgstr "Įrašyti minutes prieš pradžią" msgctxt "#30106" msgid "Record minutes after end" msgstr "Įrašyti minutes po pabaigos" msgctxt "#30110" msgid "Record Once" msgstr "Įrašyti tik kartą" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Įrašyti kasdien (šią programą)" msgctxt "#30112" msgid "Record Weekly" msgstr "Įrašyti kas savaitę" msgctxt "#30113" msgid "Record Weekends" msgstr "Įrašyti savaitgaliais" msgctxt "#30114" msgid "Record Weekdays" msgstr "Įrašyti darbo dienomis" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Įrašyti kiekvieną kartą šiame kanale" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Įrašyti kiekvieną kartą kiekviename kanale" msgctxt "#30117" msgid "Record every week at this time" msgstr "Įrašyti kiekvieną savaitę šiuo metu" msgctxt "#30118" msgid "Record every day at this time" msgstr "Įrašyti kiekvieną dieną šiuo metu" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Įrašyti kas savaitę šiame kanale" msgctxt "#30120" msgid "This time" msgstr "Šiuo metu" msgctxt "#30121" msgid "Anytime" msgstr "Bet kuriuo metu" msgctxt "#30122" msgid "Manual" msgstr "Rankiniu būdu" msgctxt "#30125" msgid "This Channel" msgstr "Šis kanalas" msgctxt "#30126" msgid "Any Channel" msgstr "Bet kuris kanalas" msgctxt "#30130" msgid "Until space needed" msgstr "Kol reikalinga vieta" msgctxt "#30131" msgid "Until watched" msgstr "Kol bus peržiūrėta" msgctxt "#30132" msgid "Days" msgstr "Dienos" msgctxt "#30133" msgid "Always" msgstr "Visada" msgctxt "#30134" msgid "1 week" msgstr "1 savaitė" msgctxt "#30135" msgid "Backend default" msgstr "Kaip numatyta posistemėje" msgctxt "#30136" msgid "Kodi default" msgstr "Kaip numatyta Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d savaitės" msgctxt "#30138" msgid "1 month" msgstr "1 mėnuo" msgctxt "#30139" msgid "%d months" msgstr "%d mėnesiai" msgctxt "#30140" msgid "1 year" msgstr "1 metai" resource.language.lv_lv/000077500000000000000000000000001346756700600347225ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000127641346756700600367650ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.lv_lv# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Latvian (Latvia) (http://www.transifex.com/projects/p/kodi-main/language/lv_LV/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: lv_LV\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal saimniekvārds" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi spraudņa ports" msgctxt "#30002" msgid "Free-to-air only" msgstr "Tikai bezmaksas pārraides" msgctxt "#30003" msgid "Include Radio" msgstr "Iekļaut radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Ātrā kanāu pārslēgšana (neaptur laika nobīdi)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Savienojuma noilgums" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importēt tikai TV kanālus no grupas" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importēt tikai radio kanālus no grupas" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Pārvērst saimniekvārdu uz IP adresi" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Lasa žanru virknes (lēns)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Gaidīšanas laiks pēc kanāla uztveršanas (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Noklusētais ieraksta kalpošanas laiks" msgctxt "#30015" msgid "Streaming method" msgstr "Straumēšanas metode" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows lietotāja konts (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows parole (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Izmantot RTSP straumēšanu" msgctxt "#30040" msgid "Connection" msgstr "Pieslēgums" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Atskaņošana" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Jūsu TVServerKodi versija '%s' ir pārāk veca. Lūdzu, uzlabojiet to uz '%s' vai jaunāku!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Jūsu TVServerKodi versija ir pārāk veca. Lūdzu, uzlabojiet to uz '%s' vai jaunāku!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Ieraksta atskaņošana neizdevās. Tuksšs URL vai faila nosaukums." msgctxt "#30060" msgid "All cards are busy" msgstr "Visas kartes ir aizņemtas" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanāls ir šifrēts" msgctxt "#30062" msgid "No video or audio detected" msgstr "Video un audio signāls nav atrasts" msgctxt "#30063" msgid "No signal detected" msgstr "Signāls nav atrasts" msgctxt "#30064" msgid "Unknown error" msgstr "Nezināma kļūda" msgctxt "#30065" msgid "Unable to start graph" msgstr "Nevar sākt grafiku" msgctxt "#30066" msgid "Unknown channel" msgstr "Nezināms kanāls" msgctxt "#30067" msgid "No tuning details" msgstr "Nav uztveršanas detaļas" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanāls nav piekartēts nevienai kartei." msgctxt "#30069" msgid "Card is disabled" msgstr "Karte ir izslēgta" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Neizdevās pieslēgties sekotājam" msgctxt "#30071" msgid "Not the owner" msgstr "Nav īpašnieks" msgctxt "#30072" msgid "Graph building failed" msgstr "Grafika būvēšana neizdevās" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Trūkst SW kodētājs" msgctxt "#30074" msgid "No free disk space" msgstr "Diskā nav brīvas vietas" msgctxt "#30075" msgid "No PMT found" msgstr "PMT nav atrasts" msgctxt "#30100" msgid "Schedule settings" msgstr "Plānotāja iestatījumi" msgctxt "#30101" msgid "Frequency" msgstr "Frekvence" msgctxt "#30102" msgid "Airtime" msgstr "Raidlaiks" msgctxt "#30103" msgid "Channels" msgstr "Kanāli" msgctxt "#30104" msgid "Keep" msgstr "Paturēt" msgctxt "#30105" msgid "Record minutes before start" msgstr "Ieraksta minūtes pirms sākuma" msgctxt "#30106" msgid "Record minutes after end" msgstr "Ieraksta minūtes pirms beigām" msgctxt "#30110" msgid "Record Once" msgstr "Ierakstīt vienreiz" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Ierakstīt katru dienu (šo programmu)" msgctxt "#30112" msgid "Record Weekly" msgstr "Ierakstīt katru nedēļu" msgctxt "#30113" msgid "Record Weekends" msgstr "Ierakstīt nedēļas nogalēs" msgctxt "#30120" msgid "This time" msgstr "Šoreiz" msgctxt "#30121" msgid "Anytime" msgstr "Jebkurā laikā" msgctxt "#30122" msgid "Manual" msgstr "Pašrocīgi" msgctxt "#30125" msgid "This Channel" msgstr "Šis kanāls" msgctxt "#30126" msgid "Any Channel" msgstr "Jebkurš kanāls" msgctxt "#30130" msgid "Until space needed" msgstr "Līdz nepieciešama vieta" msgctxt "#30132" msgid "Days" msgstr "Dienas" msgctxt "#30133" msgid "Always" msgstr "Vienmēr" msgctxt "#30135" msgid "Backend default" msgstr "Galasistēmas noklusējums" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi noklusējums" resource.language.mi/000077500000000000000000000000001346756700600342055ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000020131346756700600362320ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.mi# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Maori (http://www.transifex.com/projects/p/kodi-main/language/mi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: mi\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Tuhi taunoa ora" msgctxt "#30042" msgid "Playback" msgstr "Pūrei anō" msgctxt "#30103" msgid "Channels" msgstr "Ngā hongere" msgctxt "#30104" msgid "Keep" msgstr "Penapena" msgctxt "#30122" msgid "Manual" msgstr "pukawhakaatu" msgctxt "#30132" msgid "Days" msgstr "Ngā rangi" msgctxt "#30133" msgid "Always" msgstr "Tonu" resource.language.mk_mk/000077500000000000000000000000001346756700600346765ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000146601346756700600367360ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.mk_mk# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Macedonian (Macedonia) (http://www.transifex.com/projects/p/kodi-main/language/mk_MK/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: mk_MK\n" "Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostname" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi plugin Port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Само Free-to-air" msgctxt "#30003" msgid "Include Radio" msgstr "Вклучи и радио" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Брзо префрлување помеѓу канали (не сопирај временско поместување)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Тајмаут при поврзување (сек)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Увези само ТВ канали од групата" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Увези само Радио канали од групата" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Конвертирај име на хост во IP адреса" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Read genre strings (slow)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Време на чекање по префрлување на канал (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Фабрички рок на траење на снимките" msgctxt "#30015" msgid "Streaming method" msgstr "Метод на stream-ање" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows корисничко име (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows лозинка (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Користи RTSP streaming" msgctxt "#30040" msgid "Connection" msgstr "Конекција" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Репродукција" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Твојот TVServerKodi верзија '%s' е престара. Надгради на '%s' или по нова!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Твојата TVServerKodi верзија е престара. Надгради на '%s' или по нова!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Репродукција на снименото неуспешна. Празна адреса или име на папка." msgctxt "#30060" msgid "All cards are busy" msgstr "Сите карти се зафатени" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Каналот е кодиран" msgctxt "#30062" msgid "No video or audio detected" msgstr "Нема аудио или видео" msgctxt "#30063" msgid "No signal detected" msgstr "Нема сигнал" msgctxt "#30064" msgid "Unknown error" msgstr "Непозната грешка" msgctxt "#30065" msgid "Unable to start graph" msgstr "Неможе да се стартува графата" msgctxt "#30066" msgid "Unknown channel" msgstr "Непознат канал" msgctxt "#30067" msgid "No tuning details" msgstr "Нема детали за каналите" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Каналот не е мапиран кон било која карта" msgctxt "#30069" msgid "Card is disabled" msgstr "Картата е оневозможена" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Конекцијата со slave-от е неуспешна" msgctxt "#30071" msgid "Not the owner" msgstr "Не си сопственик" msgctxt "#30072" msgid "Graph building failed" msgstr "Неуспешна изградба на графата" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Недостасува SW енкодер" msgctxt "#30074" msgid "No free disk space" msgstr "Нема слободно место на дискот" msgctxt "#30075" msgid "No PMT found" msgstr "Нема пронајдено РМТ" msgctxt "#30100" msgid "Schedule settings" msgstr "Подеси го распоредот" msgctxt "#30101" msgid "Frequency" msgstr "Фреквенција" msgctxt "#30102" msgid "Airtime" msgstr "Време на емитување" msgctxt "#30103" msgid "Channels" msgstr "Канали" msgctxt "#30104" 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 "#30120" msgid "This time" msgstr "Овој пат" msgctxt "#30121" msgid "Anytime" msgstr "Било кога" msgctxt "#30122" msgid "Manual" msgstr "Рачно" msgctxt "#30125" msgid "This Channel" msgstr "Овој канал" msgctxt "#30126" msgid "Any Channel" msgstr "Било кој канал" msgctxt "#30130" msgid "Until space needed" msgstr "Се додека не е потребен простор" msgctxt "#30132" msgid "Days" msgstr "денови" msgctxt "#30133" msgid "Always" msgstr "Секогаш" msgctxt "#30135" msgid "Backend default" msgstr "Основни подесувања на backend-от" msgctxt "#30136" msgid "Kodi default" msgstr "Основни подесувања на Kodi" resource.language.mn_mn/000077500000000000000000000000001346756700600347045ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000014731346756700600367420ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.mn_mn# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Mongolian (Mongolia) (http://www.transifex.com/projects/p/kodi-main/language/mn_MN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: mn_MN\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Холболтын хугцаа хэтэрсэн" msgctxt "#30103" msgid "Channels" msgstr "Сувгууд" resource.language.ms_my/000077500000000000000000000000001346756700600347245ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000143511346756700600367610ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.ms_my# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Malay (Malaysia) (http://www.transifex.com/projects/p/kodi-main/language/ms_MY/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ms_MY\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nama hos Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Port pemalam Kodi Mediaportal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Free-to-air sahaja" msgctxt "#30003" msgid "Include Radio" msgstr "Sertakan Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Pertukaran saluran pantas (tidak henti anjak masa)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Had masa tamat sambung" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Hanya import Saluran TV dari kumpulan" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Hanya import Saluran Radio dari kumpulan" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Tukar nama hos ke alamat-IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Baca rentetan genre (perlahan)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Tunggu masa selepas menala saluran (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Benarkan dialog rakaman siri lama" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Hayat rakaman lalai" msgctxt "#30015" msgid "Streaming method" msgstr "Kaedah penstriman" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Akaun pengguna Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Kata laluan Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Guna penstriman RTSP" msgctxt "#30040" msgid "Connection" msgstr "Sambungan" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Main Balik" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi versi '%s' anda terlalu tua. Sila kemaskini ke '%s' atau lebih baharu!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi versi anda terlalu tua. Sila kemaskini ke '%s' atau lebih baharu!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Main balik rakaman gagal. URL nama fail kosong." msgctxt "#30060" msgid "All cards are busy" msgstr "Semua kad sibuk" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Saluran bercelaru" msgctxt "#30062" msgid "No video or audio detected" msgstr "Tiada video atau audio dikesan" msgctxt "#30063" msgid "No signal detected" msgstr "Tiada isyarat dikesan" msgctxt "#30064" msgid "Unknown error" msgstr "Ralat tidak diketahui" msgctxt "#30065" msgid "Unable to start graph" msgstr "Tidak boleh mulakan graf" msgctxt "#30066" msgid "Unknown channel" msgstr "Saluran tidak diketahui" msgctxt "#30067" msgid "No tuning details" msgstr "Tiada perincian penalaan" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Saluran tidak dipetakan ke mana-mana kad" msgctxt "#30069" msgid "Card is disabled" msgstr "Kad dilumpuhkan" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Sambungan ke hamba gagal" msgctxt "#30071" msgid "Not the owner" msgstr "Bukan pemilik" msgctxt "#30072" msgid "Graph building failed" msgstr "Pembinaan kad gagal" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Pengekod SW hilang" msgctxt "#30074" msgid "No free disk space" msgstr "Tiada ruang cakera bebas" msgctxt "#30075" msgid "No PMT found" msgstr "Tiada PMT ditemui" msgctxt "#30100" msgid "Schedule settings" msgstr "Tetapan jadual" msgctxt "#30101" msgid "Frequency" msgstr "Frekuensi" msgctxt "#30102" msgid "Airtime" msgstr "Masa Diudara" msgctxt "#30103" msgid "Channels" msgstr "Saluran" msgctxt "#30104" msgid "Keep" msgstr "Kekal" msgctxt "#30105" msgid "Record minutes before start" msgstr "Rakam minit sebelum mula" msgctxt "#30106" msgid "Record minutes after end" msgstr "Rakam minit selepas tamat" msgctxt "#30110" msgid "Record Once" msgstr "Rakam Sekali" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Rakam Harian (Program ini)" msgctxt "#30112" msgid "Record Weekly" msgstr "Rakam Mingguan" msgctxt "#30113" msgid "Record Weekends" msgstr "Rakam Hujung Minggu" msgctxt "#30114" msgid "Record Weekdays" msgstr "Rakam Hari Isnin-Jumaat" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Rakam setiap masa pada saluran ini" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Rakam setiap masa pada setiap saluran" msgctxt "#30117" msgid "Record every week at this time" msgstr "Rakam setiap minggu pada masa ini" msgctxt "#30118" msgid "Record every day at this time" msgstr "Rakam setiap hari pada masa ini" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Rakam setiap minggu pada saluran ini" msgctxt "#30120" msgid "This time" msgstr "Masa ini" msgctxt "#30121" msgid "Anytime" msgstr "Bila-bila Masa" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "Saluran Ini" msgctxt "#30126" msgid "Any Channel" msgstr "Mana-mana Saluran" msgctxt "#30130" msgid "Until space needed" msgstr "Sehingga ruang diperlukan" msgctxt "#30131" msgid "Until watched" msgstr "Sehingga ditonton" msgctxt "#30132" msgid "Days" msgstr "Hari" msgctxt "#30133" msgid "Always" msgstr "Sentiasa" msgctxt "#30134" msgid "1 week" msgstr "1 minggu" msgctxt "#30135" msgid "Backend default" msgstr "Lalai bahagian belakang" msgctxt "#30136" msgid "Kodi default" msgstr "Lalai Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d minggu" msgctxt "#30138" msgid "1 month" msgstr "1 bulan" msgctxt "#30139" msgid "%d months" msgstr "%d bulan" msgctxt "#30140" msgid "1 year" msgstr "1 tahun" resource.language.mt_mt/000077500000000000000000000000001346756700600347205ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000024031346756700600367500ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.mt_mt# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Maltese (Malta) (http://www.transifex.com/projects/p/kodi-main/language/mt_MT/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: mt_MT\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : n==0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3);\n" msgctxt "#30003" msgid "Include Radio" msgstr "Inkludi R-Radju" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Konnessjoni skadilha l-ħin (s)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Tul ta' ħajja normali tar-rekording" msgctxt "#30042" msgid "Playback" msgstr "Daqq" msgctxt "#30103" msgid "Channels" msgstr "Stazzjonijiet" msgctxt "#30104" msgid "Keep" msgstr "Żomm" msgctxt "#30122" msgid "Manual" msgstr "Manwali" msgctxt "#30132" msgid "Days" msgstr "Ġranet" msgctxt "#30133" msgid "Always" msgstr "Dejjem" resource.language.my_mm/000077500000000000000000000000001346756700600347165ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000064571346756700600367630ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.my_mm# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Burmese (Myanmar) (http://www.transifex.com/projects/p/kodi-main/language/my_MM/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: my_MM\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30002" msgid "Free-to-air only" msgstr "အခမဲ့ထုတ်လွင့်မှု့သာလျှင်" msgctxt "#30003" msgid "Include Radio" msgstr "ရေဒီယိုအပါအဝင်" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "ချိတ်ဆက်မှု့အချိန်ပြည့်သွားပါပြီ။" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "hostname ကို IP-adress အဖြစ်သို့ပြောင်းမည်" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows စကားဝှက် (SMB)" msgctxt "#30040" msgid "Connection" msgstr "ကွန်နက်ရှင်" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "သင်၏ TVServerKodi ဗားရှင်း '%s' သည်အရမ်းဟောင်းနေသည်။'%s' သို့မဟုတ် သူ့ထက်မြင့်သောဗားရှင်းသို့အဆင့်မြင့်ပါ" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "သင်၏ TVServerKodi ဗားရှင်း '%s' သည်အရမ်;ဟောင်းနေသည်။'%s' သို့မဟုတ် သူ့ထက်မြင့်သောဗားရှင်းသို့အဆင့်မြင့်ပါ" msgctxt "#30060" msgid "All cards are busy" msgstr "ကဒ်အားလုံးသည်အလုပ်များနေသည်" msgctxt "#30062" msgid "No video or audio detected" msgstr "ဗွီဒီယို သို့မဟုတ် အသံများ မသိရှိပါ။" msgctxt "#30064" msgid "Unknown error" msgstr "မသိရှိသောချို့ယွင်းချက်" msgctxt "#30065" msgid "Unable to start graph" msgstr "ဂရပ်ကိုမစနိုင်ပါ" msgctxt "#30066" msgid "Unknown channel" msgstr "အမည်မသိချယ်နယ်" msgctxt "#30069" msgid "Card is disabled" msgstr "ကဒ်ကိုပိတ်ထားသည်" msgctxt "#30071" msgid "Not the owner" msgstr "ပိုင်ရှင်မဟုတ်ပါ" msgctxt "#30074" msgid "No free disk space" msgstr "နေရာလွတ်မရှိပါ။" msgctxt "#30075" msgid "No PMT found" msgstr "PMT ကိုမတွေ့ပါ။" msgctxt "#30103" msgid "Channels" msgstr "Channel များ" msgctxt "#30122" msgid "Manual" msgstr "စိတ်ကြိုက်" msgctxt "#30132" msgid "Days" msgstr "ရက်များ" msgctxt "#30133" msgid "Always" msgstr "အမြဲတမ်း" resource.language.nb_no/000077500000000000000000000000001346756700600346735ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000143211346756700600367250ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.nb_no# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Norwegian Bokmål (Norway) (http://www.transifex.com/projects/p/kodi-main/language/nb_NO/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: nb_NO\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "MediaPortal vertsnavn" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "MediaPortal Kodi plugin port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Kun gratis kanaler" msgctxt "#30003" msgid "Include Radio" msgstr "Inkluder radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Kjapp kanalskifte(ikke stopp timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Tilkobling tidsavbrudd(er)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importer kun TV kanaler fra gruppe" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importer kun Radio kanaler fra gruppe" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Konverter vertsnavn til Ip-adresse" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Les sjanger (treg)" msgctxt "#30010" 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 opptaksdialog" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Standard levetid for opptak" msgctxt "#30015" msgid "Streaming method" msgstr "Streming metode" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows bruker konto (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows bruker passord (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Bruk RTSP streaming" msgctxt "#30040" msgid "Connection" msgstr "Tilkobling" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Avspilling" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Din TVServerKodi-versjon '%s' er for gammel. Oppgrader til '%s' eller høyere!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Din TVServerKodi-versjon er for gammel. Oppgrader til '%s' eller høyere!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Avspilling av opptak feilet. URL eller filnavn mangler." msgctxt "#30060" msgid "All cards are busy" msgstr "Alle kort er opptatt" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanal er kodet" msgctxt "#30062" msgid "No video or audio detected" msgstr "Finner ingen video eller lyd" msgctxt "#30063" msgid "No signal detected" msgstr "Ingen signal funnet" msgctxt "#30064" msgid "Unknown error" msgstr "Ukjent feil" msgctxt "#30065" msgid "Unable to start graph" msgstr "Kan ikke starte graf" msgctxt "#30066" msgid "Unknown channel" msgstr "Ukjent kanal" msgctxt "#30067" msgid "No tuning details" msgstr "Ingen tuning detaljer" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanal er ikke mappet til noe kort" msgctxt "#30069" msgid "Card is disabled" msgstr "Kort er deaktivert" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Tilkobling til slave feilet" msgctxt "#30071" msgid "Not the owner" msgstr "Ikke eieren" msgctxt "#30072" msgid "Graph building failed" msgstr "Graf bygging feilet" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW Encoder mangler" msgctxt "#30074" msgid "No free disk space" msgstr "Ikke nok ledig lagringsplass" msgctxt "#30075" msgid "No PMT found" msgstr "Fant ikke PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "Programinnstillinger" msgctxt "#30101" msgid "Frequency" msgstr "Frekvens" msgctxt "#30102" msgid "Airtime" msgstr "Sendes" msgctxt "#30103" msgid "Channels" msgstr "Kanaler" msgctxt "#30104" msgid "Keep" msgstr "Behold" msgctxt "#30105" msgid "Record minutes before start" msgstr "Ta opp minutter før start" msgctxt "#30106" msgid "Record minutes after end" msgstr "Ta opp minutter etter slutt" msgctxt "#30110" msgid "Record Once" msgstr "Ta opp en gang" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Ta opp daglig (dette programmet)" msgctxt "#30112" msgid "Record Weekly" msgstr "Ta opp ukentlig" msgctxt "#30113" 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" msgctxt "#30121" msgid "Anytime" msgstr "Når som helst" msgctxt "#30122" msgid "Manual" msgstr "Manuelt" msgctxt "#30125" msgid "This Channel" msgstr "Denne kanalen" msgctxt "#30126" msgid "Any Channel" msgstr "Hvilken som helst kanal" msgctxt "#30130" msgid "Until space needed" msgstr "Frem til plass trengs" msgctxt "#30131" msgid "Until watched" msgstr "Fram til sett" msgctxt "#30132" msgid "Days" msgstr "Dager" msgctxt "#30133" msgid "Always" msgstr "Alltid" msgctxt "#30134" msgid "1 week" msgstr "Én uke" msgctxt "#30135" msgid "Backend default" msgstr "Backend standard" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi standard" msgctxt "#30137" msgid "%d weeks" msgstr "%d uker" msgctxt "#30138" msgid "1 month" msgstr "Én måned" msgctxt "#30139" msgid "%d months" msgstr "%d måneder" msgctxt "#30140" msgid "1 year" msgstr "Étt år" resource.language.nl_nl/000077500000000000000000000000001346756700600347025ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000143571346756700600367450ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.nl_nl# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/kodi-main/language/nl_NL/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: nl_NL\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostnaam" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi pluginpoort" msgctxt "#30002" msgid "Free-to-air only" msgstr "Toon alleen vrij te ontvangen kanalen" msgctxt "#30003" msgid "Include Radio" msgstr "Toon radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Snel zappen (timeshift niet stoppen)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Verbindingstimeout (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importeer alleen TV-kanalen uit groep" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importeer alleen radiokanalen uit groep" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Converteer hostnaam naar IP-adres" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Genretekst inlezen (traag)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Wachttijd na kanaal tunen (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Inschakelen opnamedialoog oude series" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Standaard opnamelevensduur" msgctxt "#30015" msgid "Streaming method" msgstr "Streaming-methode" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows gebruikersnaam (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows wachtwoord (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Gebruik RTSP-streaming" msgctxt "#30040" msgid "Connection" msgstr "Verbinding" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Afspelen" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Uw TVServerKodi-plugin '%s' is te oud. U heeft minimaal versie '%s' of nieuwer nodig!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Uw TVServerKodi-plugin is te oud. U heeft minimaal versie '%s' of nieuwer nodig!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Afspelen opname mislukt. Lege URL of bestandsnaam." msgctxt "#30060" msgid "All cards are busy" msgstr "Alle kaarten zijn bezet" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanaal is gecodeerd" msgctxt "#30062" msgid "No video or audio detected" msgstr "Geen video of audio gedetecteerd" msgctxt "#30063" msgid "No signal detected" msgstr "Geen signaal" msgctxt "#30064" msgid "Unknown error" msgstr "Onbekende fout" msgctxt "#30065" msgid "Unable to start graph" msgstr "Graph starten mislukt" msgctxt "#30066" msgid "Unknown channel" msgstr "Onbekend kanaal" msgctxt "#30067" msgid "No tuning details" msgstr "Geen tuningdetails" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanaal is niet gekoppeld aan een kaart" msgctxt "#30069" msgid "Card is disabled" msgstr "Kaart is uitgeschakeld" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Verbinding met slave verbroken" msgctxt "#30071" msgid "Not the owner" msgstr "Niet de eigenaar" msgctxt "#30072" msgid "Graph building failed" msgstr "Aanmaken Graph mislukt" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW-encoder ontbreekt" msgctxt "#30074" msgid "No free disk space" msgstr "Geen vrije schijfruimte" msgctxt "#30075" msgid "No PMT found" msgstr "Geen PMT gevonden" msgctxt "#30100" msgid "Schedule settings" msgstr "Schema-instellingen" msgctxt "#30101" msgid "Frequency" msgstr "Frequentie" msgctxt "#30102" msgid "Airtime" msgstr "Uitzendtijd" msgctxt "#30103" msgid "Channels" msgstr "Kanalen" msgctxt "#30104" msgid "Keep" msgstr "Behouden" msgctxt "#30105" msgid "Record minutes before start" msgstr "Opnameminuten voor start" msgctxt "#30106" msgid "Record minutes after end" msgstr "Opnameminuten na einde" msgctxt "#30110" msgid "Record Once" msgstr "Eenmaal opnemen" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Dagelijks opnemen (Dit programma)" msgctxt "#30112" msgid "Record Weekly" msgstr "Wekelijks opnemen" msgctxt "#30113" msgid "Record Weekends" msgstr "Opnemen weekenden" msgctxt "#30114" msgid "Record Weekdays" msgstr "Opnemen weekdagen" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Neem iedere keer op vanaf dit kanaal" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Neem iedere keer op vanaf elk kanaal" msgctxt "#30117" msgid "Record every week at this time" msgstr "Neem iedere week op deze tijd op" msgctxt "#30118" msgid "Record every day at this time" msgstr "Neem iedere dag op deze tijd op" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Neem wekelijks op van dit kanaal" msgctxt "#30120" msgid "This time" msgstr "Deze tijd" msgctxt "#30121" msgid "Anytime" msgstr "Altijd" msgctxt "#30122" msgid "Manual" msgstr "Handmatig" msgctxt "#30125" msgid "This Channel" msgstr "Dit kanaal" msgctxt "#30126" msgid "Any Channel" msgstr "Elk kanaal" msgctxt "#30130" msgid "Until space needed" msgstr "Tot wanneer ruimte benodigd is." msgctxt "#30131" msgid "Until watched" msgstr "Tot bekeken" msgctxt "#30132" msgid "Days" msgstr "dagen" msgctxt "#30133" msgid "Always" msgstr "Altijd" msgctxt "#30134" msgid "1 week" msgstr "1 week" msgctxt "#30135" msgid "Backend default" msgstr "Backend standaard" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi-standaard" msgctxt "#30137" msgid "%d weeks" msgstr "%d weken" msgctxt "#30138" msgid "1 month" msgstr "1 maand" msgctxt "#30139" msgid "%d months" msgstr "%d maanden" msgctxt "#30140" msgid "1 year" msgstr "1 jaar" resource.language.pl_pl/000077500000000000000000000000001346756700600347065ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000151321346756700600367410ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.pl_pl# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Polish (Poland) (http://www.transifex.com/projects/p/kodi-main/language/pl_PL/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pl_PL\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 "Nazwa lub adres serwera" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Port wtyczki Mediaportal dla Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Tylko niekodowane" msgctxt "#30003" msgid "Include Radio" msgstr "Uwzględniaj kanały radiowe" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Szybka zmiana kanału (nie zatrzymuj przesunięcia czasowego)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Limit czasu połączenia (sekundy)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importuj tylko kanały telewizyjne z grupy" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importuj tylko kanały radiowe z grupy" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Rozwiązuj nazwę serwera na adres IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Przewodnik: wczytuj gatunki (wolne)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Czas oczekiwania po dostrojeniu kanału (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Aktywuj okno nagrywania starych seriali" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Domyślny czas życia nagrania" msgctxt "#30015" msgid "Streaming method" msgstr "Metoda transmisji" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Nazwa użytkownika Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Hasło użytkownika Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Używaj transmisji RTSP" msgctxt "#30040" msgid "Connection" msgstr "Połączenie" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Odtwarzanie" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Dodatek TVServerKodi '%s' jest w zbyt starej wersji. Zaktualizuj go do wersji '%s' lub nowszej!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Dodatek TVServerKodi jest w starej nieobsługiwanej wersji. Zaktualizuj go do wersji '%s\" lub nowszej!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Nieudane odtwarzanie nagrania. Adres URL lub nazwa pliku jest pusta." msgctxt "#30060" msgid "All cards are busy" msgstr "Wszystkie karty są zajęte" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanał jest zakodowany" msgctxt "#30062" msgid "No video or audio detected" msgstr "Nie wykryto transmisji obrazu lub dźwięku" msgctxt "#30063" msgid "No signal detected" msgstr "Brak sygnału" msgctxt "#30064" msgid "Unknown error" msgstr "Nieznany błąd" msgctxt "#30065" msgid "Unable to start graph" msgstr "Nieudane uruchomienie diagramu" msgctxt "#30066" msgid "Unknown channel" msgstr "Nieznany kanał" msgctxt "#30067" msgid "No tuning details" msgstr "Brak danych dostrajania" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanał nie jest przypisany do żadnej karty" msgctxt "#30069" msgid "Card is disabled" msgstr "Karta jest niedostępna" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Nieudane połączenie z usługą" msgctxt "#30071" msgid "Not the owner" msgstr "To nie jest właściciel" msgctxt "#30072" msgid "Graph building failed" msgstr "Nieudane budowanie diagramu" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Brak dekodera programowego" msgctxt "#30074" msgid "No free disk space" msgstr "Brak miejsca na dysku" msgctxt "#30075" msgid "No PMT found" msgstr "Nie znaleziono PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "Ustawienia harmonogramu" msgctxt "#30101" msgid "Frequency" msgstr "Programator" msgctxt "#30102" msgid "Airtime" msgstr "Czas emisji" msgctxt "#30103" msgid "Channels" msgstr "Kanały" msgctxt "#30104" msgid "Keep" msgstr "Zachowaj" msgctxt "#30105" msgid "Record minutes before start" msgstr "Nagrywaj przed zaplanowanym czasem początku" msgctxt "#30106" msgid "Record minutes after end" msgstr "Nagrywaj po zaplanowanym czasie końca" msgctxt "#30110" msgid "Record Once" msgstr "Nagrywaj jednorazowo" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Nagrywaj codziennie (ten program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Nagrywaj co tydzień" msgctxt "#30113" msgid "Record Weekends" msgstr "Nagrywaj w weekendy" msgctxt "#30114" msgid "Record Weekdays" msgstr "Nagrywaj w dni powszednie" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Nagrywaj za każdym razem na tym kanale" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Nagrywaj za każdym razem na każdym kanale" msgctxt "#30117" msgid "Record every week at this time" msgstr "Nagrywaj co tydzień o tej porze" msgctxt "#30118" msgid "Record every day at this time" msgstr "Nagrywaj co dzień o tej porze" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Nagrywaj co tydzień na tym kanale" msgctxt "#30120" msgid "This time" msgstr "Tym razem" msgctxt "#30121" msgid "Anytime" msgstr "Kiedykolwiek" msgctxt "#30122" msgid "Manual" msgstr "Manualnie" msgctxt "#30125" msgid "This Channel" msgstr "Ten kanał" msgctxt "#30126" msgid "Any Channel" msgstr "Dowolny kanał" msgctxt "#30130" msgid "Until space needed" msgstr "Do momentu, gdy braknie przestrzeni" msgctxt "#30131" msgid "Until watched" msgstr "Do czasu obejrzenia" msgctxt "#30132" msgid "Days" msgstr "Dni" msgctxt "#30133" msgid "Always" msgstr "Zawsze" msgctxt "#30134" msgid "1 week" msgstr "1 tydzień" msgctxt "#30135" msgid "Backend default" msgstr "Domyślne serwera" msgctxt "#30136" msgid "Kodi default" msgstr "Domyślne dla Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d tygodni" msgctxt "#30138" msgid "1 month" msgstr "1 miesiąc" msgctxt "#30139" msgid "%d months" msgstr "%d miesięcy" msgctxt "#30140" msgid "1 year" msgstr "1 rok" resource.language.pt_br/000077500000000000000000000000001346756700600347065ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000146721346756700600367510ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.pt_br# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/kodi-main/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nome do host do Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Porta do plugin Kodi do Mediaportal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Somente canais abertos" msgctxt "#30003" msgid "Include Radio" msgstr "Incluir Rádio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Troca rápida de canais (não interrompe o timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Tempo limite para conexão (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importar somente canais de TV do grupo" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importar somente canais de rádio do grupo" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Converter nome do host para endereço IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Ler strings de gêneros (lento)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Tempo limite após sintonizar um canal (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Ativar diálogo de gravação de seriados mais antigos" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Tempo de vida da gravação padrão" msgctxt "#30015" msgid "Streaming method" msgstr "Método Streaming" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Conta de usuário do Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Senha do Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Usar streaming RTSP" msgctxt "#30040" msgid "Connection" msgstr "Conexão" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Reprodução" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Seu TVServerKodi versão '%s' é muito antigo. Por favor, atualize para '%s' ou maior!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "A versão do seu TVServerKodi é muito antiga. Por favor, atualize para '%s' ou maior!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Falha na gravação. URL do nome do arquivo vazia." msgctxt "#30060" msgid "All cards are busy" msgstr "Todas as placas estão ocupadas" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Canal está codificado" msgctxt "#30062" msgid "No video or audio detected" msgstr "Nenhum vídeo ou áudio detectado" msgctxt "#30063" msgid "No signal detected" msgstr "Nenhum sinal detectado" msgctxt "#30064" msgid "Unknown error" msgstr "Erro desconhecido" msgctxt "#30065" msgid "Unable to start graph" msgstr "Não foi possível iniciar gráfico" msgctxt "#30066" msgid "Unknown channel" msgstr "Canal desconhecido" msgctxt "#30067" msgid "No tuning details" msgstr "Não há detalhes de sintonia" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Canal não está mapeado para nenhum placa" msgctxt "#30069" msgid "Card is disabled" msgstr "Placa está desativada" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Conexão para slave falhou" msgctxt "#30071" msgid "Not the owner" msgstr "Não é o proprietário" msgctxt "#30072" msgid "Graph building failed" msgstr "Falha na construção do gráfico" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Encoder SW faltando" msgctxt "#30074" msgid "No free disk space" msgstr "Sem espaço livre no disco" msgctxt "#30075" msgid "No PMT found" msgstr "Nenhum PMT encontrado" msgctxt "#30100" msgid "Schedule settings" msgstr "Ajustes de agendamento" msgctxt "#30101" msgid "Frequency" msgstr "Frequência" msgctxt "#30102" msgid "Airtime" msgstr "Horário no ar:" msgctxt "#30103" msgid "Channels" msgstr "Canais" msgctxt "#30104" msgid "Keep" msgstr "Manter" msgctxt "#30105" msgid "Record minutes before start" msgstr "Grave minutos antes do início" msgctxt "#30106" msgid "Record minutes after end" msgstr "Grave minutos antes do fim" msgctxt "#30110" msgid "Record Once" msgstr "Gravação Única" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Grave Diariamente (Este Programa)" msgctxt "#30112" msgid "Record Weekly" msgstr "Gravações Semanais" msgctxt "#30113" msgid "Record Weekends" msgstr "Gravações nos Fins de Semana" msgctxt "#30114" msgid "Record Weekdays" msgstr "Gravação Dias Úteis" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Grave toda vez deste canal" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Grave toda vez neste canal" msgctxt "#30117" msgid "Record every week at this time" msgstr "Gravar toda semana neste horário" msgctxt "#30118" msgid "Record every day at this time" msgstr "Gravar todo dia neste horário" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Gravar semanalmente este canal" msgctxt "#30120" msgid "This time" msgstr "Neste horário" msgctxt "#30121" msgid "Anytime" msgstr "Qualquer Tempo" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "Neste Canal" msgctxt "#30126" msgid "Any Channel" msgstr "Qualquer Canal" msgctxt "#30130" msgid "Until space needed" msgstr "Até existir espaço necessário" msgctxt "#30131" msgid "Until watched" msgstr "Até os Assistidos" msgctxt "#30132" msgid "Days" msgstr "Dias" msgctxt "#30133" msgid "Always" msgstr "Sempre" msgctxt "#30134" msgid "1 week" msgstr "1 semana" msgctxt "#30135" msgid "Backend default" msgstr "Backend padrão" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi padrão" msgctxt "#30137" msgid "%d weeks" msgstr "%d semanas" msgctxt "#30138" msgid "1 month" msgstr "1 mês" msgctxt "#30139" msgid "%d months" msgstr "%d meses" msgctxt "#30140" msgid "1 year" msgstr "1 ano" resource.language.pt_pt/000077500000000000000000000000001346756700600347265ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000150501346756700600367600ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.pt_pt# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/kodi-main/language/pt_PT/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pt_PT\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Nome do servidor Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Porto do plugin Kodi Mediaportal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Apenas emissões livres" msgctxt "#30003" msgid "Include Radio" msgstr "Incluir Rádio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Mudança de canais rápida (sem parar o timeshit)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Tempo-limite da ligação (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importar apenas Canais de TV do grupo" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importar apenas Canais de Rádio do grupo" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Converter nome do servidor para endereço IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Obter géneros (lento)" msgctxt "#30010" 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 "#30012" msgid "Default recording lifetime" msgstr "Padrão para tempo de vida da gravação" msgctxt "#30015" msgid "Streaming method" msgstr "Método de transmissão" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Conta do utilizador Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Palavra-passe Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Usar transmissão RTSP" msgctxt "#30040" msgid "Connection" msgstr "Ligação" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Reprodução" 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 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 atualize para '%s' ou superior!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "A reprodução da gravação falhou. Nome do ficheiro ou URL vazios." msgctxt "#30060" msgid "All cards are busy" msgstr "Todos as placas estão ocupadas" msgctxt "#30061" msgid "Channel is scrambled" msgstr "O canal é codificado" msgctxt "#30062" msgid "No video or audio detected" msgstr "Não foi detectado vídeo nem áudio" msgctxt "#30063" msgid "No signal detected" msgstr "Não foi detetado sinal" msgctxt "#30064" msgid "Unknown error" msgstr "Erro desconhecido" msgctxt "#30065" msgid "Unable to start graph" msgstr "Não foi possível iniciar o gráfico" msgctxt "#30066" msgid "Unknown channel" msgstr "Canal desconhecido" msgctxt "#30067" msgid "No tuning details" msgstr "Sem detalhes de sintonia" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "O canal não está mapeado para nenhuma placa" msgctxt "#30069" msgid "Card is disabled" msgstr "A placa está desativada" msgctxt "#30070" msgid "Connection to slave failed" msgstr "A ligação ao escravo falhou" msgctxt "#30071" msgid "Not the owner" msgstr "Não é o dono" msgctxt "#30072" msgid "Graph building failed" msgstr "Falha na criação do gráfico" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Falta o codificador por SW" msgctxt "#30074" msgid "No free disk space" msgstr "Não existe espaço disponível no disco" msgctxt "#30075" msgid "No PMT found" msgstr "PMT não encontrado" msgctxt "#30100" msgid "Schedule settings" msgstr "Definições de agendamento" msgctxt "#30101" msgid "Frequency" msgstr "Frequência" msgctxt "#30102" msgid "Airtime" msgstr "Tempo de emissão" msgctxt "#30103" msgid "Channels" msgstr "Canais" msgctxt "#30104" msgid "Keep" msgstr "Manter" msgctxt "#30105" msgid "Record minutes before start" msgstr "Minutos de gravação antes de iniciar" msgctxt "#30106" msgid "Record minutes after end" msgstr "Minutos de gravação depois de terminar" msgctxt "#30110" msgid "Record Once" msgstr "Gravar uma Vez" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Gravar Diariamente (Este programa)" msgctxt "#30112" msgid "Record Weekly" msgstr "Gravar Semanalmente" msgctxt "#30113" msgid "Record Weekends" 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" msgstr "Esta vez" msgctxt "#30121" msgid "Anytime" msgstr "Qualquer hora" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "Este Canal" msgctxt "#30126" msgid "Any Channel" 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" msgctxt "#30133" msgid "Always" msgstr "Sempre" msgctxt "#30134" msgid "1 week" msgstr "1 semana" msgctxt "#30135" msgid "Backend default" msgstr "Predefinição da infraestrutura" msgctxt "#30136" msgid "Kodi default" msgstr "Predefinição do Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d semanas" msgctxt "#30138" msgid "1 month" msgstr "1 mês" msgctxt "#30139" msgid "%d months" msgstr "%d meses" msgctxt "#30140" msgid "1 year" msgstr "1 ano" resource.language.ro_ro/000077500000000000000000000000001346756700600347205ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000152761346756700600367640ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.ro_ro# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Romanian (Romania) (http://www.transifex.com/projects/p/kodi-main/language/ro_RO/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ro_RO\n" "Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostname" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Portul pluginului Mediaportal Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Doar canalele gratuite" msgctxt "#30003" msgid "Include Radio" msgstr "Include Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Schimbare de canal rapidă (nu opri decalajul temporal)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Expirare timp conexiune (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importă doar canalele tv din grup" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importă doar canalele radio din grup" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Convertește hostname în IP-adress" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Citește genul stringurilor (încet)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Timp de așteptare după tuning a unui canal (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Activează dialogul de înregistrare al seriilor vechi" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Durată de viață implicită pentru înregistrări" msgctxt "#30015" msgid "Streaming method" msgstr "Metoda de streaming" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Cont Utilizator Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Parola Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Folosește streaming RTSP " msgctxt "#30040" msgid "Connection" msgstr "Conexiune" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Redare" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Versiunea ta de TVServerKodi '%s' este prea veche. Va rugam actualizați la '%s' sau mai nouă." msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" 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." msgstr "Redarea de înregistrare a eșuat. URL-ul sau fișierul sunt goale." msgctxt "#30060" msgid "All cards are busy" msgstr "Toate cardurile sunt ocupate" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Canalul este codat" msgctxt "#30062" msgid "No video or audio detected" msgstr "Nici video nici audio nu a fost detectat" msgctxt "#30063" msgid "No signal detected" msgstr "Nici un semnal detectat" msgctxt "#30064" msgid "Unknown error" msgstr "Eroare necunoscută" msgctxt "#30065" msgid "Unable to start graph" msgstr "Graficul nu poate fi pornit" msgctxt "#30066" msgid "Unknown channel" msgstr "Canal necunoscut" msgctxt "#30067" msgid "No tuning details" msgstr "Nici un detaliu de tuning" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Canalul nu este mapat pe nici un card" msgctxt "#30069" msgid "Card is disabled" msgstr "Cardul este dezactivat" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Conectarea la slave nu a reușit" msgctxt "#30071" msgid "Not the owner" msgstr "Nu este proprietarul" msgctxt "#30072" msgid "Graph building failed" msgstr "Creerea graficului a eșuat" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Lipsește encoderul SW " msgctxt "#30074" msgid "No free disk space" msgstr "Spațiu indisponibil pe disk" msgctxt "#30075" msgid "No PMT found" msgstr "PMT nu a fost gasit" msgctxt "#30100" msgid "Schedule settings" msgstr "Stabiliri planificare" msgctxt "#30101" msgid "Frequency" msgstr "Frecvență" msgctxt "#30102" msgid "Airtime" msgstr "Oră de difuzare" msgctxt "#30103" msgid "Channels" msgstr "Canale" msgctxt "#30104" msgid "Keep" msgstr "Păstrează" msgctxt "#30105" msgid "Record minutes before start" msgstr "Minute de înregistrat înainte de început" msgctxt "#30106" msgid "Record minutes after end" msgstr "Minute de înregistrat după sfârșit" msgctxt "#30110" msgid "Record Once" msgstr "Înregistrează o dată" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Înregistrează zilnic (Acest program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Înregistrare săptămânală" msgctxt "#30113" msgid "Record Weekends" msgstr "Înregistrare sfârșit de săptămână" msgctxt "#30114" msgid "Record Weekdays" msgstr "Înregistrează în zilele săptămânii" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Înregistrează întotdeauna pe acest canal" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Înregistrează întotdeauna pe fiecare canal" msgctxt "#30117" msgid "Record every week at this time" msgstr "Înregistrează în fiecare săptămână la această oră" msgctxt "#30118" msgid "Record every day at this time" msgstr "Înregistrează în fiecare zi la această oră" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Înregistrează săptămânal pe acest canal" msgctxt "#30120" msgid "This time" msgstr "De data aceasta" msgctxt "#30121" msgid "Anytime" msgstr "Oricând" msgctxt "#30122" msgid "Manual" msgstr "Manual" msgctxt "#30125" msgid "This Channel" msgstr "Acest canal" msgctxt "#30126" msgid "Any Channel" msgstr "Orice canal" msgctxt "#30130" msgid "Until space needed" msgstr "Până este nevoie de spațiu" msgctxt "#30131" msgid "Until watched" msgstr "Până sunt văzute" msgctxt "#30132" msgid "Days" msgstr "zile" msgctxt "#30133" msgid "Always" msgstr "Întotdeauna" msgctxt "#30134" msgid "1 week" msgstr "1 săptămână" msgctxt "#30135" msgid "Backend default" msgstr "Furnizor din spate implicit" msgctxt "#30136" msgid "Kodi default" msgstr "Implicit pentru Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d săptămâni" msgctxt "#30138" msgid "1 month" msgstr "1 lună" msgctxt "#30139" msgid "%d months" msgstr "%d luni" msgctxt "#30140" msgid "1 year" msgstr "1 an" resource.language.ru_ru/000077500000000000000000000000001346756700600347345ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000176311346756700600367750ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.ru_ru# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Russian (Russia) (http://www.transifex.com/projects/p/kodi-main/language/ru_RU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ru_RU\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Имя хоста Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Порт дополнения Kodi для Mediaportal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Только отрытые каналы" msgctxt "#30003" msgid "Include Radio" msgstr "Включать радио" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Быстрое переключение каналов (без остановки таймшифта)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Интервал ожидания подключения (с.)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Импортировать только каналы ТВ из группы" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Импортировать только каналы радио из группы" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Преобразовать имя хоста в IP-адрес" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: получать жанры (медленно)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Интервал ожидания после настройки канала (мс)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Включить старый диалог записи серий" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Срок хранения по умолчанию" msgctxt "#30015" msgid "Streaming method" msgstr "Метод потока" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Имя пользователя Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Пароль Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Использовать поток RTSP" msgctxt "#30040" msgid "Connection" msgstr "Соединение" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Воспроизведение" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Версия ТВ-сервера Kodi \"%s\" слишком старая. Обновитесь до версии \"%s\" или выше." msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Версия ТВ-сервера Kodi слишком старая. Обновитесь до версии \"%s\" или выше." msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Не удалось воспроизвести запись. Путой URL или имя файла" msgctxt "#30060" msgid "All cards are busy" msgstr "Все карты заняты" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Канал зашифрован" msgctxt "#30062" msgid "No video or audio detected" msgstr "Видео- или аудиопоток не обнаружен" msgctxt "#30063" msgid "No signal detected" msgstr "Нет сигнала" msgctxt "#30064" msgid "Unknown error" msgstr "Неизвестная ошибка" msgctxt "#30065" msgid "Unable to start graph" msgstr "Не удалось запустить граф" msgctxt "#30066" msgid "Unknown channel" msgstr "Неизвестный канал" msgctxt "#30067" msgid "No tuning details" msgstr "Нет сведений о настройке" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Канал не привязан ни к одной карте" msgctxt "#30069" msgid "Card is disabled" msgstr "Карта отключена" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Не удалось подключиться к ведомому устройство" msgctxt "#30071" msgid "Not the owner" msgstr "Владелец отсутствует" msgctxt "#30072" msgid "Graph building failed" msgstr "Не удалось построить граф" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Отсутствует программный кодировщик" msgctxt "#30074" msgid "No free disk space" msgstr "Нет места на диске" msgctxt "#30075" msgid "No PMT found" msgstr "PMT не найдена" msgctxt "#30100" msgid "Schedule settings" msgstr "Настройки Расписаний" msgctxt "#30101" msgid "Frequency" msgstr "Периодичность" msgctxt "#30102" msgid "Airtime" msgstr "Время эфира" msgctxt "#30103" msgid "Channels" msgstr "Каналы" msgctxt "#30104" 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 "Любое время" msgctxt "#30122" msgid "Manual" msgstr "Вручную" msgctxt "#30125" msgid "This Channel" msgstr "Этот канал" msgctxt "#30126" msgid "Any Channel" msgstr "Любой Канал" msgctxt "#30130" msgid "Until space needed" msgstr "Пока не будет нужно свободное место" msgctxt "#30131" msgid "Until watched" msgstr "Пока не просмотрено" msgctxt "#30132" msgid "Days" msgstr "дн." msgctxt "#30133" msgid "Always" msgstr "Всегда" msgctxt "#30134" msgid "1 week" msgstr "1 неделя" msgctxt "#30135" msgid "Backend default" msgstr "Стандартные настройки серверной части" 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 год" resource.language.si_lk/000077500000000000000000000000001346756700600347015ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000021251346756700600367320ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.si_lk# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Sinhala (Sri Lanka) (http://www.transifex.com/projects/p/kodi-main/language/si_LK/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: si_LK\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30003" msgid "Include Radio" msgstr "ගුවන් විදුලිය ඇතුළත් කරන්න" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "සම්බන්ධවීමේ කාලය(න්) ඉවරයි. " msgctxt "#30042" msgid "Playback" msgstr "නැවත වාදනය " msgctxt "#30122" msgid "Manual" msgstr "කාය වශයෙන්" msgctxt "#30132" msgid "Days" msgstr "දින " resource.language.sk_sk/000077500000000000000000000000001346756700600347125ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000153611346756700600367510ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.sk_sk# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Slovak (Slovakia) (http://www.transifex.com/projects/p/kodi-main/language/sk_SK/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sk_SK\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Názov hostiteľa Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi plugin - port" msgctxt "#30002" msgid "Free-to-air only" msgstr "Iba nekódované programy" msgctxt "#30003" msgid "Include Radio" msgstr "Vrátane rozhlasových kanálov" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Rýchle prepínanie kanálov (nevypínať časový posun)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Časový limit pre pripojenie" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Zo skupiny importovať iba TV kanály" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Zo skupiny importovať iba rozhlasové kanály" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Konvertovať názov hostiteľa na IP adresu" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: čítať informácie o žánri (pomalé)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Doba čakania po naladení kanálu (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Zapnúť pôvodné dialógové okno pre nahrávanie seriálov" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Predvolená životnosť nahrávania" msgctxt "#30015" msgid "Streaming method" msgstr "Metóda streamovania" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Používateľské konto systému Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Heslo pre systém Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Použiť RTSP streamovanie" msgctxt "#30040" msgid "Connection" msgstr "Pripojenie" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Prehrávanie" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Vaša TVServerKodi verzia '%s' je príliš neaktuálna. Aktualizujte ju, prosím, na verziu '%s' alebo vyššiu!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Vaša TVServerKodi verzia je príliš neaktuálna. Aktualizujte ju, prosím, na verziu '%s' alebo vyššiu!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Počas prehrávania nahrávky nastala chyba. Prázdna URL adresa alebo názov súboru." msgctxt "#30060" msgid "All cards are busy" msgstr "Všetky karty sú zaneprázdnené" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanál je kódovaný" msgctxt "#30062" msgid "No video or audio detected" msgstr "Nebolo zistené žiadne video alebo audio" msgctxt "#30063" msgid "No signal detected" msgstr "Nebol zistený žiadny signál" msgctxt "#30064" msgid "Unknown error" msgstr "Neznáma chyba" msgctxt "#30065" msgid "Unable to start graph" msgstr "Nie je možné spustiť graf" msgctxt "#30066" msgid "Unknown channel" msgstr "Neznámy kanál" msgctxt "#30067" msgid "No tuning details" msgstr "Nie sú žiadne podrobnosti o ladení" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanál nie je namapovaný na žiadnu kartu" msgctxt "#30069" msgid "Card is disabled" msgstr "Karta je vypnutá" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Pripojenie k zariadeniu zlyhalo" msgctxt "#30071" msgid "Not the owner" msgstr "Nie je vlastník" msgctxt "#30072" msgid "Graph building failed" msgstr "Nastala chyba počas vytvárania grafu" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Chýba softvérový kodér" msgctxt "#30074" msgid "No free disk space" msgstr "Nie je voľné miesto na disku" msgctxt "#30075" msgid "No PMT found" msgstr "Nenašla sa žiadna PMT tabuľka" msgctxt "#30100" msgid "Schedule settings" msgstr "Nastavenia plánovania" msgctxt "#30101" msgid "Frequency" msgstr "Frekvencia" msgctxt "#30102" msgid "Airtime" msgstr "Čas vysielania" msgctxt "#30103" msgid "Channels" msgstr "Kanály" msgctxt "#30104" msgid "Keep" msgstr "Ponechať" msgctxt "#30105" msgid "Record minutes before start" msgstr "Minúty nahrávania pred začiatkom" msgctxt "#30106" msgid "Record minutes after end" msgstr "Minúty nahrávania po skončení" msgctxt "#30110" msgid "Record Once" msgstr "Nahrať raz" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Nahrávať denne (tento program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Nahrávať týždenne" msgctxt "#30113" msgid "Record Weekends" msgstr "Nahrávať cez víkendy" msgctxt "#30114" msgid "Record Weekdays" msgstr "Nahrávať cez pracovné dni" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Nahrávať zakaždým na tomto kanále" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Nahrávať zakaždým na každom kanále" msgctxt "#30117" msgid "Record every week at this time" msgstr "Nahrávať každý týždeň v tomto čase" msgctxt "#30118" msgid "Record every day at this time" msgstr "Nahrávať každý deň v tomto čase" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Nahrávať týždenne na tomto kanále" msgctxt "#30120" msgid "This time" msgstr "O tomto čase" msgctxt "#30121" msgid "Anytime" msgstr "Kedykoľvek" msgctxt "#30122" msgid "Manual" msgstr "Manuálne" msgctxt "#30125" msgid "This Channel" msgstr "Tento kanál" msgctxt "#30126" msgid "Any Channel" msgstr "Akýkoľvek kanál" msgctxt "#30130" msgid "Until space needed" msgstr "Dokiaľ nebude potrebné miesto" msgctxt "#30131" msgid "Until watched" msgstr "Dokiaľ bude nepozreté" msgctxt "#30132" msgid "Days" msgstr "Dni" msgctxt "#30133" msgid "Always" msgstr "Vždy" msgctxt "#30134" msgid "1 week" msgstr "1 týždeň" msgctxt "#30135" msgid "Backend default" msgstr "Predvolené hodnoty backendu" msgctxt "#30136" msgid "Kodi default" msgstr "Predvolené hodnoty Kodi" msgctxt "#30137" msgid "%d weeks" msgstr "%d týždňov" msgctxt "#30138" msgid "1 month" msgstr "1 mesiac" msgctxt "#30139" msgid "%d months" msgstr "%d mesiacov" msgctxt "#30140" msgid "1 year" msgstr "1 rok" resource.language.sl_si/000077500000000000000000000000001346756700600347115ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000130031346756700600367370ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.sl_si# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/kodi-main/language/sl_SI/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sl_SI\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Ime gostitelja Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Vrata vtičnika Mediaportal za Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Samo brezplačni programi" msgctxt "#30003" msgid "Include Radio" msgstr "Vključi radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Hitro preklapljanje (ne ustavi časovnega zamika)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Časovni pretek povezave (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Uvozi le TV programe iz skupine" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Uvozi le radijske programe iz skupine" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Pretvori ime gostitelja v IP naslov" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Preberi žanre (počasno)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Čakanje med preklopom programa (ms)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Privzeti čas ohranitve posnetkov" msgctxt "#30015" msgid "Streaming method" msgstr "Metoda pretakanja" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Uporabnik sistema Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Geslo sistema Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Uporabi pretakanje RTSP" msgctxt "#30040" msgid "Connection" msgstr "Povezava" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Predvajanje" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Vaša različica TVServerKodija '%s' je prestara. Posodobite na '%s' ali novejše!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Vaša različica TVServerKodija je prestara. Posodobite na '%s' ali novejše!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Predvajanje posnetka ni uspelo. Prazen URL ali ime datoteke." msgctxt "#30060" msgid "All cards are busy" msgstr "Vse kartice so zasedene" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Program je kodiran" msgctxt "#30062" msgid "No video or audio detected" msgstr "Ni zaznanega zvoka ali slike" msgctxt "#30063" msgid "No signal detected" msgstr "Ni zaznanega signala" msgctxt "#30064" msgid "Unknown error" msgstr "Neznana napaka" msgctxt "#30065" msgid "Unable to start graph" msgstr "Ni mogoče zagnati grafa" msgctxt "#30066" msgid "Unknown channel" msgstr "Neznan program" msgctxt "#30067" msgid "No tuning details" msgstr "Ni podrobnosti preklopa" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Program ni povezan z nobeno kartico" msgctxt "#30069" msgid "Card is disabled" msgstr "Kartica je onemogočena" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Povezava ni uspela" msgctxt "#30071" msgid "Not the owner" msgstr "Niste lastnik" msgctxt "#30072" msgid "Graph building failed" msgstr "Ni mogoče zgraditi grafa" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Manjkajoč SW enkoder" msgctxt "#30074" msgid "No free disk space" msgstr "Ni več prostora na disku" msgctxt "#30075" msgid "No PMT found" msgstr "PMT ni mogoče najti" msgctxt "#30100" msgid "Schedule settings" msgstr "Nastavitve urnika" msgctxt "#30101" msgid "Frequency" msgstr "Frekvenca" msgctxt "#30102" msgid "Airtime" msgstr "Čas predvajanja" msgctxt "#30103" msgid "Channels" msgstr "Programi" msgctxt "#30104" msgid "Keep" msgstr "Obdrži" msgctxt "#30105" msgid "Record minutes before start" msgstr "Snemanje minut pred začetkom" msgctxt "#30106" msgid "Record minutes after end" msgstr "Snemanje minut po koncu" msgctxt "#30110" msgid "Record Once" msgstr "Posnemi enkrat" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Snemaj dnevno (ta program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Snemaj tedensko" msgctxt "#30113" msgid "Record Weekends" msgstr "Snemanj ob vikendih" msgctxt "#30120" msgid "This time" msgstr "Tokrat" msgctxt "#30121" msgid "Anytime" msgstr "Kadarkoli" msgctxt "#30122" msgid "Manual" msgstr "Ročno" msgctxt "#30125" msgid "This Channel" msgstr "Ta program" msgctxt "#30126" msgid "Any Channel" msgstr "Katerikoli program" msgctxt "#30130" msgid "Until space needed" msgstr "Dokler se ne potrebuje prostora" msgctxt "#30132" msgid "Days" msgstr "Dni" msgctxt "#30133" msgid "Always" msgstr "Vedno" msgctxt "#30134" msgid "1 week" msgstr "1 teden" msgctxt "#30135" msgid "Backend default" msgstr "Privzeto za hrbtenico" msgctxt "#30136" msgid "Kodi default" msgstr "Privzeto za Kodi" msgctxt "#30138" msgid "1 month" msgstr "1 mesec" msgctxt "#30140" msgid "1 year" msgstr "1 leto" resource.language.sq_al/000077500000000000000000000000001346756700600346775ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000022661346756700600367360ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.sq_al# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Albanian (Albania) (http://www.transifex.com/projects/p/kodi-main/language/sq_AL/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sq_AL\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30003" msgid "Include Radio" msgstr "Inkludo Radionë" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Timeout i lidhjes (s)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Jeta e prezgjedhur i nji regjistrimi" msgctxt "#30042" msgid "Playback" msgstr "Ndëgjim" msgctxt "#30103" msgid "Channels" msgstr "Kanalet" msgctxt "#30104" msgid "Keep" msgstr "Mbaj" msgctxt "#30122" msgid "Manual" msgstr "Udhëzuesi" msgctxt "#30132" msgid "Days" msgstr "Ditët" msgctxt "#30133" msgid "Always" msgstr "Gjithnjë" resource.language.sr_rs/000077500000000000000000000000001346756700600347305ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000170671346756700600367740ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.sr_rs# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Serbian (Serbia) (http://www.transifex.com/projects/p/kodi-main/language/sr_RS/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sr_RS\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostname" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Порт Mediaportal Kodi прикључка" msgctxt "#30002" msgid "Free-to-air only" msgstr "Само некодирани" msgctxt "#30003" msgid "Include Radio" msgstr "Укључи Радио" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Брза промена канала (не заустављај временски померај)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Истек времена за повезивање (с)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Увези само ТВ Канале из групе" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Увези само Радио Канале из групе" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Претвори hostname у IP-адресу" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Читај стрингове жанра (споро)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Време чекања након промене канала (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Укључи дијалог снимања старих серија" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Подразумевани рок трајања снимања" msgctxt "#30015" msgid "Streaming method" msgstr "Метод стримовања" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows кориснички налог (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows лозинка (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Користи RTSP стримовање" msgctxt "#30040" msgid "Connection" msgstr "Веза" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Репродукција" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Ваша верзија TVServerKodi '%s' је превише стара. Молимо, ажурирајте на '%s' или више!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Ваша верзија TVServerKodi је превише стара. Молимо, ажурирајте на '%s' или више!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Снимање репродукције неуспело. Празан URL или име датотеке." msgctxt "#30060" msgid "All cards are busy" msgstr "Све карте су заузете" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Канал је кодиран" msgctxt "#30062" msgid "No video or audio detected" msgstr "Видео или аудио нису откривени" msgctxt "#30063" msgid "No signal detected" msgstr "Сигнал није откривен" msgctxt "#30064" msgid "Unknown error" msgstr "Непозната грешка" msgctxt "#30065" msgid "Unable to start graph" msgstr "Није могуће покретање графика" msgctxt "#30066" msgid "Unknown channel" msgstr "Непознати канал" msgctxt "#30067" msgid "No tuning details" msgstr "Нема детаља о промени" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Канал није мапиран ни на једној карти" msgctxt "#30069" msgid "Card is disabled" msgstr "Карта је онемогућена" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Веза са слејвом неуспела" msgctxt "#30071" msgid "Not the owner" msgstr "Не власник" msgctxt "#30072" msgid "Graph building failed" msgstr "Неуспело прављење графика" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Недостаје SW Кодер" msgctxt "#30074" msgid "No free disk space" msgstr "Нема слободног простора на диску" msgctxt "#30075" msgid "No PMT found" msgstr "PMT није пронађен" msgctxt "#30100" msgid "Schedule settings" msgstr "Подешавања распореда" msgctxt "#30101" msgid "Frequency" msgstr "Фреквенција" msgctxt "#30102" msgid "Airtime" msgstr "Време емитовања" msgctxt "#30103" msgid "Channels" msgstr "Канали" msgctxt "#30104" 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 "Било када" msgctxt "#30122" msgid "Manual" msgstr "Ручно" msgctxt "#30125" msgid "This Channel" msgstr "Овај Канал" msgctxt "#30126" msgid "Any Channel" msgstr "Било који Канал" msgctxt "#30130" msgid "Until space needed" msgstr "Док простор није потребан" msgctxt "#30131" msgid "Until watched" msgstr "Док није одгледано" msgctxt "#30132" msgid "Days" msgstr "Дана" msgctxt "#30133" msgid "Always" msgstr "Увек" msgctxt "#30134" msgid "1 week" msgstr "1 недеља" msgctxt "#30135" msgid "Backend default" msgstr "Подразумевано позадине" 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 година" resource.language.sr_rs@latin/000077500000000000000000000000001346756700600360605ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000146061346756700600401200ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.sr_rs@latin# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Serbian (Latin) (Serbia) (http://www.transifex.com/projects/p/kodi-main/language/sr_RS@latin/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sr_RS@latin\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Hostname" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Port Mediaportal Kodi priključka" msgctxt "#30002" msgid "Free-to-air only" msgstr "Samo nekodirani" msgctxt "#30003" msgid "Include Radio" msgstr "Uključi Radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Brza promena kanala (ne zaustavljaj vremenski pomeraj)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Istek vremena za povezivanje (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Uvezi samo TV Kanale iz grupe" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Uvezi samo Radio Kanale iz grupe" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Pretvori hostname u IP-adresu" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Čitaj stringove žanra (sporo)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Vreme čekanja nakon promene kanala (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Uključi dijalog za snimanje starih serija" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Podrazumevani rok trajanja snimanja" msgctxt "#30015" msgid "Streaming method" msgstr "Metod strimovanja" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows korisnički nalog (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows lozinka (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Koristi RTSP strimovanje" msgctxt "#30040" msgid "Connection" msgstr "Veza" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Reprodukcija" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Vaša verzija TVServerKodi '%s' je previše stara. Molimo, ažurirajte na '%s' ili više!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Vaša verzija TVServerKodi je previše stara. Molimo, ažurirajte na '%s' ili više!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Snimanje reprodukcije neuspelo. Prazan URL ili ime datoteke." msgctxt "#30060" msgid "All cards are busy" msgstr "Sve karte su zauzete" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanal je kodiran" msgctxt "#30062" msgid "No video or audio detected" msgstr "Video ili audio nisu otkriveni" msgctxt "#30063" msgid "No signal detected" msgstr "Signal nije otkriven" msgctxt "#30064" msgid "Unknown error" msgstr "Nepoznata greška" msgctxt "#30065" msgid "Unable to start graph" msgstr "Nije moguće pokretanje grafika" msgctxt "#30066" msgid "Unknown channel" msgstr "Nepoznati kanal" msgctxt "#30067" msgid "No tuning details" msgstr "Nema detalja o promeni" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanal nije mapiran ni na jednoj karti" msgctxt "#30069" msgid "Card is disabled" msgstr "Karta je onemogućena" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Veza sa slejvom neuspela" msgctxt "#30071" msgid "Not the owner" msgstr "Ne vlasnik" msgctxt "#30072" msgid "Graph building failed" msgstr "Neuspelo pravljenje grafika" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Nedostaje SW Koder" msgctxt "#30074" msgid "No free disk space" msgstr "Nema slobodnog prostora na disku" msgctxt "#30075" msgid "No PMT found" msgstr "PMT nije pronađen" msgctxt "#30100" msgid "Schedule settings" msgstr "Podešavanja rasporeda" msgctxt "#30101" msgid "Frequency" msgstr "Frekvencija" msgctxt "#30102" msgid "Airtime" msgstr "Vreme emitovanja" msgctxt "#30103" msgid "Channels" msgstr "Kanali" msgctxt "#30104" msgid "Keep" msgstr "Zadržati" msgctxt "#30105" msgid "Record minutes before start" msgstr "Snimiti minute pre početka" msgctxt "#30106" msgid "Record minutes after end" msgstr "Snimiti minute posle kraja" msgctxt "#30110" msgid "Record Once" msgstr "Snimi Jednom" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Snimaj Dnevno (Ovaj program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Snimaj Nedeljno" msgctxt "#30113" msgid "Record Weekends" msgstr "Snimaj Vikende" msgctxt "#30114" msgid "Record Weekdays" msgstr "Snimaj Radnim danima" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Snimaj uvek na ovom kanalu" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Snimaj uvek na svim kanalima" msgctxt "#30117" msgid "Record every week at this time" msgstr "Snimaj svake nedelje u ovo vreme" msgctxt "#30118" msgid "Record every day at this time" msgstr "Snimaj svaki dan u ovo vreme" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Snimaj nedeljno na ovom kanalu" msgctxt "#30120" msgid "This time" msgstr "Ovo vreme" msgctxt "#30121" msgid "Anytime" msgstr "Bilo kada" msgctxt "#30122" msgid "Manual" msgstr "Ručno" msgctxt "#30125" msgid "This Channel" msgstr "Ovaj Kanal" msgctxt "#30126" msgid "Any Channel" msgstr "Bilo koji Kanal" msgctxt "#30130" msgid "Until space needed" msgstr "Dok prostor nije potreban" msgctxt "#30131" msgid "Until watched" msgstr "Dok nije odgledano" msgctxt "#30132" msgid "Days" msgstr "Dana" msgctxt "#30133" msgid "Always" msgstr "Uvek" msgctxt "#30134" msgid "1 week" msgstr "1 nedelja" msgctxt "#30135" msgid "Backend default" msgstr "Podrazumevano pozadine" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi podrazumevano" msgctxt "#30137" msgid "%d weeks" msgstr "%d nedelja" msgctxt "#30138" msgid "1 month" msgstr "1 mesec" msgctxt "#30139" msgid "%d months" msgstr "%d meseci" msgctxt "#30140" msgid "1 year" msgstr "1 godina" resource.language.sv_se/000077500000000000000000000000001346756700600347175ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000145021346756700600367520ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.sv_se# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Swedish (Sweden) (http://www.transifex.com/projects/p/kodi-main/language/sv_SE/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: sv_SE\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportalvärdnamn" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi-tilläggsport" msgctxt "#30002" msgid "Free-to-air only" msgstr "Endast okodade kanaler" msgctxt "#30003" msgid "Include Radio" msgstr "Inkludera radio" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Snabbt kanalbyte (stoppa inte timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Anslutningstimeout (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Importera endast tv-kanaler från grupp" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Importera endast radiokanaler från grupp" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Konvertera värdnamn till IP-adress" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Läs genresträngar (långsamt)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Väntetid efter inställning av kanal (ms)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "Aktivera inspelnings-dialog för gamla serier" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Standard inspelningslängd" msgctxt "#30015" msgid "Streaming method" msgstr "Strömningsmetod" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windowsanvändarnamn (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windowslösenord (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Använd RTSP-strömning" msgctxt "#30040" msgid "Connection" msgstr "Anslutning" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Uppspelning" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Din TVServerKodi version '%s' är för gammal. Uppgradera till '%s' eller nyare!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Din TVServerKodi version är för gammal. Uppgradera till '%s' eller nyare!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Inspelning av uppspelning misslyckades. Tom URL eller filnamn." msgctxt "#30060" msgid "All cards are busy" msgstr "Alla kort är upptagna" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanalen är krypterad" msgctxt "#30062" msgid "No video or audio detected" msgstr "Ingen bild eller ljud detekterad" msgctxt "#30063" msgid "No signal detected" msgstr "Ingen signal detekterad" msgctxt "#30064" msgid "Unknown error" msgstr "Okänt fel" msgctxt "#30065" msgid "Unable to start graph" msgstr "Kunde inte starta graf" msgctxt "#30066" msgid "Unknown channel" msgstr "Okänd kanal" msgctxt "#30067" msgid "No tuning details" msgstr "Inga inställningsdetaljer" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanalen är inte mappad till något kort" msgctxt "#30069" msgid "Card is disabled" msgstr "Kort inaktiverat" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Anslutning till slav misslyckades" msgctxt "#30071" msgid "Not the owner" msgstr "Inte ägare" msgctxt "#30072" msgid "Graph building failed" msgstr "Grafgenerering misslyckades" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Mjukvarukodare saknas" msgctxt "#30074" msgid "No free disk space" msgstr "Inget ledigt diskutrymme" msgctxt "#30075" msgid "No PMT found" msgstr "Ingen PMT hittades" msgctxt "#30100" msgid "Schedule settings" msgstr "Schemainställningar" msgctxt "#30101" msgid "Frequency" msgstr "Upprepningar" msgctxt "#30102" msgid "Airtime" msgstr "Sändningstid" msgctxt "#30103" msgid "Channels" msgstr "Kanaler" msgctxt "#30104" msgid "Keep" msgstr "Behåll" msgctxt "#30105" msgid "Record minutes before start" msgstr "Inspelningsminuter innan start" msgctxt "#30106" msgid "Record minutes after end" msgstr "Inspelningsminuter efter slut" msgctxt "#30110" msgid "Record Once" msgstr "Spela in en gång" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Spela in dagligen(Detta program)" msgctxt "#30112" msgid "Record Weekly" msgstr "Spela in veckovis" msgctxt "#30113" msgid "Record Weekends" msgstr "Spela in helger" msgctxt "#30114" msgid "Record Weekdays" msgstr "Spela in Veckodagar" msgctxt "#30115" msgid "Record every time on this channel" msgstr "Spela in varje gång på denna kanal" msgctxt "#30116" msgid "Record every time on every channel" msgstr "Spela in varje gång på varje kanal" msgctxt "#30117" msgid "Record every week at this time" msgstr "Spela in varje vecka vid denna tid" msgctxt "#30118" msgid "Record every day at this time" msgstr "Spela in varje dag vid denna tid" msgctxt "#30119" msgid "Record weekly on this channel" msgstr "Spela in varje vecka på denna kanal" msgctxt "#30120" msgid "This time" msgstr "Den här tiden" msgctxt "#30121" msgid "Anytime" msgstr "När som helst" msgctxt "#30122" msgid "Manual" msgstr "Manuell" msgctxt "#30125" msgid "This Channel" msgstr "Den här kanalen" msgctxt "#30126" msgid "Any Channel" msgstr "Vilken kanal som helst" msgctxt "#30130" msgid "Until space needed" msgstr "Tills utrymme krävs" msgctxt "#30131" msgid "Until watched" msgstr "Tills visad" msgctxt "#30132" msgid "Days" msgstr "Dagar" msgctxt "#30133" msgid "Always" msgstr "Alltid" msgctxt "#30134" msgid "1 week" msgstr "1 vecka" msgctxt "#30135" msgid "Backend default" msgstr "Backend default" msgctxt "#30136" msgid "Kodi default" msgstr "Kodi default" msgctxt "#30137" msgid "%d weeks" msgstr "%d veckor" msgctxt "#30138" msgid "1 month" msgstr "1 månad" msgctxt "#30139" msgid "%d months" msgstr "%d månader" msgctxt "#30140" msgid "1 year" msgstr "1 år" resource.language.szl/000077500000000000000000000000001346756700600344105ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000151301346756700600364410ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.szl# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Silesian (http://www.transifex.com/projects/p/kodi-main/language/szl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "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 "#30012" msgid "Default recording lifetime" msgstr "Wychodny czas trzimaniŏ nagrań" 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" msgctxt "#30104" 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" 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" resource.language.ta_in/000077500000000000000000000000001346756700600346725ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000022271346756700600367260ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.ta_in# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Tamil (India) (http://www.transifex.com/projects/p/kodi-main/language/ta_IN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ta_IN\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30012" msgid "Default recording lifetime" msgstr "முன்னிருப்பு பதிவு வாழ்நாள்" msgctxt "#30042" msgid "Playback" msgstr "பின்னணி" msgctxt "#30103" msgid "Channels" msgstr "சேனல்கள்" msgctxt "#30104" msgid "Keep" msgstr "வை" msgctxt "#30122" msgid "Manual" msgstr "கைமுறை" msgctxt "#30132" msgid "Days" msgstr "நாட்கள்" msgctxt "#30133" msgid "Always" msgstr "எப்போதும்" resource.language.te_in/000077500000000000000000000000001346756700600346765ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000013151346756700600367270ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.te_in# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Telugu (India) (http://www.transifex.com/projects/p/kodi-main/language/te_IN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: te_IN\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30132" msgid "Days" msgstr "రోజులు" resource.language.tg_tj/000077500000000000000000000000001346756700600347075ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000021411346756700600367360ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.tg_tj# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Tajik (Tajikistan) (http://www.transifex.com/projects/p/kodi-main/language/tg_TJ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: tg_TJ\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Мӯҳлати нигоҳдории сабти пешфарз" msgctxt "#30042" msgid "Playback" msgstr "Пахш" msgctxt "#30103" msgid "Channels" msgstr "Шабакаҳо" msgctxt "#30104" msgid "Keep" msgstr "Нигоҳ доштан" msgctxt "#30122" msgid "Manual" msgstr "Дастур" msgctxt "#30132" msgid "Days" msgstr "Рӯз" msgctxt "#30133" msgid "Always" msgstr "Ҳамеша" resource.language.th_th/000077500000000000000000000000001346756700600347065ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000032351346756700600367420ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.th_th# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Thai (Thailand) (http://www.transifex.com/projects/p/kodi-main/language/th_TH/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: th_TH\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "ระยะเวลาหยุดการเชื่อมต่อ (วิ)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "ค่าเริ่มต้นของ ช่วงเวลาการบันทึก" msgctxt "#30040" msgid "Connection" msgstr "การเชื่อมต่อ" msgctxt "#30042" msgid "Playback" msgstr "เล่นเพลง" msgctxt "#30064" msgid "Unknown error" msgstr "ข้อผิดพลาดที่ไม่ทราบสาเหตุ" msgctxt "#30102" msgid "Airtime" msgstr "เวลาออกอากาศ" msgctxt "#30103" msgid "Channels" msgstr "ช่องสัญญาณ" msgctxt "#30104" msgid "Keep" msgstr "คงไว้" msgctxt "#30110" msgid "Record Once" msgstr "บันทึกทันที" msgctxt "#30122" msgid "Manual" msgstr "ด้วยตนเอง" msgctxt "#30132" msgid "Days" msgstr "วัน" msgctxt "#30133" msgid "Always" msgstr "เสมอ" resource.language.tr_tr/000077500000000000000000000000001346756700600347325ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000144471346756700600367750ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.tr_tr# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/projects/p/kodi-main/language/tr_TR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: tr_TR\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal Ana Bilgisayar Adı" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi eklentisi Bağlantı noktası" msgctxt "#30002" msgid "Free-to-air only" msgstr "Sadece şifresiz" msgctxt "#30003" msgid "Include Radio" msgstr "Radyoyu dahil et" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Hızlı kanal değiştirme (Zaman kaydırma bitmiyor)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Bağlantı zaman aşımı (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Gruptan sadece TV Kanallarını içe aktar." msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Gruptan sadece Radyo Kanallarını içe aktar." msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Ana bilgisayar adını IP adresine çevir" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Tür dizelerini oku (yavaş)" msgctxt "#30010" 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 "#30012" msgid "Default recording lifetime" msgstr "Varsayılan kayıt ömrü" msgctxt "#30015" msgid "Streaming method" msgstr "Akış yöntemi" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows kullanıcı hesabı (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows parolası (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "RTSP akışını kullan" msgctxt "#30040" msgid "Connection" msgstr "Bağlantı" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Oynatım" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi '%s' sürümünüz çok eski. Lütfen '%s' veya üstüne güncelleyin!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi sürümünüz çok eski. Lütfen '%s' veya üstüne güncelleyin!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Kayıt oynatma başarısız oldu. Dosya boş/URL." msgctxt "#30060" msgid "All cards are busy" msgstr "Tüm kartlar meşgul" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kanal şifreli" msgctxt "#30062" msgid "No video or audio detected" msgstr "Video veya ses tespit edilmedi" msgctxt "#30063" msgid "No signal detected" msgstr "Sinyal tespit edilmedi" msgctxt "#30064" msgid "Unknown error" msgstr "Bilinmeyen hata" msgctxt "#30065" msgid "Unable to start graph" msgstr "Grafik başlatma yetersiz" msgctxt "#30066" msgid "Unknown channel" msgstr "Bilinmeyen kanal" msgctxt "#30067" msgid "No tuning details" msgstr "Başarısız ayarlama detayları" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kanal, kart ile eşleşmedi." msgctxt "#30069" msgid "Card is disabled" msgstr "Kart devre dışı" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Bağlantı başarısız" msgctxt "#30071" msgid "Not the owner" msgstr "Sahipsiz" msgctxt "#30072" msgid "Graph building failed" msgstr "Grafik bina başarısız oldu" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW Kodlayıcı bulunamadı" msgctxt "#30074" msgid "No free disk space" msgstr "Diskte boş yer yok" msgctxt "#30075" msgid "No PMT found" msgstr "Hiç PMT bulunamadı" msgctxt "#30100" msgid "Schedule settings" msgstr "Zamanlama ayarları" msgctxt "#30101" msgid "Frequency" msgstr "Frekans" msgctxt "#30102" msgid "Airtime" msgstr "Yayın süresi" msgctxt "#30103" msgid "Channels" msgstr "Kanallar" msgctxt "#30104" msgid "Keep" msgstr "Sakla" msgctxt "#30105" msgid "Record minutes before start" msgstr "Başlamadan önce kaydedilecek dakika" msgctxt "#30106" msgid "Record minutes after end" msgstr "Bittikten sonra kaydedilecek dakika" msgctxt "#30110" msgid "Record Once" msgstr "Tek kayıt" msgctxt "#30111" msgid "Record Daily (This program)" msgstr "Günlük Kaydet (Bu programı)" msgctxt "#30112" msgid "Record Weekly" msgstr "Haftalık Kaydet" msgctxt "#30113" 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" msgctxt "#30121" msgid "Anytime" msgstr "Hep" msgctxt "#30122" msgid "Manual" msgstr "El ile" msgctxt "#30125" msgid "This Channel" msgstr "Bu Kanal" msgctxt "#30126" msgid "Any Channel" msgstr "Herhangi Bir Kanal" msgctxt "#30130" msgid "Until space needed" msgstr "Boşluk gerekene kadar" msgctxt "#30131" msgid "Until watched" msgstr "İzlenene dek" msgctxt "#30132" msgid "Days" msgstr "Gün" msgctxt "#30133" msgid "Always" msgstr "Her zaman" msgctxt "#30134" msgid "1 week" msgstr "1 hafta" msgctxt "#30135" msgid "Backend default" msgstr "Arka uç varsayılanı" 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" resource.language.uk_ua/000077500000000000000000000000001346756700600347045ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000125061346756700600367410ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.uk_ua# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Ukrainian (Ukraine) (http://www.transifex.com/projects/p/kodi-main/language/uk_UA/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: uk_UA\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Сервер Медіапорталу" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Порт додатку Медіапортал для Kodi" msgctxt "#30002" msgid "Free-to-air only" msgstr "Лише незакодовані канали" msgctxt "#30003" msgid "Include Radio" msgstr "Додати радіо" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Швидке переключання каналів (не зупиняти зсув часу)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Затримка зв’язку (с) " msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Імпортувати тільки канали ТБ з групи" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Імпортувати тільки радіо-канали з групи" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Конвертувати назву сервера до адреси IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "Програма передач: Читати стрічку жанру (повільно)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Затримка після регулювання каналу (мс)" msgctxt "#30012" msgid "Default recording lifetime" msgstr "Термін зберігання запису за промовчанням" msgctxt "#30015" msgid "Streaming method" msgstr "Метод потоку" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Рахунок користувача Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Пароль Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Використовувати потік RTSP" msgctxt "#30040" msgid "Connection" msgstr "Зв’язок" msgctxt "#30041" msgid "MediaPortal" msgstr "МедіаПортал" msgctxt "#30042" msgid "Playback" msgstr "Відтворення" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Ваша версія TVServerKodi '%s' застаріла. Оновіть її до '%s' або вищої." msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Ваша версія TVServerKodi застаріла. Оновіть її до '%s' або вищої." msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Запис не вдався. Порожнє посилання або назва файлу." msgctxt "#30060" msgid "All cards are busy" msgstr "Всі карти зайняті" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Канал закодований" msgctxt "#30062" msgid "No video or audio detected" msgstr "Не знайдено ані відео, ані аудіо" msgctxt "#30063" msgid "No signal detected" msgstr "Не знайдено сигналу" msgctxt "#30064" msgid "Unknown error" msgstr "Невідома помилка" msgctxt "#30065" msgid "Unable to start graph" msgstr "Не вдалося запустити граф" msgctxt "#30066" msgid "Unknown channel" msgstr "Невідомий канал" msgctxt "#30067" msgid "No tuning details" msgstr "Немає деталей налаштування" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Канал не віднесений до жодної карти" msgctxt "#30069" msgid "Card is disabled" msgstr "Карта виключена" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Зв’язок не вдався" msgctxt "#30071" msgid "Not the owner" msgstr "Не власник" msgctxt "#30072" msgid "Graph building failed" msgstr "Побудова графу не вдалася" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Пропущено SW Encoder" msgctxt "#30074" msgid "No free disk space" msgstr "Немає вільного місця на диску" msgctxt "#30075" msgid "No PMT found" msgstr "Не знайдено PMT" msgctxt "#30103" msgid "Channels" msgstr "Канали" msgctxt "#30104" msgid "Keep" msgstr "Залишити" msgctxt "#30110" msgid "Record Once" msgstr "Записати один раз" msgctxt "#30121" msgid "Anytime" msgstr "Будь-коли" msgctxt "#30122" msgid "Manual" msgstr "Уручну" msgctxt "#30126" msgid "Any Channel" msgstr "Будь-який канал" msgctxt "#30132" msgid "Days" msgstr "дн." msgctxt "#30133" msgid "Always" msgstr "Завжди" resource.language.uz_uz/000077500000000000000000000000001346756700600347545ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000015221346756700600370050ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.uz_uz# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Uzbek (Uzbekistan) (http://www.transifex.com/projects/p/kodi-main/language/uz_UZ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: uz_UZ\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30103" msgid "Channels" msgstr "Kanallar" msgctxt "#30122" msgid "Manual" msgstr "Qo‘lda" msgctxt "#30132" msgid "Days" msgstr "Kun" msgctxt "#30133" msgid "Always" msgstr "Doim" resource.language.vi_vn/000077500000000000000000000000001346756700600347215ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000104761346756700600367620ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.vi_vn# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Vietnamese (Viet Nam) (http://www.transifex.com/projects/p/kodi-main/language/vi_VN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: vi_VN\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Tên miền Mediaportal" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Cổng Mediaportal" msgctxt "#30002" msgid "Free-to-air only" msgstr "Chỉ qua không dây" msgctxt "#30003" msgid "Include Radio" msgstr "Bao gồm Đài phát thanh" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "Chuyển kênh nhanh (không tắt timeshift)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "Thời gian chờ kết nối (s)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "Chỉ nhập các kênh TV từ nhóm" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "Chỉ nhập các kênh Radio từ nhóm" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "Chuyển đổi tên miền sang địa chỉ IP" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "EPG: Thêm thể loại chương trình (chậm)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "Thời gian chờ sau khi dò kênh (mili giây)" msgctxt "#30015" msgid "Streaming method" msgstr "Phương pháp truyền streaming" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Tài khoản vào Windows (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Mật khẩu vào Windows (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "Sử dụng giao thức RTSP (thời gian thực)" msgctxt "#30040" msgid "Connection" msgstr "Kết nối" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "Xem lại" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "Phiên bản TVServerKodi '%s' đã quá cũ. Hãy nâng cấp lên '%s' hoặc mới hơn!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "Phiên bản TVServerKodi đã quá cũ. Hãy nâng cấp lên '%s' hoặc mới hơn!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "Thu chương trình bị lỗi. Kiểm tra lại địa chỉ URL chứa file" msgctxt "#30060" msgid "All cards are busy" msgstr "Tất cả thẻ truy cập đều bận" msgctxt "#30061" msgid "Channel is scrambled" msgstr "Kênh bị mã hoá" msgctxt "#30062" msgid "No video or audio detected" msgstr "Không có tín hiệu video hoặc audio" msgctxt "#30063" msgid "No signal detected" msgstr "Không có tín hiệu" msgctxt "#30064" msgid "Unknown error" msgstr "Xảy ra lỗi" msgctxt "#30065" msgid "Unable to start graph" msgstr "Không thể bắt đầu biểu đồ " msgctxt "#30066" msgid "Unknown channel" msgstr "Không tìm thấy kênh" msgctxt "#30067" msgid "No tuning details" msgstr "Không có thông tin dò" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "Kênh chưa được đặt với bất kỳ thẻ nào" msgctxt "#30069" msgid "Card is disabled" msgstr "Thẻ đã bị loại" msgctxt "#30070" msgid "Connection to slave failed" msgstr "Lỗi kết nối tới slave" msgctxt "#30071" msgid "Not the owner" msgstr "Không sở hữu" msgctxt "#30072" msgid "Graph building failed" msgstr "Lỗi vẽ biểu đồ" msgctxt "#30073" msgid "SW Encoder missing" msgstr "Không thấy bộ mã hoá SW" msgctxt "#30074" msgid "No free disk space" msgstr "Không có đĩa trống" msgctxt "#30075" msgid "No PMT found" msgstr "Không tìm thấy PMT" msgctxt "#30103" msgid "Channels" msgstr "Kênh" msgctxt "#30122" msgid "Manual" msgstr "Thủ công" msgctxt "#30132" msgid "Days" msgstr "Ngày" msgctxt "#30133" msgid "Always" msgstr "Luôn luôn" resource.language.zh_cn/000077500000000000000000000000001346756700600347015ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000137411346756700600367400ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.zh_cn# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Chinese (China) (http://www.transifex.com/projects/p/kodi-main/language/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal 主机名" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi 插件端口" msgctxt "#30002" msgid "Free-to-air only" msgstr "仅非加密频道" msgctxt "#30003" msgid "Include Radio" msgstr "包含电台" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "快速频道切换(不停止时光平移)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "连接超时(秒)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "仅从组导入电视频道" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "仅从组导入电台频道" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "主机名转换为 IP 地址" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "电子节目单:读取类型字串(慢)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "换台延迟时间(毫秒)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "启用老系列录像对话框" msgctxt "#30012" msgid "Default recording lifetime" msgstr "默认录像生命期" msgctxt "#30015" msgid "Streaming method" msgstr "流传输方式" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "Windows 用户帐号(SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "Windows 密码(SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "使用 RTSP 流" msgctxt "#30040" msgid "Connection" msgstr "连接" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "播放" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi 版本“%s”太旧,请升级到“%s”或更高版本!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "TVServerKodi 版本太旧,请升级到“%s”或更高版本!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "录像播放失败。文件名 URL 空。" msgctxt "#30060" msgid "All cards are busy" msgstr "所有卡都忙" msgctxt "#30061" msgid "Channel is scrambled" msgstr "频道被占用" msgctxt "#30062" msgid "No video or audio detected" msgstr "未检测到音视频" msgctxt "#30063" msgid "No signal detected" msgstr "未检测到信号" msgctxt "#30064" msgid "Unknown error" msgstr "未知错误" msgctxt "#30065" msgid "Unable to start graph" msgstr "无法启动 graph" msgctxt "#30066" msgid "Unknown channel" msgstr "未知频道" msgctxt "#30067" msgid "No tuning details" msgstr "无调谐器详细信息" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "频道未映射到任何卡" msgctxt "#30069" msgid "Card is disabled" msgstr "卡被禁用" msgctxt "#30070" msgid "Connection to slave failed" msgstr "连接到从机失败" msgctxt "#30071" msgid "Not the owner" msgstr "非所有者" msgctxt "#30072" msgid "Graph building failed" msgstr "Graph 创建失败" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW 编码器丢失" msgctxt "#30074" msgid "No free disk space" msgstr "无可用磁盘空间" msgctxt "#30075" msgid "No PMT found" msgstr "未找到 PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "预约设置" msgctxt "#30101" msgid "Frequency" msgstr "频次" msgctxt "#30102" msgid "Airtime" msgstr "播出时间" msgctxt "#30103" msgid "Channels" msgstr "频道" msgctxt "#30104" 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 "所有时间" msgctxt "#30122" msgid "Manual" msgstr "手动" msgctxt "#30125" msgid "This Channel" msgstr "此频道" msgctxt "#30126" msgid "Any Channel" msgstr "所有频道" msgctxt "#30130" msgid "Until space needed" msgstr "直到需要空间" msgctxt "#30131" msgid "Until watched" msgstr "直到已观看" msgctxt "#30132" msgid "Days" msgstr "天" msgctxt "#30133" msgid "Always" msgstr "总是" msgctxt "#30134" msgid "1 week" msgstr "1 周" msgctxt "#30135" msgid "Backend default" msgstr "后端默认" 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 年" resource.language.zh_tw/000077500000000000000000000000001346756700600347335ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/languagestrings.po000066400000000000000000000141521346756700600367670ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/language/resource.language.zh_tw# Kodi Media Center language file # Addon Name: MediaPortal PVR Client # Addon id: pvr.mediaportal.tvserver # Addon Provider: Marcel Groothuis msgid "" msgstr "" "Project-Id-Version: KODI Main\n" "Report-Msgid-Bugs-To: https://github.com/xbmc/xbmc/issues/\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kodi Translation Team\n" "Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/kodi-main/language/zh_TW/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" msgctxt "#30000" msgid "Mediaportal Hostname" msgstr "Mediaportal主機名稱" msgctxt "#30001" msgid "Mediaportal Kodi plugin Port" msgstr "Mediaportal Kodi外掛程式端口" msgctxt "#30002" msgid "Free-to-air only" msgstr "僅觀看免費頻道" msgctxt "#30003" msgid "Include Radio" msgstr "包含廣播電台" msgctxt "#30004" msgid "Fast channel switching (don't stop timeshift)" msgstr "快速頻道切換(不停止時光平移)" msgctxt "#30005" msgid "Connect timeout (s)" msgstr "連接超時(秒)" msgctxt "#30006" msgid "Import only TV Channels from group" msgstr "僅從群組導入電視頻道" msgctxt "#30007" msgid "Import only Radio Channels from group" msgstr "僅從群組導入廣播頻道" msgctxt "#30008" msgid "Convert hostname to IP-adress" msgstr "轉換主機名稱到IP位址" msgctxt "#30009" msgid "EPG: Read genre strings (slow)" msgstr "電子節目表:讀取類別字符串流 (慢)" msgctxt "#30010" msgid "Wait time after tuning a channel (ms)" msgstr "頻道調整後的等待時間 (毫秒)" msgctxt "#30011" msgid "Enable old series recording dialog" msgstr "開啟舊系列影集的錄影對話框" msgctxt "#30012" msgid "Default recording lifetime" msgstr "預設的錄影生命周期" msgctxt "#30015" msgid "Streaming method" msgstr "串流方式" msgctxt "#30016" msgid "Windows user account (SMB)" msgstr "視窗使用帳戶 (SMB)" msgctxt "#30017" msgid "Windows password (SMB)" msgstr "視窗密碼 (SMB)" msgctxt "#30018" msgid "Use RTSP streaming" msgstr "使用RTSP串流" msgctxt "#30040" msgid "Connection" msgstr "連接" msgctxt "#30041" msgid "MediaPortal" msgstr "MediaPortal" msgctxt "#30042" msgid "Playback" msgstr "播放" msgctxt "#30050" msgid "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!" msgstr "您的TVServerKodi版本'%s' 太舊了。請升級到'%s' 或更高版本!" msgctxt "#30051" msgid "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!" msgstr "您的TVServerKodi版本太舊了。請升級到'%s' 或更高版本!" msgctxt "#30052" msgid "Recording playback failed. Empty URL of filename." msgstr "錄影撥放失敗。檔案名稱是失效的URL連結。" msgctxt "#30060" msgid "All cards are busy" msgstr "所有的電視卡都在忙碌中" msgctxt "#30061" msgid "Channel is scrambled" msgstr "頻道已加密" msgctxt "#30062" msgid "No video or audio detected" msgstr "偵測不到影片或音樂" msgctxt "#30063" msgid "No signal detected" msgstr "偵測不到訊號" msgctxt "#30064" msgid "Unknown error" msgstr "未知的錯誤" msgctxt "#30065" msgid "Unable to start graph" msgstr "無法啟動影像" msgctxt "#30066" msgid "Unknown channel" msgstr "未知的頻道" msgctxt "#30067" msgid "No tuning details" msgstr "無選台的詳細訊息" msgctxt "#30068" msgid "Channel is not mapped to any card" msgstr "頻道沒有映射到任何電視卡" msgctxt "#30069" msgid "Card is disabled" msgstr "電視卡已禁用" msgctxt "#30070" msgid "Connection to slave failed" msgstr "連接到附屬設備失敗" msgctxt "#30071" msgid "Not the owner" msgstr "不是所有者" msgctxt "#30072" msgid "Graph building failed" msgstr "影像建立失敗" msgctxt "#30073" msgid "SW Encoder missing" msgstr "SW編碼器丟失" msgctxt "#30074" msgid "No free disk space" msgstr "沒有可用的磁碟空間" msgctxt "#30075" msgid "No PMT found" msgstr "找不到PMT" msgctxt "#30100" msgid "Schedule settings" msgstr "排程設定" msgctxt "#30101" msgid "Frequency" msgstr "頻率" msgctxt "#30102" msgid "Airtime" msgstr "播放時間" msgctxt "#30103" msgid "Channels" msgstr "頻道" msgctxt "#30104" 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 "任何時間" msgctxt "#30122" msgid "Manual" msgstr "手動" msgctxt "#30125" msgid "This Channel" msgstr "此頻道" msgctxt "#30126" msgid "Any Channel" msgstr "任何頻道" msgctxt "#30130" msgid "Until space needed" msgstr "直到空間不足" msgctxt "#30131" msgid "Until watched" msgstr "直到看完" msgctxt "#30132" msgid "Days" msgstr "天" msgctxt "#30133" msgid "Always" msgstr "總是" msgctxt "#30134" msgid "1 week" msgstr "1週" msgctxt "#30135" msgid "Backend default" msgstr "後端預設值" 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年" pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/settings.xml000066400000000000000000000036011346756700600310460ustar00rootroot00000000000000 pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/000077500000000000000000000000001346756700600276135ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.ace/000077500000000000000000000000001346756700600313065ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.ace/720p/000077500000000000000000000000001346756700600317765ustar00rootroot00000000000000DialogRecordSettings.xml000066400000000000000000000132541346756700600365250ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.ace/720p 10 1 Animation_DialogPopup 110 0 30 720 shadow.png C3FFFFFF 1140 0 30 720 shadow.png C3FFFFFF 140 0 1000 720 darkgrey.png $VAR[value_texturecolor] Animation_DefaultGlass 140 0 1000 80 black.png 90FFFFFF header label 215 23 852 30 Font_32 FFFFFAF0 center center Animation_Fade 215 130 825 2 left divider.png 215 90 852 30 center center $VAR[value_headercolor] Font_30 true 215 143 0 0 852 25 Font_19 center center FFFFFAF0 0 35 852 25 Font_19 center center FFFFFAF0 215 200 825 2 left divider.png 140 210 0 1000 290 9001 9001 Frequency 10 10 Airtime 11 11 Channels 12 12 Separator left divider.png Keep 13 13 Record minutes before start 14 14 Record minutes after end 15 15 140 600 40 1000 black.png 90FFFFFF 140 600 15 10 horizontal 0 Ok Button 500 SettingsBottomButton 2 2 Cancel Button 500 SettingsBottomButton font12_title 1 1 pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.aeon.nox.5/000077500000000000000000000000001346756700600324465ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.aeon.nox.5/1080i/000077500000000000000000000000001346756700600332075ustar00rootroot00000000000000DialogRecordSettings.xml000066400000000000000000000113611346756700600377330ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.aeon.nox.5/1080i 10 1 399 210 dialogeffect background image 0 0 1172 660 dialogs/default/bg.png header label 30 32 1112 40 font15_title center dialogheader 30 86 790 542 dialogs/default/inner.png 45 100 0 0 738 30 grey font14 true 0 45 738 25 font14 grey selected 0 90 738 25 font14 grey selected 31 230 789 2 left separator2.png 31 245 0 6 789 540 23 23 9001 9001 Frequency 789 Airtime 789 Channels 789 Separator 789 2 left separator2.png Keep 789 Record minutes before start 789 Record minutes after end 789 824 88 -1 -2 312 542 dialogs/default/inner.png 9001 23 23 9001 Ok Button 310 Cancel Button 310 pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.aeonmq5/000077500000000000000000000000001346756700600321235ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.aeonmq5/720p/000077500000000000000000000000001346756700600326135ustar00rootroot00000000000000DialogRecordSettings.xml000066400000000000000000000133511346756700600373400ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.aeonmq5/720p 10 1 Animation_DialogPopup 110 0 30 720 shadow.png C3FFFFFF 1140 0 30 720 shadow.png C3FFFFFF 140 0 1000 720 darkgrey.png $VAR[value_texturecolor] Animation_DefaultGlass 140 0 1000 80 black.png 90FFFFFF header label 215 23 852 30 Font_32 FFFFFAF0 center center Animation_Fade 215 130 825 2 left divider.png 215 90 852 30 center center $VAR[value_headercolor] Font_30 true 215 143 0 0 852 25 Font_19 center center FFFFFAF0 0 35 852 25 Font_19 center center FFFFFAF0 215 200 825 2 left divider.png 140 210 0 1000 290 9001 9001 Frequency 10 10 Airtime 11 11 Channels 12 12 Separator left divider.png Keep 13 13 Record minutes before start 14 14 Record minutes after end 15 15 140 600 40 1000 black.png 90FFFFFF 140 600 15 10 horizontal 0 Ok Button 500 SettingsBottomButton 2 2 Cancel Button 500 SettingsBottomButton font12_title 1 1 pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.amber/000077500000000000000000000000001346756700600316445ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.amber/1080i/000077500000000000000000000000001346756700600324055ustar00rootroot00000000000000DialogRecordSettings.xml000066400000000000000000000105541346756700600371340ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.amber/1080i 10 1 OpenClose DimBG DialogOpenClose 300 100 1220 880 dialogs/DialogBG.png header label 396 145 1005 Orange center InfoTitle 396 220 0 0 1005 36 center center White2 Details true 0 40 1005 36 Details center center White2 0 80 1005 36 Details center center White2 396 360 0 1005 900 9001 9001 Frequency 10 10 Airtime 11 11 Channels 12 12 Separator 20 Keep 13 13 Record minutes before start 14 14 Record minutes after end 15 15 590 850 15 10 horizontal 0 Ok Button 300 center center 2 2 Cancel Button 300 center center 1 1 pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.confluence/000077500000000000000000000000001346756700600326775ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.confluence/720p/000077500000000000000000000000001346756700600333675ustar00rootroot00000000000000DialogRecordSettings.xml000066400000000000000000000136501346756700600401160ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.confluence/720p 10 1 240 100 dialogeffect background image 0 0 800 500 DialogBack.png Dialog Header image 40 16 720 40 dialogheader.png header label 40 20 720 30 font13_title center center selected black Close Window button 710 15 64 32 - PreviousMenu DialogCloseButton-focus.png DialogCloseButton.png 10 10 10 10 system.getbool(input.enablemouse) 40 100 720 30 font12_title left center white 40 60 355 30 font12_title left center white 400 60 355 30 font12_title left center white 40 140 5 720 290 9001 9001 Frequency 720 40 10 10 Airtime 720 40 11 11 Channels 720 40 12 12 Separator 2 720 stretch separator2.png Keep 720 40 13 13 Record minutes before start 720 40 14 14 Record minutes after end 720 40 15 15 190 435 15 10 horizontal Ok Button 200 center center 2 2 Cancel Button 200 center center 1 1 pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.estuary/000077500000000000000000000000001346756700600322525ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.estuary/1080i/000077500000000000000000000000001346756700600330135ustar00rootroot00000000000000DialogRecordSettings.xml000066400000000000000000000104621346756700600375400ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.estuary/1080i 10 200 105 Animation_DialogPopupOpenClose 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 pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/000077500000000000000000000000001346756700600323155ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/720p/000077500000000000000000000000001346756700600330055ustar00rootroot00000000000000DialogRecordSettings.xml000066400000000000000000000162331346756700600375340ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/720p 10 1 240 100 background image 0 0 800 500 DialogBack.png Dialog Header image 40 16 720 40 dialogheader.png header label 40 20 720 30 font13_title center center FFEB9E17 black Close Window button 710 15 64 32 - PreviousMenu DialogCloseButton-focus.png DialogCloseButton.png 10 10 10 10 system.getbool(input.enablemouse) 40 100 720 30 font12_title left center FFF1F1F1 40 60 355 30 font12_title left center FFF1F1F1 400 60 355 30 font12_title left center FFF1F1F1 40 140 5 720 290 9001 9001 Frequency 720 40 10 10 Airtime 720 40 11 11 Channels 720 40 12 12 Separator 2 720 stretch separator2.png Keep 720 40 13 13 Record minutes before start 720 40 14 14 Record minutes after end 720 40 15 15 190 435 15 10 horizontal Ok Button font13_title FF999999 FFF1F1F1 200 center center 2 2 button-focus.png button-nofocus.png Cancel Button font13_title FF999999 FFF1F1F1 200 center center 1 1 button-focus.png button-nofocus.png pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/media/000077500000000000000000000000001346756700600333745ustar00rootroot00000000000000DialogBack.png000066400000000000000000000205471346756700600360130ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/mediaPNG  IHDR\rf pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx]oGO5i}CN䝉` E`f۹?wrfv*A<1 -͌[eg/\U]UMFn>@Pj6[}sSFU61&qnذ:zx!Gv˹y/6ڿiLƑvm[DT?@gD`fo"FD퍽Bcۀ#"DdODm+c +S`W"r."?k+86Dd_D|h<A.ED^W\}_UU4u?;/GUU2[_#"jϟS:w_UUi4PUSU;ݿ[afl63;LkF{ܛl2L^o -ϸMM}.} G:L~|vrr鑈S+so}~wǓ_VUz=kʘH~OHsv|CUU0qKq_7 #j ۃۜ/|p6wy;d8Pן~y㬚fB˯~_~`;j|`/X[;wƟ|˗qtt'9ȏ"rYTuukGϟ?suu59&`x"'.[ YGXD)7=P'_狗k *S9 P֋}͒3#h( Dz h {q2<ș֥4[>~` -nF3&YgRx?}uY_XϾVB|4CJ6րZ(dzlpm9c'M%q(4i eWAX+&&& (|xh!["=>)0<8r7u(MYw\Cx窟v,CV3^&_A묒ϯ$"ny8h*}a 6X0RT]]%{^dujtBB~? FQ6CxMGcM_{F{6 cFuyiFLb0O,?dy_5&sM"# ǬfkfMlLƟywlN'VU($0_&5kXYHC-,Ej6sӆR?4/s}{wqқsKݚ~_6m~D[[PR7ږVro}[5mK{Q_'TpBw/<PJի*{lI[̪R'd/V-t<*3U',[SR T EI@g*U- Q XZX7mGݶKھ/Л vLGrg6 ;+LM=cuܠ-X%w"PkН@c *v=h"р 4z2г^:^ XT"C 9i3 83{1рL6b2 y6ԅ9},"`=_}dαXhOR*_O`ی(ӜP}jM0 x_а ]+;+pڀ4M99˧V`"TR0!ߍU5hXH4mQZ hc3Q/4G L${t& HV%!=63U:ixN 2S/<;D _eT`,Axzn 'X*x"0>@ɥecNߒK6 -ȲtQ(!(,=b `5 S `V:6ì  z. Y? (~}ą}VLKiL@(ЭO"T "8`8hoŁ (?6U=L@,+BٱJ`@~iрí8!P 4]~#T G@+$ ['c `xxB@oob G>@VBlVUxkne0 ,Ra@ /K& 0*V8X ) (Wr,L @?뷚 Z4``v 0J~{43`8^bAAq9_= e)J05~HU5n`߉ /Pl4@&  Td~Z[ 0Xd~Y 2 z*a&`h!Pr>ϚW26ō 6ǐ @7K?6W!@Q?{B$n}]8BlܟJ{=[Q0 wB6%m~)szE` l8!G |>;|+ %Uzci+7lS"`h@MY0 q.g=%*gJw4`'EPYz] BT`X8b] g]+?' MA4$-IM TX _h*'+__QkG%\l%@w2k iT |?6nwF ,f: W/VOр3;=F}ӂJ J~DZ]Џ]U/Y|y0 ?4)  @cwڞ_";=x@ko7JӴp$ @-\`:]ڵ=S -@WYt#-/|/UF 7_m-`ή] e {I$) S%$b:[ n[ jSX0FlB}AH-v˪/8X~`9IB-f~}!01hkBP*.K ,Œ@>eռs ̅AŬ9_mV: T~<8!Sre vٿMx )? Ph@ݮ;gT\_ArJpU@0%z& B7uX,J&q@(fK. xͽ0H9ߍU2 1n|(I[  @`X~}D g@l [eo0*@tN4й F;8W^&c=N=_z꽼/uk}M(CߡǍ+9ߡǹf{y_}M(wdmiHDFVn:ŋQM8So"zl"؄!7>k>G>ŎRI70M}b[&7)9i"#4[Cikb{?i M3O1mX?snR*"2޳ 3L1[~[?Zi8MRv].(uK?GsC A3O?pl˦W"jJ}P2!71;n:DUק!~3Lvvvn]^^oaR;c|Sd dRS$W@g kfSN&CإekwfN Omu^gM&sy#"׮=p`bpyqq?~痗oSo#_bISd?7#h˝Z2we*\{$ߗrmdf:ZwdI|0x""V&1NNN><<~ڹ0rJB-TJI`0~m?IUe)a ̭*r% [ݥߧ8ѣ?+ a[/GǏ 狦N 1%&;t*{K!i47)Vj s lT%+5ԠǏwŋȹgy7@<]/"Ǐ?=z ԏJ;cIS0׏vK`IUN%YIeN}Q$Mf&mJMvd =|pϾ?N&g"79K "2["rۊD;>>M^~=Ta6%=iL%6ׇts@I$9BR޹#%dI_{ݛZΝ;ǧ~tt'9࿰TUȃÃvww?H\__OOOD䥈b ۏgȖ}}$ s>f.Mvg Wv;6dqve{SCj.ʾwT Y_<3[/ㆅ,3~~?@%q4(>,ae@[IENDB`DialogCloseButton-focus.png000066400000000000000000000047601346756700600405300ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/mediaPNG  IHDR@ ~ cHRMz%u0`:o_F IDATxk]EkfsnblJ`i`~V5! _ )(jM|Ĥ1?T (QD Ġ`bR}{3kafmo[0&&0d߳gYkZ̨.R<;]\I]8"'Қu23 U03" h(Xa!*VN1x| a`xB1CxYK;WtG\ȉCăHιAߛjpj@5b XaD,!ZDŝ$0|4{~\RG``j3MaC,сx!^``+]+  k YaM.#]zTZ @:+7%R NuRպ1 +,R\^P{' E>ZhŞͳ!b(dkFQPW0Di?d,A.[%-H`/{/ؽ^^G]k> $&9/ h3Zʸð"A.'wHÀru'n`)4tW %6m"@o,.]ؽ^oβe!~VZ;/TVeyo{K6%5:8ސ0F{3s?OswR%eYyf ҥK)˒}qż{ݵ=5p?"!#5/[JGb*є`( bĮEο-ϟϺuضmeYvZ^zy>ϮK_⪀oW;bt""AT})fJYJ5VT&)\'"DAWonaܹ\rDe]'/"eT "6P4VJ)"JQ RH+xa41П O?z*,T)>yO˕[rcŊ Cc۔S5kJeՈ%*@W|qBLLKWuzXѥ`"S# tml5n`ꫯ9 Xpޥ(0 2k15jh5ͼ.zh}wHKaqfb4 L^ ?:Op_uVV^ŋYp!z+\s >ɡ !Wqh,3Xxp`b0uY ƘRfS%0@1 L*0@À2+ӈG_SٰaCn6^֯_)˒3<-[-4F,tP$V̐'eW5 .`YYŬx> tzw%0_oQd۳S,Xsgk@gƍL+T $dCeIf5͹4YC㇁+y+$nm਩gL0w 9HU@Cqw4B~ϼs?W]?RnF/_>rP  XهBjM^ǁAHF3l)#f4DQ;BiƇ|5]p1dlO_ș7oDꥧ)w=d9րПF>T,$%R)b1Cd,Lb$q| ZWΊqߚl\]{$Z@Hсt{Нt;/3j.ZԽUURB 1m1;Pk@TOޟ USǛFMzŶ4zgFf > Zm. @B C Ji0VbRTνV< < |xifGX " NZoHT[Hmz-ܺ) 6OH^l_?+x̦@7tic\%Elrϗ<~mqxxx+nsj<`,wSdv($=&u5#-Ik˖UQg;x8L@6N@,rl*] U-h (:!'2 W> @](u7^>"03@UsmߚoEχeEckܡ#x#| {ġe3mg_IENDB`DialogCloseButton.png000066400000000000000000000034721346756700600374120ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/mediaPNG  IHDR@ ~ cHRMz%u0`:o_FIDATxMh[VuW&m48ϐlBEPdtݻrs!0 وj1M&,f䑗Nm+a<[{>PJE7@:t0R"4M$DD"iiH)1 !RJ[/ES)E%yy8y^qRGjRqZ%)D)UHWu]FGGWB` ( ^)(Aqx@ 0?DwT$|Oe|7 }6% >@0MsI#,C7Z&"׵l6udd$;;;.]4q"ڏ?ܜ:tOOV!O>_?xF?K Vk!W%e%N:abbb͛xyؘf[҃y\Grȑ9JuS=5;88qp۷o#Lִ8?~={̜={vxYM̵b2AJ) PaRA(4 Ԇ ҧO```q8y$6m:q"4e!RJQ !εZ"\avP(`q$U) !ضmr{{{FmmΒwΝ;i3gLNAU a$RJ!)2tnܢ=`>k:-G444lٲy޼yCSSy^UfP$?14MY|޷m;<Ϸ, ˲H&$UIGe#C\e\mܹ[wtt̬}Gն TlƲ,#L5k[) 7 Lg\:fohlltwwS({.ǎcd2nݺŅ q]J[ tzEqxtnI] q<<=z1sڵfx"ߟwuuu޽l6Kww7DۿJYjnq8SYzefe6BBPlG |t&\z8~f:;;'3ŋqvō7xٷ߿7|7cyl.W)7|T*UcW]X(-=|f߾}9ooo[J&S(Z^F=z\Rb"'vɎ;Vт(Z V"@__|^XX8---9˲JTF}w\MM_)'( .yo߾'Rj>N@g)%mmmRuT Dn%7ޖ#d2ISST?G)\$["ϣ333LLLho/߁wJrT*E]]7n\3OMZZXX`qqexx3iTooy<0GV"@!H-`!\LJ)U#X@]HD:lׄ"?<_B]3$W5=S\O'|k!˟ A:MCg+_ J/[~ E;IENDB`button-focus.png000066400000000000000000000055651346756700600364660ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/mediaPNG  IHDR@] cHRMz%u0`:o_F IDATx]= B_gU{G>V:̑wZz$A VԚOj~"_g @L0dv5evݧv{H1-:-Dթ2MS>\v-r۹"MޣUND0wssOLD ܾiW"k[v$#?Dž7;z ˒¿'K 'O̺gIs.!=`ng#"E;-w{* M>H.F借=7tV qZ#P8D0ρAvB`iD\`s14ۻuTPBL(XsB_4p3@69}n' C) ϒ$0?M O.g,?GNnRkֈ sr$p.p w{n:@C<:H5!2 z!:N*D]߆z?o.O3 KI R%禃ߐׯ~ 'o$^#[ !u φEs"pĻ03d!}@x,~Yournd`F~S5$E/EyB[\ O}P:r@GH_3]57 2`uEo+ 0c9_C'{>HS/3()Z = ̾T-$`Fd܋}8`k{HB90:䐛'x nv\׆ݜ\LILz+~ _T,NX@18' ˊ g‚:ͭYm'\y,'{L*wлAJhPEkwU2^FGzjs:nng霹]*}ۆrQX?D !@9t-GHBS(*wZ0{Հ龁%I-}Pε1{ L~]C eB4x%pg;c/΋k)9NHiHBDÚD2p| \\ˮ$HOVxʹf;A[,nyT?*>u%bXCa%na[ڀ쐪GPhǝ7x/,zn3Ui 1Gd1 7i Tv< O eR1AiJ DDAh! !lO!i^ALL BLMLy].@O:tMil cMȂA o b`ynp c^=4h _..z䕛[LFLlR7& B1ݘ-~Īj.vQnȉR׃uB6 &P"C]M [Wؕ?L=5Hj_F4D׿+ #TI\$Yos3L~YTJ®*Yw |EsG[€Y hˊ dv9_&P=`' (_7/{8Wj2 ZB"{[Wz С탭 2# ۣ)ƚ1CMZ"erY] uP^ZT@zAp ʫ"}ٖvÈ[Wgx~c$>hc H@ <*s&pki~G&o?]ɾ^6v}ni$@8"]}ص?~UY\wm@)P R5)`sfLPL#+Dwҿs#? Xzu*~H.Հ&M20Gn͞?Ӯr\/())%H ^<"JM_@2d`ޕaB b5s^yp@U`D`us4흀5$ T{"ʃIn W v0Г.%/w\ T"P{ ,Bͭ( 3X'+? =VՀJ*P<:UПɇuP?@~?!t [l=p;'P :\ ޡ,.wX^uJVjƚs=Wy>I@i=Q;}F!@Aᚑ (u"x-_OZ eXpћ;ݞ!`G eHEK~OVF:Iazg}D)p]Ik@+xHv_\82FD?vbf![nۮ)8su>NsECzi Gi! 'luZyi)vws;|c\&*\~g"~}pss~"W/3p IENDB`button-nofocus.png000066400000000000000000000004071346756700600370110ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/mediaPNG  IHDR@ cHRMz%u0`:o_FIDATxݱ P CA&d V}u:IRUc}fn3& @ @ @ @ @ @ @ NHbWp E IENDB`dialogheader.png000066400000000000000000000117221346756700600364360ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/mediaPNG  IHDR dK OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-bKGD pHYs+tIME69 IDATx]r8 $<ge%%`^hPGbf\&A #iUu!C7{-M,JI i~[`iGAt*׬eV2eʔ)SL.%Ȕ)SL~aI;{/u 5#Ӓ=^J^&zk$+:$}8#]d%:`<~Ȧ1a,u ՃNEA(ξZ{~R @5vu2]+ @^G\Mex{<FO ef8<6)CN pB?o` H2@R50ki.13}[#o*6-fṪ~@9g`^Tz=/yWRC(5쭰^Ad. tې`֩AƦeiԅbԐh&vO%no 䁮|q,~2jBVʺ, &׽ vf`?0}>KT嗎j嘰j-Ow:XC"=~ @}hE )7 {iy\n^~.a[f(.s<'Z!Nz}}4@}0}_2z6ȭ誓Yyp$yH}+F4AR @3Z0-H Fz.:DA@ZȳE@%@4?&}֎Qo [gWGB}hܕ ?`/d~2 n3I' JG< @J$xiy z3 yD\CYy/!*>/`q9h D,Nz ^:Ҹk .d7#+smiEJ9-xl ѻW,}| зΎ ֚SN}0W(r+o4r.]'LJCsV?"A Mo&;(dU'w; տjC-o\b }2cz`׫U?:¹Hg A38ZZf #ד=\co:zMc=:titmo覎Lu?ڶ%QٶuҶmo}|xh퇞ZתQTITIENDB`separator2.png000066400000000000000000000060341346756700600361100ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.fallback/mediaPNG  IHDRQ[ pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FGIDATxI0 EcզEN/~D<P%ˇW]jp=}gj-Ƙ̚3螻cצu#QmHR|Ƚk m鷅6֑_5g܌^)2^,Véi;@`g#d plIp@"Px"IqR2izgbL_fO3GQnXr,V3ߥ}#>u&2d>2ߘFݼw˳IENDB`pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.transparency/000077500000000000000000000000001346756700600332675ustar00rootroot000000000000001080p/000077500000000000000000000000001346756700600337605ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.transparencyDialogRecordSettings.xml000066400000000000000000000143521346756700600405660ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/pvr.mediaportal.tvserver/resources/skins/skin.transparency/1080p 10 1 490 240 dialogeffect 0 0 1000 600 black.png header label 40 30 920 30 center center font-32 blue 40 70 920 2 separator.png dialogeffect4 930 37 20 20 close-window-focus.png close-window-nofocus.png Dialog.Close(pvrtimersetting) Skin.HasSetting(TouchScreenMode) 40 70 0 0 920 30 Font_19 center center white 0 30 920 30 Font_19 center center white 0 60 920 30 Font_19 center center white 40 160 920 1 separator.png 40 170 5 920 290 9001 9001 Frequency 920 40 10 10 Airtime 920 40 11 11 Channels 920 40 12 12 Separator 920 1 separator.png Keep 920 40 13 13 Record minutes before start 920 40 14 14 Record minutes after end 920 40 15 15 290 525 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 290 525 200 35 button-focus.png Conditional Conditional Control.HasFocus(1) | Control.HasFocus(2) pvr.mediaportal.tvserver-3.5.18-Leia/src/000077500000000000000000000000001346756700600202535ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/Cards.cpp000066400000000000000000000073061346756700600220210ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include "Cards.h" #include "uri.h" #include "utils.h" #include "client.h" #include "DateTime.h" using namespace std; using namespace ADDON; bool CCards::ParseLines(vector& lines) { if (lines.empty()) { KODI->Log(LOG_DEBUG, "No card settings found."); return false; } for (vector::iterator it = lines.begin(); it < lines.end(); ++it) { string data = *it; if (!data.empty()) { vector fields; Card card; uri::decode(data); Tokenize(data, fields, "|"); // field 0 = idCard // field 1 = devicePath // field 2 = name // field 3 = priority // field 4 = grabEPG // field 5 = lastEpgGrab (2000-01-01 00:00:00 = infinite) // field 6 = recordingFolder // field 7 = idServer // field 8 = enabled // field 9 = camType // field 10 = timeshiftingFolder // field 11 = recordingFormat // field 12 = decryptLimit // field 13 = preload // field 14 = CAM // field 15 = NetProvider // field 16 = stopgraph // field 17 = UNC path recording folder (when shared) // field 18 = UNC path timeshift folder (when shared) if (fields.size() < 17) return false; card.IdCard = atoi(fields[0].c_str()); card.DevicePath = fields[1]; card.Name = fields[2]; card.Priority = atoi(fields[3].c_str()); card.GrabEPG = stringtobool(fields[4]); if (card.LastEpgGrab.SetFromDateTime(fields[5]) == false) { card.LastEpgGrab.SetFromTime(MPTV::cUndefinedDate); } card.RecordingFolder = fields[6]; card.IdServer = atoi(fields[7].c_str()); card.Enabled = stringtobool(fields[8]); card.CamType = atoi(fields[9].c_str()); card.TimeshiftFolder = fields[10]; card.RecordingFormat = atoi(fields[11].c_str()); card.DecryptLimit = atoi(fields[12].c_str()); card.Preload = stringtobool(fields[13]); card.CAM = stringtobool(fields[14]); card.NetProvider = atoi(fields[15].c_str()); card.StopGraph = stringtobool(fields[16]); if (fields.size() >= 19) // since TVServerKodi build 115 { card.RecordingFolderUNC = fields[17]; card.TimeshiftFolderUNC = fields[18]; if (card.RecordingFolderUNC.empty()) { KODI->Log(LOG_NOTICE, "Warning: no recording share defined in the TVServerKodi settings for card '%s'", card.Name.c_str()); } if (card.TimeshiftFolderUNC.empty()) { KODI->Log(LOG_NOTICE, "Warning: no timeshift share defined in the TVServerKodi settings for card '%s'", card.Name.c_str()); } } else { card.RecordingFolderUNC = ""; card.TimeshiftFolderUNC = ""; } push_back(card); } } return true; } bool CCards::GetCard(int id, Card& card) { for (unsigned int i = 0; i < size(); i++) { if (at(i).IdCard == id) { card = at(i); return true; } } card.IdCard = -1; return false; } pvr.mediaportal.tvserver-3.5.18-Leia/src/Cards.h000066400000000000000000000037651346756700600214730ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include "DateTime.h" /** * MediaPortal TVServer card settings ("card" table in the database) */ typedef struct Card { int IdCard; std::string DevicePath; std::string Name; int Priority; bool GrabEPG; MPTV::CDateTime LastEpgGrab; std::string RecordingFolder; std::string RecordingFolderUNC; int IdServer; bool Enabled; int CamType; std::string TimeshiftFolder; std::string TimeshiftFolderUNC; int RecordingFormat; int DecryptLimit; bool Preload; bool CAM; int NetProvider; bool StopGraph; } Card; class CCards: public std::vector { public: /** * \brief Parse the multi-line string response from the TVServerKodi plugin command "GetCardSettings" * The data is stored in "struct Card" item. * * \param lines Vector with response lines * \return True on success, False on failure */ bool ParseLines(std::vector& lines); /** * \brief Return the data for the card with the given id * \param id The card id * \param card Return value: card data or NULL if not found. * \return True on success, False on failure */ bool GetCard(int id, Card& card); }; pvr.mediaportal.tvserver-3.5.18-Leia/src/DateTime.cpp000066400000000000000000000074301346756700600224570ustar00rootroot00000000000000/* * Copyright (C) 2005-2013 Team Kodi * https://kodi.tv * * 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 . * */ #include #include #include #include #include "DateTime.h" namespace MPTV { void CDateTime::InitLocale(void) { // Follow system default for date and time formatting // because we cannot access XBMC's locale settings from the PVR addon setlocale(LC_ALL, ""); } CDateTime::CDateTime() { InitLocale(); memset(&m_time, 0, sizeof(m_time)); } CDateTime::CDateTime(const time_t& dateTime) { InitLocale(); SetFromTime(dateTime); } CDateTime::CDateTime(const struct tm& dateTime) { InitLocale(); m_time = dateTime; } CDateTime::~CDateTime() {} int CDateTime::GetDay() const { return m_time.tm_mday; } int CDateTime::GetMonth() const { return (m_time.tm_mon + 1); } int CDateTime::GetYear() const { return (m_time.tm_year + 1900); } int CDateTime::GetHour() const { return (m_time.tm_hour); } int CDateTime::GetMinute() const { return (m_time.tm_min); } int CDateTime::GetSecond() const { return (m_time.tm_sec); } int CDateTime::GetDayOfWeek() const { return (m_time.tm_wday); } time_t CDateTime::GetAsTime() const { time_t retval; struct tm tm_time = m_time; retval = mktime (&tm_time); if(retval < 0) retval = 0; return retval; } bool CDateTime::SetFromDateTime(const std::string& dateTime) { int year, month ,day; int hour, minute, second; int count; count = sscanf(dateTime.c_str(), "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second); if(count != 6) return false; m_time.tm_hour = hour; m_time.tm_min = minute; m_time.tm_sec = second; m_time.tm_year = year - 1900; m_time.tm_mon = month - 1; m_time.tm_mday = day; // Make the other fields empty: m_time.tm_isdst = -1; m_time.tm_wday = 0; m_time.tm_yday = 0; // call mktime to set the timeinfo->tm_wday field mktime(&m_time); return true; } void CDateTime::SetFromTime(const time_t& time) { m_time = *localtime( &time ); } void CDateTime::SetFromTM(const struct tm& time) { m_time = time; } void CDateTime::GetAsLocalizedDate(std::string & strDate) const { const unsigned int bufSize = 64; char buffer[bufSize]; strftime(buffer, bufSize, "%x", &m_time); strDate = buffer; } void CDateTime::GetAsLocalizedTime(std::string & strTime) const { const unsigned int bufSize = 64; char buffer[bufSize]; strftime(buffer, bufSize, "%H:%M", &m_time); strTime = buffer; } int CDateTime::operator -(const CDateTime& right) const { time_t leftTime = GetAsTime(); time_t rightTime = right.GetAsTime(); return (int) (leftTime-rightTime); } const CDateTime& CDateTime::operator =(const time_t& right) { SetFromTime(right); return *this; } const CDateTime& CDateTime::operator =(const tm& right) { m_time = right; return *this; } bool CDateTime::operator ==(const time_t& right) const { time_t left = GetAsTime(); return (left == right); } const CDateTime& CDateTime::operator +=(const int seconds) { time_t left = GetAsTime(); left += seconds; SetFromTime(left); return *this; } time_t CDateTime::Now() { time_t now; time(&now); return now; } } // namespace MPTV pvr.mediaportal.tvserver-3.5.18-Leia/src/DateTime.h000066400000000000000000000045401346756700600221230ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2013 Team Kodi * https://kodi.tv * * 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 . * */ #include #include namespace MPTV { const time_t cUndefinedDate = 946681200; ///> 01-01-2000 00:00:00 in time_t class CDateTime { public: CDateTime(); CDateTime(const struct tm& time); CDateTime(const time_t& time); virtual ~CDateTime(); int GetDay() const; int GetMonth() const; int GetYear() const; int GetHour() const; int GetMinute() const; int GetSecond() const; int GetDayOfWeek() const; int GetMinuteOfDay() const; time_t GetAsTime(void) const; /** * @brief Converts the stored datetime value to a string with the date representation for current locale * @param strDate the date string (return value) */ void GetAsLocalizedDate(std::string& strDate) const; /** * @brief Converts the stored datetime value to a string with the time representation for current locale * @param strDate the date string (return value) */ void GetAsLocalizedTime(std::string& strTime) const; /** * @brief Sets the date and time from a C# DateTime string * Assumes the usage of somedatetimeval.ToString("u") in C# */ bool SetFromDateTime(const std::string& dateTime); /** * @brief Sets the date and time from a time_t value */ void SetFromTime(const time_t& time); void SetFromTM(const struct tm& time); int operator -(const CDateTime& right) const; const CDateTime& operator =(const time_t& right); const CDateTime& operator =(const tm& right); bool operator ==(const time_t& right) const; const CDateTime& operator +=(const int seconds); static time_t Now(); private: void InitLocale(void); struct tm m_time; }; } // namespace MPTV pvr.mediaportal.tvserver-3.5.18-Leia/src/FileUtils.h000066400000000000000000000022511346756700600223240ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2014 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 . * */ #include namespace OS { class CFile { public: static bool Exists(const std::string& strFileName, long* errCode = NULL); }; #ifdef TARGET_WINDOWS_DESKTOP /** * Return the location of the Program Data folder * @param[in,out] programData Reference to a string that will receive the program data path * @return true on success, false on failure */ bool GetProgramData(std::string& programData); #endif }; pvr.mediaportal.tvserver-3.5.18-Leia/src/GUIDialogRecordSettings.cpp000066400000000000000000000273231346756700600254120ustar00rootroot00000000000000/* * Copyright (C) 2005-2013 Team Kodi * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Kodi; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "client.h" #include "GUIDialogRecordSettings.h" #include "libKODI_guilib.h" #include "timers.h" #include "utils.h" #include "DateTime.h" #include "p8-platform/util/StringUtils.h" /* Dialog item identifiers */ #define BUTTON_OK 1 #define BUTTON_CANCEL 2 #define SPIN_CONTROL_FREQUENCY 10 #define SPIN_CONTROL_AIRTIME 11 #define SPIN_CONTROL_CHANNELS 12 #define SPIN_CONTROL_KEEP 13 #define SPIN_CONTROL_PRERECORD 14 #define SPIN_CONTROL_POSTRECORD 15 #define LABEL_PROGRAM_TITLE 20 #define LABEL_PROGRAM_START_TIME 21 #define LABEL_PROGRAM_CHANNEL 22 using namespace std; using namespace MPTV; CGUIDialogRecordSettings::CGUIDialogRecordSettings(const PVR_TIMER &timerinfo, cTimer& timer, const std::string& channelName) : m_spinFrequency(NULL), m_spinAirtime(NULL), m_spinChannels(NULL), m_spinKeep(NULL), m_spinPreRecord(NULL), m_spinPostRecord(NULL), m_frequency(Once), m_airtime(ThisTime), m_channels(ThisChannel), m_timerinfo(timerinfo), m_timer(timer) { CDateTime startTime(m_timerinfo.startTime); CDateTime endTime(m_timerinfo.endTime); startTime.GetAsLocalizedTime(m_startTime); startTime.GetAsLocalizedDate(m_startDate); endTime.GetAsLocalizedTime(m_endTime); m_title = m_timerinfo.strTitle; m_channel = channelName; // 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.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", "skin.fallback", false, true); if (m_window) { m_window->m_cbhdl = this; m_window->CBOnInit = OnInitCB; m_window->CBOnFocus = OnFocusCB; m_window->CBOnClick = OnClickCB; m_window->CBOnAction = OnActionCB; } } CGUIDialogRecordSettings::~CGUIDialogRecordSettings() { GUI->Window_destroy(m_window); } bool CGUIDialogRecordSettings::OnInit() { // Display the recording details in the window m_window->SetControlLabel(LABEL_PROGRAM_TITLE, m_title.c_str()); string strTimeSlot = m_startDate + " " + m_startTime + " - " + m_endTime; m_window->SetControlLabel(LABEL_PROGRAM_START_TIME, strTimeSlot.c_str()); m_window->SetControlLabel(LABEL_PROGRAM_CHANNEL, m_channel.c_str()); // Init spin controls m_spinFrequency = GUI->Control_getSpin(m_window, SPIN_CONTROL_FREQUENCY); m_spinAirtime = GUI->Control_getSpin(m_window, SPIN_CONTROL_AIRTIME); m_spinChannels = GUI->Control_getSpin(m_window, SPIN_CONTROL_CHANNELS); m_spinKeep = GUI->Control_getSpin(m_window, SPIN_CONTROL_KEEP); m_spinPreRecord = GUI->Control_getSpin(m_window, SPIN_CONTROL_PRERECORD); m_spinPostRecord = GUI->Control_getSpin(m_window, SPIN_CONTROL_POSTRECORD); if (!m_spinFrequency || !m_spinAirtime || !m_spinChannels || !m_spinKeep || !m_spinPreRecord || !m_spinPostRecord) return false; // Populate Frequency spin control for (int i = 0; i < 5; i++) { // show localized recording options m_spinFrequency->AddLabel(KODI->GetLocalizedString(30110 + i), i); } // set the default value m_spinFrequency->SetValue(CGUIDialogRecordSettings::Once); // Populate Airtime spin control string strThisTime = KODI->GetLocalizedString(30120); strThisTime += "(" + m_startTime + ")"; m_spinAirtime->AddLabel(strThisTime.c_str(), CGUIDialogRecordSettings::ThisTime); m_spinAirtime->AddLabel(KODI->GetLocalizedString(30121), CGUIDialogRecordSettings::AnyTime); // Set the default values m_spinAirtime->SetValue(CGUIDialogRecordSettings::ThisTime); m_spinAirtime->SetVisible(false); // Populate Channels spin control for (int i = 0; i < 2; i++) { // show localized recording options m_spinChannels->AddLabel(KODI->GetLocalizedString(30125 + i), i); } // Set the default values 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(KODI->GetLocalizedString(30130 + i), i); } // Set the default values m_spinKeep->SetValue(TvDatabase::Always); // Populate PreRecord spin control std::string marginStart; marginStart = StringUtils::Format("%d (%s)", m_timerinfo.iMarginStart, KODI->GetLocalizedString(30136)); m_spinPreRecord->AddLabel(KODI->GetLocalizedString(30135), -1); m_spinPreRecord->AddLabel(marginStart.c_str(), m_timerinfo.iMarginStart); //value from XBMC m_spinPreRecord->SetValue(m_timerinfo.iMarginStart); // Set the default value m_spinPreRecord->AddLabel("0", 0); m_spinPreRecord->AddLabel("3", 3); m_spinPreRecord->AddLabel("5", 5); m_spinPreRecord->AddLabel("7", 7); m_spinPreRecord->AddLabel("10", 10); m_spinPreRecord->AddLabel("15", 15); // Populate PostRecord spin control std::string marginEnd; marginEnd = StringUtils::Format("%d (%s)", m_timerinfo.iMarginEnd, KODI->GetLocalizedString(30136)); m_spinPostRecord->AddLabel(KODI->GetLocalizedString(30135), -1); m_spinPostRecord->AddLabel(marginEnd.c_str(), m_timerinfo.iMarginEnd); //value from XBMC m_spinPostRecord->SetValue(m_timerinfo.iMarginEnd); // Set the default value m_spinPostRecord->AddLabel("0", 0); m_spinPostRecord->AddLabel("3", 3); m_spinPostRecord->AddLabel("5", 5); m_spinPostRecord->AddLabel("7", 7); m_spinPostRecord->AddLabel("10", 10); m_spinPostRecord->AddLabel("15", 15); m_spinPostRecord->AddLabel("20", 20); m_spinPostRecord->AddLabel("30", 30); m_spinPostRecord->AddLabel("45", 45); m_spinPostRecord->AddLabel("60", 60); return true; } bool CGUIDialogRecordSettings::OnClick(int controlId) { switch(controlId) { case BUTTON_OK: // save value from GUI, then FALLS THROUGH TO CANCEL m_frequency = (RecordingFrequency) m_spinFrequency->GetValue(); m_airtime = (RecordingAirtime) m_spinAirtime->GetValue(); m_channels = (RecordingChannels) m_spinChannels->GetValue(); /* Update the Timer settings */ UpdateTimerSettings(); m_retVal = 1; Close(); break; case BUTTON_CANCEL: m_retVal = 0; Close(); break; /* Limit the available options based on the SPIN settings * MediaPortal does not support all combinations */ case SPIN_CONTROL_FREQUENCY: m_frequency = (RecordingFrequency) m_spinFrequency->GetValue(); switch (m_frequency) { case Once: case Weekends: case WeekDays: m_spinAirtime->SetVisible(false); m_spinChannels->SetVisible(false); break; case Weekly: m_spinAirtime->SetVisible(true); m_spinChannels->SetVisible(false); break; case Daily: m_spinAirtime->SetVisible(true); m_spinChannels->SetVisible(true); break; } break; case SPIN_CONTROL_CHANNELS: m_channels = (RecordingChannels) m_spinChannels->GetValue(); //switch (m_frequency) //{ // case Once: // case Weekends: // case WeekDays: // case Weekly: // m_channels = ThisChannel; // m_spinChannels->SetValue(m_channels); // break; //} /* This time on any channel is not supported by MediaPortal */ if (m_channels == AnyChannel) m_spinAirtime->SetValue(AnyTime); break; case SPIN_CONTROL_AIRTIME: m_airtime = (RecordingAirtime) m_spinAirtime->GetValue(); if (m_airtime == ThisTime) m_spinChannels->SetValue(ThisChannel); break; } return true; } bool CGUIDialogRecordSettings::OnInitCB(GUIHANDLE cbhdl) { CGUIDialogRecordSettings* dialog = static_cast(cbhdl); return dialog->OnInit(); } bool CGUIDialogRecordSettings::OnClickCB(GUIHANDLE cbhdl, int controlId) { CGUIDialogRecordSettings* dialog = static_cast(cbhdl); return dialog->OnClick(controlId); } bool CGUIDialogRecordSettings::OnFocusCB(GUIHANDLE cbhdl, int controlId) { CGUIDialogRecordSettings* dialog = static_cast(cbhdl); return dialog->OnFocus(controlId); } bool CGUIDialogRecordSettings::OnActionCB(GUIHANDLE cbhdl, int actionId) { CGUIDialogRecordSettings* dialog = static_cast(cbhdl); return dialog->OnAction(actionId); } bool CGUIDialogRecordSettings::Show() { if (m_window) return m_window->Show(); return false; } void CGUIDialogRecordSettings::Close() { if (m_window) { GUI->Control_releaseSpin(m_spinFrequency); GUI->Control_releaseSpin(m_spinAirtime); GUI->Control_releaseSpin(m_spinChannels); GUI->Control_releaseSpin(m_spinKeep); GUI->Control_releaseSpin(m_spinPreRecord); GUI->Control_releaseSpin(m_spinPostRecord); m_window->Close(); } } int CGUIDialogRecordSettings::DoModal() { if (m_window) m_window->DoModal(); return m_retVal; } bool CGUIDialogRecordSettings::OnFocus(int controlId) { return true; } /* * This callback is called by XBMC before executing its internal OnAction() function * Returning "true" tells XBMC that we already handled the action, returing "false" * passes action to the XBMC internal OnAction() function */ bool CGUIDialogRecordSettings::OnAction(int actionId) { //KODI->Log(ADDON::LOG_DEBUG, "%s: action = %i\n", __FUNCTION__, actionId); if (actionId == ADDON_ACTION_CLOSE_DIALOG || actionId == ADDON_ACTION_PREVIOUS_MENU || actionId == 92 /* Back */) return OnClick(BUTTON_CANCEL); else /* return false to tell XBMC that it should take over */ return false; } void CGUIDialogRecordSettings::UpdateTimerSettings(void) { switch(m_frequency) { case Once: m_timer.SetScheduleRecordingType(TvDatabase::Once); break; case Weekends: m_timer.SetScheduleRecordingType(TvDatabase::Weekends); break; case WeekDays: m_timer.SetScheduleRecordingType(TvDatabase::WorkingDays); break; case Weekly: if (m_airtime == ThisTime) m_timer.SetScheduleRecordingType(TvDatabase::Weekly); else m_timer.SetScheduleRecordingType(TvDatabase::WeeklyEveryTimeOnThisChannel); break; case Daily: switch (m_airtime) { case ThisTime: m_timer.SetScheduleRecordingType(TvDatabase::Daily); break; case AnyTime: if (m_channels == ThisChannel) m_timer.SetScheduleRecordingType(TvDatabase::EveryTimeOnThisChannel); else m_timer.SetScheduleRecordingType(TvDatabase::EveryTimeOnEveryChannel); break; } } m_timer.SetKeepMethod((TvDatabase::KeepMethodType) m_spinKeep->GetValue()); m_timer.SetPreRecordInterval(m_spinPreRecord->GetValue()); m_timer.SetPostRecordInterval(m_spinPostRecord->GetValue()); } pvr.mediaportal.tvserver-3.5.18-Leia/src/GUIDialogRecordSettings.h000066400000000000000000000051121346756700600250470ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2013 Marcel Groothuis * Copyright (C) 2005-2013 Team Kodi * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Kodi; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "client.h" #include "timers.h" class CGUIDialogRecordSettings { public: CGUIDialogRecordSettings(const PVR_TIMER &timerinfo, cTimer& timer, const std::string& channelName); virtual ~CGUIDialogRecordSettings(); bool Show(); void Close(); int DoModal(); // returns -1 => load failed, 0 => cancel, 1 => ok private: // Following is needed for every dialog: CAddonGUIWindow* m_window; int m_retVal; // -1 => load failed, 0 => cancel, 1 => ok bool OnClick(int controlId); bool OnFocus(int controlId); bool OnInit(); bool OnAction(int actionId); static bool OnClickCB(GUIHANDLE cbhdl, int controlId); static bool OnFocusCB(GUIHANDLE cbhdl, int controlId); static bool OnInitCB(GUIHANDLE cbhdl); static bool OnActionCB(GUIHANDLE cbhdl, int actionId); // Specific for this dialog: CAddonGUISpinControl* m_spinFrequency; CAddonGUISpinControl* m_spinAirtime; CAddonGUISpinControl* m_spinChannels; CAddonGUISpinControl* m_spinKeep; CAddonGUISpinControl* m_spinPreRecord; CAddonGUISpinControl* m_spinPostRecord; void UpdateTimerSettings(void); /* Enumerated types corresponding with the spincontrol values */ enum RecordingFrequency { Once = 0, Daily = 1, Weekly = 2, Weekends = 3, WeekDays = 4 }; enum RecordingAirtime { ThisTime = 0, AnyTime = 1 }; enum RecordingChannels { ThisChannel = 0, AnyChannel = 1 }; /* Private members */ std::string m_channel; std::string m_startTime; std::string m_startDate; std::string m_endTime; std::string m_title; RecordingFrequency m_frequency; RecordingAirtime m_airtime; RecordingChannels m_channels; const PVR_TIMER &m_timerinfo; cTimer& m_timer; }; pvr.mediaportal.tvserver-3.5.18-Leia/src/GenreTable.cpp000066400000000000000000000077231346756700600230000ustar00rootroot00000000000000/* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 . * */ #include #include "client.h" #include "GenreTable.h" #include "tinyxml.h" using namespace ADDON; using namespace std; bool CGenreTable::LoadGenreXML(const std::string &filename) { TiXmlDocument xmlDoc; if (!xmlDoc.LoadFile(filename)) { KODI->Log(LOG_ERROR, "Unable to load %s: %s at line %d", filename.c_str(), xmlDoc.ErrorDesc(), xmlDoc.ErrorRow()); return false; } KODI->Log(LOG_NOTICE, "Opened %s to read genre string to type/subtype translation table", filename.c_str()); TiXmlHandle hDoc(&xmlDoc); TiXmlElement* pElem; TiXmlHandle hRoot(0); const char* sGenreType = NULL; const char* sGenreSubType = NULL; genre_t genre; // block: genrestrings pElem = hDoc.FirstChildElement("genrestrings").Element(); // should always have a valid root but handle gracefully if it does if (!pElem) { KODI->Log(LOG_ERROR, "Could not find element"); return false; } //This should hold: pElem->Value() == "genrestrings" // save this for later hRoot=TiXmlHandle(pElem); // iterate through all genre elements TiXmlElement* pGenreNode = hRoot.FirstChildElement("genre").Element(); //This should hold: pGenreNode->Value() == "genre" if (!pGenreNode) { KODI->Log(LOG_ERROR, "Could not find element"); return false; } for (; pGenreNode != NULL; pGenreNode = pGenreNode->NextSiblingElement("genre")) { const char* sGenreString = pGenreNode->GetText(); if (sGenreString) { sGenreType = pGenreNode->Attribute("type"); sGenreSubType = pGenreNode->Attribute("subtype"); if ((sGenreType) && (strlen(sGenreType) > 2)) { if(sscanf(sGenreType + 2, "%5x", &genre.type) != 1) genre.type = 0; } else { genre.type = 0; } if ((sGenreSubType) && (strlen(sGenreSubType) > 2 )) { if(sscanf(sGenreSubType + 2, "%5x", &genre.subtype) != 1) genre.subtype = 0; } else { genre.subtype = 0; } if (genre.type > 0) { KODI->Log(LOG_DEBUG, "Genre '%s' => 0x%x, 0x%x", sGenreString, genre.type, genre.subtype); m_genremap.insert(std::pair(sGenreString, genre)); } } } return true; } void CGenreTable::GenreToTypes(string& strGenre, int& genreType, int& genreSubType) { // The xmltv plugin from the MediaPortal TV Server can return genre // strings in local language (depending on the external TV guide source). // The only way to solve this at the XMBC side is to transfer the // genre string to XBMC or to let this plugin (or the TVServerKodi // plugin) translate it into XBMC compatible (numbered) genre types string m_genre = strGenre; if(!m_genremap.empty() && !m_genre.empty()) { GenreMap::iterator it; std::transform(m_genre.begin(), m_genre.end(), m_genre.begin(), ::tolower); it = m_genremap.find(m_genre); if (it != m_genremap.end()) { genreType = it->second.type; genreSubType = it->second.subtype; } else { KODI->Log(LOG_DEBUG, "EPG: No mapping of '%s' to genre type/subtype found.", strGenre.c_str()); genreType = EPG_GENRE_USE_STRING; genreSubType = 0; } } else { genreType = 0; genreSubType = 0; } } pvr.mediaportal.tvserver-3.5.18-Leia/src/GenreTable.h000066400000000000000000000025101346756700600224320ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 . */ #include #include typedef struct genre { int type; int subtype; } genre_t; typedef std::map GenreMap; class CGenreTable { public: CGenreTable(const std::string &filename) { LoadGenreXML(filename); }; bool LoadGenreXML(const std::string &filename); /** * \brief Convert a genre string into a type/subtype combination using the data in the GenreMap * \param strGenre (in) * \param genreType (out) * \param genreSubType (out) */ void GenreToTypes(std::string& strGenre, int& genreType, int& genreSubType); private: GenreMap m_genremap; }; pvr.mediaportal.tvserver-3.5.18-Leia/src/README000066400000000000000000000157671346756700600211530ustar00rootroot00000000000000XBMC MediaPortal TV-client ('MPTV') PVR Add-on ---------------------------------------------- Supported platforms (pvrclient): - Windows - Linux - OSX (should work, not tested by me) Dependencies: - MediaPortal TVServer 1.1.x or 1.2.x. May work also on newer versions - TVServerXBMC v1.1.0.100 or higher THIS IS A PRELIMINARY README AND IS SUBJECT TO CHANGE!!! Written by: Marcel Groothuis Project's homepage: http://www.scintilla.utwente.nl/~marcelg/xbmc/ Latest version available at: project homepage The MediaPortal TV-Server plugin "TVServerXMBC", status updates, screenshots, last-minute patches can be found at: http://www.scintilla.utwente.nl/~marcelg/xbmc/ 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 any later version. See the file LICENSE.GPL for more information. ---------------------------------------------- General description: This is a PVR Add-on for XBMC to access/control the MediaPortal TV-Server backend from XBMC. It consists of two plugins: one for XBMC-PVR (1) and one for the MediaPortal TV Server (2). 1. PVR client "XBMC_MPTV_win32.pvr" (for Windows): This is the addon in this directory. After building the XBMC solution (and the "pvrclient_mptv") this plugin file should be in addons\pvr\MediaPortal\ The MediaPortal TV Server is written in C#, making it difficult for XBMC to control it directly. The TV Server connection of this AddOn depends on a special plugin at the TV Server side, called TVServerXBMC. 2. TV Server plugin TVServerXBMC.dll/.exe (Windows only): The TVServerXBMC plugin for the TV Server provides a socket interface to XBMC to control the TV Server. You can download it separately from: http://www.scintilla.utwente.nl/~marcelg/xbmc (It is not included by default in the XBMC pvr-testing2 branch, because it will introduce a dependency on Visual C#.) Please read the "readme.txt" file included in the TVServerXBMC zip files for more information. ---------------------------------------------- Detailed instructions: (Preliminary...) 1. Install MediaPortal & MediaPortal TV Server (1.1.0) (for older versions, you will need to recompile the TVServerXBMC plugin from source) 2. Use MediaPortal to make sure that the TV Server is working fine 3. Download the TVServerXBMC plugin for the TV Server (see my website) 4. Run the TVServerXBMC.exe (standalone version of the TV Server plugin) and test it (see the readme.txt file included in the TVServerXBMC zip file). Test for example the commands "ListTVChannels", "TimeshiftChannel" (id is the first field returned by "ListTVChannels"). The "TimeshiftChannel" command should return a URL like "rtsp://xxx.xxx.xxx.xx/stream2.0" on a successful timeshift start. You can test this URL in VLC player. This should work before proceeding to the next steps. 5. Test the rtsp stream in XBMC (you can just use the standard 9.11) Create a playlist file "rtsp-stream.m3u" with the rtsp:// URL as content and start it from inside XBMC. Example contents rtsp-stream.m3u: ----- rtsp://192.168.2.5/steam2.0 ----- When this is all working fine, you can finally build XBMC-pvr-testing2: 6. Build the XBMC solution in VC Express (pvr-testing2) (check if XBMC_MPTV_win32.pvr was created succesfully in addons\pvr\MediaPortal) ---------------------------------------------- MediaPortal PVR-addon settings: Names are taken from addons/pvr/MediaPortal/settings.xml host: "Mediaportal Hostname" IP-address of the machine that runs the TVServerXBMC tool Default: 127.0.0.1 (localhost) port: "Mediaportal XBMC plugin Port" Port number for the TVServerXBMC. Default: 9596 ftaonly: "Free-to-air only" Fetch/show only Free-to-air channels from MediaPortal TV Default: false useradio: "Include Radio" Fetch also radio channels Default: true convertchar: "Character Set Conversion" Enable character conversion to UTF-8. Does nothing. Not yet implemented. Default: false timeout: "Connect timeout (s)" Timeout on XBMC<->TVServerXBMC communication. After the selected timeout, XBMC won't wait any longer for an answer from TVServerXBMC and abort the selected action. Bottleneck is the timeshift start for TV channels. This can take a long time, so don't make this value too small. Default: 6 tvgroup: "Import only TV Channels from group" Allows you to fetch only the TV channels in a specific MediaPortal TVServer group. E.g. you can create a "XBMC" group at the TVServer side that contains only the TV channels that you want to appear at the XBMC side. Default: radiogroup: "Import only Radio Channels from group" Allows you to fetch only the radio channels in a specific MediaPortal TVServer group. E.g. you can create a "XBMC" group at the TVServer side that contains only the radio channels that you want to appear at the XBMC side. Default: resolvertsphostname: "Convert hostname to IP-address" Resolve the TVServer hostname in the rtsp:// streaming URLs to an ip-address at the TVServerXBMC side. May help you with connection problems. Default: true readgenre: "EPG: Read genre strings (slow)" Try to translate the EPG genre strings from MediaPortal into XBMC compatible genre id's. However, depending on your EPG source, MediaPortal may return strings in your local language. In this case, you can skip the genre translation via readgenre=false. The current implementation translates only English strings as workaround for the mismatch between XBMC's genre ids and MediaPortals genre strings. Default: false (= don't read and translate the genre strings) sleeponrtspurl: "Wait after tuning a channel (ms)" Adds an additional waiting time between the request to start a timeshift for the selected channel and opening the rtsp:// stream in XBMC. You may need this in case XBMC tries to open the returned rtsp:// stream before it is really available. Typical symptom: the channel doesn't play the first time, but it does play the second time. Default: 0 (milliseconds) userecordingsdir: "Play recordings directly (no streaming)" By default, the recordings are played via rtsp:// streaming, which is not needed when the TV Server and XBMC are running on the same machine. When you enable this option, XBMC will use the filename of the recording for playback instead of the rtsp::// url. Default: false recordingsdir: "Mediaportal recordings directory" The previous setting can also be used on a different pc by sharing the recordings directory over the network. You can use this option to specify where XBMC can find the recordings. Default: ---------------------------------------------- Troubleshooting: TODO... You can reach me on the XBMC forum, user: margro or via IRC: #xbmc, #xbmc-pvr user: margro ---------------------------------------------- Links: MediaPortal: http://www.team-mediaportal.com TVServer plugin: http://www.scintilla.utwente.nl/~marcelg/xbmc pvr.mediaportal.tvserver-3.5.18-Leia/src/Socket.cpp000066400000000000000000000366611346756700600222230ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . * */ #include "libXBMC_addon.h" #include "utils.h" #include #include "p8-platform/os.h" #include "client.h" #include "Socket.h" using namespace std; using namespace ADDON; using namespace MPTV; namespace MPTV { /* Master defines for client control */ #define RECEIVE_TIMEOUT 6 //sec Socket::Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol) { _sd = INVALID_SOCKET; _family = family; _domain = domain; _type = type; _protocol = protocol; _port = 0; memset (&_sockaddr, 0, sizeof( _sockaddr ) ); #ifdef TARGET_WINDOWS memset(&_wsaData, 0, sizeof(_wsaData)); #endif } Socket::Socket() { // Default constructor, default settings _sd = INVALID_SOCKET; _family = af_inet; _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) { _hostname = host; return true; } bool Socket::close() { if (is_valid()) { if ((_sd != SOCKET_ERROR) && (_sd != INVALID_SOCKET)) { closesocket(_sd); } _sd = INVALID_SOCKET; return true; } return false; } bool Socket::create() { close(); return true; } bool Socket::bind ( const unsigned short port ) { if (!is_valid()) { 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 ); int bind_return = ::bind(_sd, (sockaddr*)(&_sockaddr), sizeof(_sockaddr)); if ( bind_return == -1 ) { errormessage( getLastError(), "Socket::bind" ); return false; } return true; } bool Socket::listen() const { if (!is_valid()) { return false; } int listen_return = ::listen (_sd, SOMAXCONN); //This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h. //linux 128//MAXCONNECTIONS =1 if (listen_return == -1) { errormessage( getLastError(), "Socket::listen" ); return false; } return true; } bool Socket::accept ( Socket& new_socket ) const { if (!is_valid()) { return false; } socklen_t addr_length = sizeof( _sockaddr ); new_socket._sd = ::accept(_sd, const_cast( (const sockaddr*) &_sockaddr), &addr_length ); #ifdef TARGET_WINDOWS if (new_socket._sd == INVALID_SOCKET) #else if (new_socket._sd <= 0) #endif { errormessage( getLastError(), "Socket::accept" ); return false; } return true; } int Socket::send ( const std::string& data ) { return Socket::send( (const char*) data.c_str(), (const unsigned int) data.size()); } int Socket::send ( const char* data, const unsigned int len ) { fd_set set_w, set_e; struct timeval tv; int result; if (!is_valid()) { return 0; } // fill with new data tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&set_w); FD_ZERO(&set_e); FD_SET(_sd, &set_w); FD_SET(_sd, &set_e); result = select(FD_SETSIZE, &set_w, NULL, &set_e, &tv); if (result < 0) { KODI->Log(LOG_ERROR, "Socket::send - select failed"); close(); return 0; } if (FD_ISSET(_sd, &set_w)) { KODI->Log(LOG_ERROR, "Socket::send - failed to send data"); close(); return 0; } int status = ::send(_sd, data, len, 0 ); if (status == -1) { errormessage( getLastError(), "Socket::send"); KODI->Log(LOG_ERROR, "Socket::send - failed to send data"); close(); return 0; } return status; } int Socket::sendto ( const char* data, unsigned int size, bool sendcompletebuffer) { int sentbytes = 0; int i; do { i = ::sendto(_sd, data, size, 0, (const struct sockaddr*) &_sockaddr, sizeof( _sockaddr ) ); if (i <= 0) { errormessage( getLastError(), "Socket::sendto"); osCleanup(); return i; } sentbytes += i; } while ( (sentbytes < (int) size) && (sendcompletebuffer == true)); return i; } int Socket::receive ( std::string& data, unsigned int minpacketsize ) const { char * buf = NULL; int status = 0; if (!is_valid()) { return 0; } buf = new char [ minpacketsize + 1 ]; memset ( buf, 0, minpacketsize + 1 ); status = receive( buf, minpacketsize, minpacketsize ); data = buf; delete[] buf; return status; } //Receive until error or \n bool Socket::ReadLine (string& line) { fd_set set_r, set_e; timeval timeout; int retries = 6; char buffer[2048]; if (!is_valid()) return false; while (true) { size_t pos1 = line.find("\r\n", 0); if (pos1 != std::string::npos) { line.erase(pos1, string::npos); return true; } timeout.tv_sec = RECEIVE_TIMEOUT; timeout.tv_usec = 0; // fill with new data FD_ZERO(&set_r); FD_ZERO(&set_e); FD_SET(_sd, &set_r); FD_SET(_sd, &set_e); int result = select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout); if (result < 0) { KODI->Log(LOG_DEBUG, "%s: select failed", __FUNCTION__); errormessage(getLastError(), __FUNCTION__); close(); return false; } if (result == 0) { if (retries != 0) { KODI->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__, retries); retries--; continue; } else { KODI->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", __FUNCTION__); return false; } } result = recv(_sd, buffer, sizeof(buffer) - 1, 0); if (result < 0) { KODI->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__); errormessage(getLastError(), __FUNCTION__); close(); return false; } buffer[result] = 0; line.append(buffer); } return true; } int Socket::receive ( std::string& data) const { char buf[MAXRECV + 1]; int status = 0; if ( !is_valid() ) { return 0; } memset ( buf, 0, MAXRECV + 1 ); status = receive( buf, MAXRECV, 0 ); data = buf; return status; } int Socket::receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const { unsigned int receivedsize = 0; if ( !is_valid() ) { return 0; } while ( (receivedsize <= minpacketsize) && (receivedsize < buffersize) ) { int status = ::recv(_sd, data+receivedsize, (buffersize - receivedsize), 0 ); if ( status == SOCKET_ERROR ) { errormessage( getLastError(), "Socket::receive" ); return status; } receivedsize += status; } return receivedsize; } int Socket::recvfrom ( char* data, const int buffersize, struct sockaddr* from, socklen_t* fromlen) const { int status = ::recvfrom(_sd, data, buffersize, 0, from, fromlen); return status; } bool Socket::connect ( const std::string& host, const unsigned short port ) { close(); if ( !setHostname( host ) ) { KODI->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'; 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; int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); if (retval != 0) { errormessage(getLastError(), "Socket::connect"); return false; } if (result == NULL) { KODI->Log(LOG_ERROR, "Socket::connect %s:%u: no address info found\n", host.c_str(), port); return false; } for (address = result; address != NULL; address = address->ai_next) { // 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, static_cast(address->ai_addrlen)); if (status == SOCKET_ERROR) { close(); continue; } // We have a conection break; } freeaddrinfo(result); if (_sd == INVALID_SOCKET) { return false; } return true; } bool Socket::reconnect() { if ( is_valid() ) { return true; } return connect(_hostname, _port); } bool Socket::is_valid() const { return (_sd != INVALID_SOCKET); } #if defined(TARGET_WINDOWS) bool Socket::set_non_blocking ( const bool b ) { u_long iMode; if ( b ) iMode = 1; // enable non_blocking else iMode = 0; // disable non_blocking if (ioctlsocket(_sd, FIONBIO, &iMode) == -1) { KODI->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode); return false; } return true; } void Socket::errormessage( int errnum, const char* functionname) const { const char* errmsg = NULL; switch (errnum) { case WSANOTINITIALISED: errmsg = "A successful WSAStartup call must occur before using this function."; break; case WSAENETDOWN: errmsg = "The network subsystem or the associated service provider has failed"; break; case WSA_NOT_ENOUGH_MEMORY: errmsg = "Insufficient memory available"; break; case WSA_INVALID_PARAMETER: errmsg = "One or more parameters are invalid"; break; case WSA_OPERATION_ABORTED: errmsg = "Overlapped operation aborted"; break; case WSAEINTR: errmsg = "Interrupted function call"; break; case WSAEBADF: errmsg = "File handle is not valid"; break; case WSAEACCES: errmsg = "Permission denied"; break; case WSAEFAULT: errmsg = "Bad address"; break; case WSAEINVAL: errmsg = "Invalid argument"; break; case WSAENOTSOCK: errmsg = "Socket operation on nonsocket"; break; case WSAEDESTADDRREQ: errmsg = "Destination address required"; break; case WSAEMSGSIZE: errmsg = "Message too long"; break; case WSAEPROTOTYPE: errmsg = "Protocol wrong type for socket"; break; case WSAENOPROTOOPT: errmsg = "Bad protocol option"; break; case WSAEPFNOSUPPORT: errmsg = "Protocol family not supported"; break; case WSAEAFNOSUPPORT: errmsg = "Address family not supported by protocol family"; break; case WSAEADDRINUSE: errmsg = "Address already in use"; break; case WSAECONNRESET: errmsg = "Connection reset by peer"; break; case WSAHOST_NOT_FOUND: errmsg = "Authoritative answer host not found"; break; case WSATRY_AGAIN: errmsg = "Nonauthoritative host not found, or server failure"; break; case WSAEISCONN: errmsg = "Socket is already connected"; break; case WSAETIMEDOUT: errmsg = "Connection timed out"; break; case WSAECONNREFUSED: errmsg = "Connection refused"; break; case WSANO_DATA: errmsg = "Valid name, no data record of requested type"; break; default: errmsg = "WSA Error"; } KODI->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); } int Socket::getLastError() const { return WSAGetLastError(); } int Socket::win_usage_count = 0; //Declared static in Socket class bool Socket::osInit() { win_usage_count++; // initialize winsock: if (WSAStartup(MAKEWORD(2,2),&_wsaData) != 0) { return false; } WORD wVersionRequested = MAKEWORD(2,2); // check version if (_wsaData.wVersion != wVersionRequested) { return false; } return true; } void Socket::osCleanup() { win_usage_count--; if(win_usage_count == 0) { WSACleanup(); } } #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD bool Socket::set_non_blocking ( const bool b ) { int opts; opts = fcntl(_sd, F_GETFL); if ( opts < 0 ) { return false; } if ( b ) opts = ( opts | O_NONBLOCK ); else opts = ( opts & ~O_NONBLOCK ); if(fcntl (_sd , F_SETFL, opts) == -1) { KODI->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); return false; } return true; } void Socket::errormessage( int errnum, const char* functionname) const { const char* errmsg = NULL; switch ( errnum ) { case EAGAIN: //same as EWOULDBLOCK errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block"; break; case EBADF: errmsg = "EBADF: An invalid descriptor was specified"; break; case ECONNRESET: errmsg = "ECONNRESET: Connection reset by peer"; break; case EDESTADDRREQ: errmsg = "EDESTADDRREQ: The socket is not in connection mode and no peer address is set"; break; case EFAULT: errmsg = "EFAULT: An invalid userspace address was specified for a parameter"; break; case EINTR: errmsg = "EINTR: A signal occurred before data was transmitted"; break; case EINVAL: errmsg = "EINVAL: Invalid argument passed"; break; case ENOTSOCK: errmsg = "ENOTSOCK: The argument is not a valid socket"; break; case EMSGSIZE: errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the message to be sent made this impossible"; break; case ENOBUFS: errmsg = "ENOBUFS: The output queue for a network interface was full"; break; case ENOMEM: errmsg = "ENOMEM: No memory available"; break; case EPIPE: errmsg = "EPIPE: The local end has been shut down on a connection oriented socket"; break; case EPROTONOSUPPORT: errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported within this domain"; break; case EAFNOSUPPORT: errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family"; break; case ENFILE: errmsg = "ENFILE: Not enough kernel memory to allocate a new socket structure"; break; case EMFILE: errmsg = "EMFILE: Process file table overflow"; break; case EACCES: errmsg = "EACCES: Permission to create a socket of the specified type and/or protocol is denied"; break; case ECONNREFUSED: errmsg = "ECONNREFUSED: A remote host refused to allow the network connection (typically because it is not running the requested service)"; break; case ENOTCONN: errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not been connected"; break; //case E: // errmsg = ""; // break; default: break; } KODI->Log(LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); } int Socket::getLastError() const { return errno; } bool Socket::osInit() { // Not needed for Linux return true; } void Socket::osCleanup() { // Not needed for Linux } #endif //TARGET_WINDOWS || TARGET_LINUX || TARGET_DARWIN || TARGET_FREEBSD } //namespace MPTV pvr.mediaportal.tvserver-3.5.18-Leia/src/Socket.h000066400000000000000000000217641346756700600216660ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once //Include platform specific datatypes, header files, defines and constants: #if defined TARGET_WINDOWS #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 #ifndef NI_MAXHOST #define NI_MAXHOST 1025 #endif #ifndef socklen_t typedef int socklen_t; #endif #ifndef ipaddr_t typedef unsigned long ipaddr_t; #endif #ifndef port_t typedef unsigned short port_t; #endif #ifndef sa_family_t #define sa_family_t ADDRESS_FAMILY #endif #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD #ifdef SOCKADDR_IN #undef SOCKADDR_IN #endif #include /* for socket,connect */ #include /* for socket,connect */ #include /* for Unix socket */ #include /* for inet_pton */ #include /* for gethostbyname */ #include /* for htons */ #include /* for read, write, close */ #include #include typedef int SOCKET; typedef sockaddr SOCKADDR; typedef sockaddr_in SOCKADDR_IN; #ifndef INVALID_SOCKET #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 #include namespace MPTV //Prevent name clash with Live555 Socket { #define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused" #define MAXRECV 1500 ///< Maximum packet size enum SocketFamily { af_unspec = AF_UNSPEC, af_inet = AF_INET, af_inet6 = AF_INET6 }; enum SocketDomain { #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD pf_unix = PF_UNIX, pf_local = PF_LOCAL, #endif pf_inet = PF_INET }; enum SocketType { sock_stream = SOCK_STREAM, sock_dgram = SOCK_DGRAM }; enum SocketProtocol { tcp = IPPROTO_TCP, udp = IPPROTO_UDP }; class Socket { public: /*! * An unconnected socket may be created directly on the local * machine. The socket type (SOCK_STREAM, SOCK_DGRAM) and * protocol may also be specified. * If the socket cannot be created, an exception is thrown. * * \param family Socket family (IPv4 or IPv6) * \param domain The domain parameter specifies a communications domain within which communication will take place; * this selects the protocol family which should be used. * \param type base type and protocol family of the socket. * \param protocol specific protocol to apply. */ Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol = tcp); Socket(void); virtual ~Socket(); //Socket settings /*! * Socket setFamily * \param family Can be af_inet or af_inet6. Default: af_inet */ void setFamily(const enum SocketFamily family) { _family = family; }; /*! * Socket setDomain * \param domain Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet */ void setDomain(const enum SocketDomain domain) { _domain = domain; }; /*! * Socket setType * \param type Can be sock_stream or sock_dgram. Default: sock_stream. */ void setType(const enum SocketType type) { _type = type; }; /*! * Socket setProtocol * \param protocol Can be tcp or udp. Default: tcp. */ void setProtocol(const enum SocketProtocol protocol) { _protocol = protocol; }; /*! * Socket setPort * \param port port number for socket communication */ void setPort (const unsigned short port) { _sockaddr.sin_port = htons ( port ); }; bool setHostname ( const std::string& host ); // Server initialization /*! * Socket create * Create a new socket * \return True if succesful */ bool create(); /*! * Socket close * Close the socket * \return True if succesful */ bool close(); /*! * Socket bind */ bool bind ( const unsigned short port ); bool listen() const; bool accept ( Socket& socket ) const; // Client initialization bool connect ( const std::string& host, const unsigned short port ); bool reconnect(); // Data Transmission /*! * Socket send function * * \param data Reference to a std::string with the data to transmit * \return Number of bytes send or -1 in case of an error */ int send ( const std::string& data ); /*! * Socket send function * * \param data Pointer to a character array of size 'size' with the data to transmit * \param size Length of the data to transmit * \return Number of bytes send or -1 in case of an error */ int send ( const char* data, const unsigned int size ); /*! * Socket sendto function * * \param data Reference to a std::string with the data to transmit * \param size Length of the data to transmit * \param sendcompletebuffer If 'true': do not return until the complete buffer is transmitted * \return Number of bytes send or -1 in case of an error */ int sendto ( const char* data, unsigned int size, bool sendcompletebuffer = false); // Data Receive /*! * Socket receive function * * \param data Reference to a std::string for storage of the received data. * \param minpacketsize The minimum number of bytes that should be received before returning from this function * \return Number of bytes received or SOCKET_ERROR */ int receive ( std::string& data, unsigned int minpacketsize ) const; /*! * Socket receive function * * \param data Reference to a std::string for storage of the received data. * \return Number of bytes received or SOCKET_ERROR */ int receive ( std::string& data ) const; /*! * Socket receive function * * \param data Pointer to a character array of size buffersize. Used to store the received data. * \param buffersize Size of the 'data' buffer * \param minpacketsize Specifies the minimum number of bytes that need to be received before returning * \return Number of bytes received or SOCKET_ERROR */ int receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const; /*! * Socket recvfrom function * * \param data Pointer to a character array of size buffersize. Used to store the received data. * \param buffersize Size of the 'data' buffer * \param from Optional: pointer to a sockaddr struct that will get the address from which the data is received * \param fromlen Optional, only required if 'from' is given: length of from struct * \return Number of bytes received or SOCKET_ERROR */ int recvfrom ( char* data, const int buffersize, struct sockaddr* from = NULL, socklen_t* fromlen = NULL) const; bool set_non_blocking ( const bool ); bool ReadLine (std::string& line); bool is_valid() const; private: 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 enum SocketType _type; ///< Socket Type enum SocketDomain _domain; ///< Socket domain #ifdef TARGET_WINDOWS WSADATA _wsaData; ///< Windows Socket data static int win_usage_count; ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used #endif void errormessage( int errornum, const char* functionname = NULL) const; int getLastError(void) const; bool osInit(); void osCleanup(); }; } //namespace MPTV pvr.mediaportal.tvserver-3.5.18-Leia/src/channels.cpp000066400000000000000000000045141346756700600225560ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . * */ #include #include "channels.h" #include "utils.h" #include #include cChannel::cChannel() { uid = 0; external_id = 0; iswebstream = false; encrypted = false; visibleinguide = true; majorChannelNr = -1; minorChannelNr = -1; } cChannel::~cChannel() { } bool cChannel::Parse(const std::string& data) { std::vector fields; Tokenize(data, fields, "|"); if (fields.size() >= 4) { // Expected format: // ListTVChannels, ListRadioChannels // 0 = channel uid // 1 = channel external id/number // 2 = channel name // 3 = isencrypted ("0"/"1") // ListRadioChannels only: (TVServerKodi >= v1.1.0.100) // 4 = iswebstream // 5 = webstream url // 6 = visibleinguide (TVServerKodi >= v1.2.3.120) // 7 = ATSC major channel number (TVServerKodi >= v1.8.0.126) // 8 = ATSC minor channel number (TVServerKodi >= v1.8.0.126) uid = atoi(fields[0].c_str()); external_id = atoi(fields[1].c_str()); name = fields[2]; encrypted = (strncmp(fields[3].c_str(), "1", 1) == 0); if (fields.size() >= 6) { iswebstream = (strncmp(fields[4].c_str(), "1", 1) == 0); url = fields[5].c_str(); if (fields.size() >= 7) { visibleinguide = (strncmp(fields[6].c_str(), "1", 1) == 0); if (fields.size() >= 9) { majorChannelNr = atoi(fields[7].c_str()); minorChannelNr = atoi(fields[8].c_str()); } else { majorChannelNr = -1; minorChannelNr = -1; } } } return true; } else { return false; } } pvr.mediaportal.tvserver-3.5.18-Leia/src/channels.h000066400000000000000000000035731346756700600222270ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #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: std::string name; int uid; int external_id; bool encrypted; bool iswebstream; bool visibleinguide; std::string url; int majorChannelNr; int minorChannelNr; public: cChannel(); virtual ~cChannel(); bool Parse(const std::string& data); const char *Name(void) const { return name.c_str(); } int UID(void) const { return uid; } int ExternalID(void) const { return external_id; } bool Encrypted(void) const { return encrypted; } bool IsWebstream(void) const { return iswebstream; } bool VisibleInGuide(void) const { return visibleinguide; } const char* URL(void) const { return url.c_str(); } int MajorChannelNr(void) const { return majorChannelNr; } int MinorChannelNr(void) const { return minorChannelNr; } }; pvr.mediaportal.tvserver-3.5.18-Leia/src/client.cpp000066400000000000000000000750621346756700600222470ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . * */ #include #include "client.h" #include "xbmc_pvr_dll.h" #include "pvrclient-mediaportal.h" #include "utils.h" #include "timers.h" using namespace std; using namespace ADDON; /* User adjustable settings are saved here. * Default values are defined inside client.h * and exported to the other source files. */ std::string g_szHostname = DEFAULT_HOST; ///< The Host name or IP of the MediaPortal TV Server int g_iPort = DEFAULT_PORT; ///< The TVServerKodi listening port (default: 9596) int g_iConnectTimeout = DEFAULT_TIMEOUT; ///< The Socket connection timeout int g_iSleepOnRTSPurl = DEFAULT_SLEEP_RTSP_URL; ///< An optional delay between tuning a channel and opening the corresponding RTSP stream in Kodi (default: 0) bool g_bOnlyFTA = DEFAULT_FTA_ONLY; ///< Send only Free-To-Air Channels inside Channel list to Kodi bool g_bRadioEnabled = DEFAULT_RADIO; ///< Send also Radio channels list to Kodi bool g_bHandleMessages = DEFAULT_HANDLE_MSG; ///< Send VDR's OSD status messages to Kodi OSD bool g_bResolveRTSPHostname = DEFAULT_RESOLVE_RTSP_HOSTNAME; ///< Resolve the server hostname in the rtsp URLs to an IP at the TV Server side (default: false) bool g_bReadGenre = DEFAULT_READ_GENRE; ///< Read the genre strings from MediaPortal and translate them into Kodi DVB genre id's (only English) bool g_bEnableOldSeriesDlg = false; ///< Show the old pre-Jarvis series recording dialog std::string g_szTVGroup = DEFAULT_TVGROUP; ///< Import only TV channels from this TV Server TV group std::string g_szRadioGroup = DEFAULT_RADIOGROUP; ///< Import only radio channels from this TV Server radio group std::string g_szSMBusername = DEFAULT_SMBUSERNAME; ///< Windows user account used to access share std::string g_szSMBpassword = DEFAULT_SMBPASSWORD; ///< Windows user password used to access share ///< Leave empty to use current user when running on Windows eStreamingMethod g_eStreamingMethod = TSReader; TvDatabase::KeepMethodType g_KeepMethodType = TvDatabase::Always; int g_DefaultRecordingLifeTime = 100; ///< The default days which are configured in kodi bool g_bFastChannelSwitch = true; ///< Don't stop an existing timeshift on a channel switch bool g_bUseRTSP = false; ///< Use RTSP streaming when using the tsreader /* Client member variables */ ADDON_STATUS m_curStatus = ADDON_STATUS_UNKNOWN; cPVRClientMediaPortal *g_client = NULL; std::string g_szUserPath = ""; std::string g_szClientPath = ""; CHelper_libXBMC_addon *KODI = NULL; CHelper_libXBMC_pvr *PVR = NULL; CHelper_libKODI_guilib *GUI = NULL; extern "C" { void ADDON_ReadSettings(void); /*********************************************************** * Standard AddOn related public library functions ***********************************************************/ //-- Create ------------------------------------------------------------------- // Called after loading of the dll, all steps to become Client functional // must be performed here. //----------------------------------------------------------------------------- ADDON_STATUS ADDON_Create(void* hdl, void* props) { if (!hdl || !props) { m_curStatus = ADDON_STATUS_UNKNOWN; return m_curStatus; } PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props; KODI = new CHelper_libXBMC_addon; if (!KODI->RegisterMe(hdl)) { SAFE_DELETE(KODI); m_curStatus = ADDON_STATUS_PERMANENT_FAILURE; return m_curStatus; } PVR = new CHelper_libXBMC_pvr; if (!PVR->RegisterMe(hdl)) { SAFE_DELETE(PVR); SAFE_DELETE(KODI); m_curStatus = ADDON_STATUS_PERMANENT_FAILURE; return m_curStatus; } GUI = new CHelper_libKODI_guilib; if (!GUI->RegisterMe(hdl)) { SAFE_DELETE(GUI); SAFE_DELETE(PVR); SAFE_DELETE(KODI); m_curStatus = ADDON_STATUS_PERMANENT_FAILURE; return m_curStatus; } KODI->Log(LOG_INFO, "Creating MediaPortal PVR-Client"); m_curStatus = ADDON_STATUS_UNKNOWN; g_szUserPath = pvrprops->strUserPath; g_szClientPath = pvrprops->strClientPath; ADDON_ReadSettings(); /* Create connection to MediaPortal Kodi TV client */ g_client = new cPVRClientMediaPortal(); m_curStatus = g_client->TryConnect(); if (m_curStatus == ADDON_STATUS_PERMANENT_FAILURE) { SAFE_DELETE(g_client); SAFE_DELETE(GUI); SAFE_DELETE(PVR); SAFE_DELETE(KODI); } 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; } //-- Destroy ------------------------------------------------------------------ // Used during destruction of the client, all steps to do clean and safe Create // again must be done. //----------------------------------------------------------------------------- void ADDON_Destroy() { SAFE_DELETE(g_client); SAFE_DELETE(GUI); SAFE_DELETE(PVR); SAFE_DELETE(KODI); m_curStatus = ADDON_STATUS_UNKNOWN; } //-- GetStatus ---------------------------------------------------------------- // Report the current Add-On Status to Kodi //----------------------------------------------------------------------------- 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; return m_curStatus; } void ADDON_ReadSettings(void) { /* Read setting "host" from settings.xml */ char buffer[1024]; if (!KODI) return; /* Connection settings */ /***********************/ if (KODI->GetSetting("host", &buffer)) { g_szHostname = buffer; uri::decode(g_szHostname); } else { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '127.0.0.1' as default"); g_szHostname = DEFAULT_HOST; } /* Read setting "port" from settings.xml */ if (!KODI->GetSetting("port", &g_iPort)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'port' setting, falling back to '9596' as default"); g_iPort = DEFAULT_PORT; } /* Read setting "timeout" from settings.xml */ if (!KODI->GetSetting("timeout", &g_iConnectTimeout)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'timeout' setting, falling back to %i seconds as default", DEFAULT_TIMEOUT); g_iConnectTimeout = DEFAULT_TIMEOUT; } /* MediaPortal settings */ /***********************/ /* Read setting "ftaonly" from settings.xml */ if (!KODI->GetSetting("ftaonly", &g_bOnlyFTA)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'ftaonly' setting, falling back to 'false' as default"); g_bOnlyFTA = DEFAULT_FTA_ONLY; } /* Read setting "useradio" from settings.xml */ if (!KODI->GetSetting("useradio", &g_bRadioEnabled)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'useradio' setting, falling back to 'true' as default"); g_bRadioEnabled = DEFAULT_RADIO; } if (!KODI->GetSetting("tvgroup", &buffer)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'tvgroup' setting, falling back to '' as default"); } else { g_szTVGroup = buffer; StringUtils::Replace(g_szTVGroup,";","|"); } if (!KODI->GetSetting("radiogroup", &buffer)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'radiogroup' setting, falling back to '' as default"); } else { g_szRadioGroup = buffer; StringUtils::Replace(g_szRadioGroup,";","|"); } if (!KODI->GetSetting("streamingmethod", &g_eStreamingMethod)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'streamingmethod' setting, falling back to 'tsreader' as default"); g_eStreamingMethod = TSReader; } /* Read setting "resolvertsphostname" from settings.xml */ if (!KODI->GetSetting("resolvertsphostname", &g_bResolveRTSPHostname)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'resolvertsphostname' setting, falling back to 'true' as default"); g_bResolveRTSPHostname = DEFAULT_RESOLVE_RTSP_HOSTNAME; } /* Read setting "readgenre" from settings.xml */ if (!KODI->GetSetting("readgenre", &g_bReadGenre)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'readgenre' setting, falling back to 'true' as default"); g_bReadGenre = DEFAULT_READ_GENRE; } /* Read setting "readgenre" from settings.xml */ if (!KODI->GetSetting("enableoldseriesdlg", &g_bEnableOldSeriesDlg)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'enableoldseriesdlg' setting, falling back to 'false' as default"); g_bEnableOldSeriesDlg = false; } /* Read setting "keepmethodtype" from settings.xml */ if (!KODI->GetSetting("keepmethodtype", &g_KeepMethodType)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'keepmethodtype' setting, falling back to 'Always' as default"); g_KeepMethodType = TvDatabase::Always; } /* Read setting "defaultrecordinglifetime" from settings.xml */ if (!KODI->GetSetting("defaultrecordinglifetime", &g_DefaultRecordingLifeTime)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'defaultrecordinglifetime' setting, falling back to '100' as default"); g_DefaultRecordingLifeTime = 100; } /* Read setting "sleeponrtspurl" from settings.xml */ if (!KODI->GetSetting("sleeponrtspurl", &g_iSleepOnRTSPurl)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'sleeponrtspurl' setting, falling back to %i seconds as default", DEFAULT_SLEEP_RTSP_URL); g_iSleepOnRTSPurl = DEFAULT_SLEEP_RTSP_URL; } /* TSReader settings */ /*********************/ /* Read setting "fastchannelswitch" from settings.xml */ if (!KODI->GetSetting("fastchannelswitch", &g_bFastChannelSwitch)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'fastchannelswitch' setting, falling back to 'false' as default"); g_bFastChannelSwitch = false; } /* read setting "user" from settings.xml */ if (!KODI->GetSetting("smbusername", &buffer)) { KODI->Log(LOG_ERROR, "Couldn't get 'smbusername' setting, falling back to '%s' as default", DEFAULT_SMBUSERNAME); g_szSMBusername = DEFAULT_SMBUSERNAME; } else g_szSMBusername = buffer; /* read setting "pass" from settings.xml */ if (!KODI->GetSetting("smbpassword", &buffer)) { KODI->Log(LOG_ERROR, "Couldn't get 'smbpassword' setting, falling back to '%s' as default", DEFAULT_SMBPASSWORD); g_szSMBpassword = DEFAULT_SMBPASSWORD; } else g_szSMBpassword = buffer; /* Read setting "usertsp" from settings.xml */ if (!KODI->GetSetting("usertsp", &g_bUseRTSP)) { /* If setting is unknown fallback to defaults */ KODI->Log(LOG_ERROR, "Couldn't get 'usertsp' setting, falling back to 'false' as default"); g_bUseRTSP = false; } /* Log the current settings for debugging purposes */ KODI->Log(LOG_DEBUG, "settings: streamingmethod: %s, usertsp=%i", (( g_eStreamingMethod == TSReader) ? "TSReader" : "ffmpeg"), (int) g_bUseRTSP); KODI->Log(LOG_DEBUG, "settings: host='%s', port=%i, timeout=%i", g_szHostname.c_str(), g_iPort, g_iConnectTimeout); KODI->Log(LOG_DEBUG, "settings: ftaonly=%i, useradio=%i, tvgroup='%s', radiogroup='%s'", (int) g_bOnlyFTA, (int) g_bRadioEnabled, g_szTVGroup.c_str(), g_szRadioGroup.c_str()); KODI->Log(LOG_DEBUG, "settings: readgenre=%i, enableoldseriesdlg=%i, sleeponrtspurl=%i", (int)g_bReadGenre, (int)g_bEnableOldSeriesDlg, g_iSleepOnRTSPurl); KODI->Log(LOG_DEBUG, "settings: resolvertsphostname=%i", (int) g_bResolveRTSPHostname); KODI->Log(LOG_DEBUG, "settings: fastchannelswitch=%i", (int) g_bFastChannelSwitch); KODI->Log(LOG_DEBUG, "settings: smb user='%s', pass=%s", g_szSMBusername.c_str(), (g_szSMBpassword.length() > 0 ? "" : "")); KODI->Log(LOG_DEBUG, "settings: keepmethodtype=%i, defaultrecordinglifetime=%i", (int)g_KeepMethodType, (int)g_DefaultRecordingLifeTime); } //-- SetSetting --------------------------------------------------------------- // Called everytime a setting is changed by the user and to inform AddOn about // new setting and to do required stuff to apply it. //----------------------------------------------------------------------------- ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue) { string str = settingName; // SetSetting can occur when the addon is enabled, but TV support still // disabled. In that case the addon is not loaded, so we should not try // to change its settings. if (!KODI) return ADDON_STATUS_OK; if (str == "host") { string tmp_sHostname; KODI->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue); tmp_sHostname = g_szHostname; g_szHostname = (const char*) settingValue; if (tmp_sHostname != g_szHostname) return ADDON_STATUS_NEED_RESTART; } else if (str == "port") { KODI->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iPort, *(int*) settingValue); if (g_iPort != *(int*) settingValue) { g_iPort = *(int*) settingValue; return ADDON_STATUS_NEED_RESTART; } } else if (str == "ftaonly") { KODI->Log(LOG_INFO, "Changed setting 'ftaonly' from %u to %u", g_bOnlyFTA, *(bool*) settingValue); g_bOnlyFTA = *(bool*) settingValue; } else if (str == "useradio") { KODI->Log(LOG_INFO, "Changed setting 'useradio' from %u to %u", g_bRadioEnabled, *(bool*) settingValue); g_bRadioEnabled = *(bool*) settingValue; } else if (str == "timeout") { KODI->Log(LOG_INFO, "Changed setting 'timeout' from %u to %u", g_iConnectTimeout, *(int*) settingValue); g_iConnectTimeout = *(int*) settingValue; } else if (str == "tvgroup") { KODI->Log(LOG_INFO, "Changed setting 'tvgroup' from '%s' to '%s'", g_szTVGroup.c_str(), (const char*) settingValue); g_szTVGroup = (const char*) settingValue; } else if (str == "radiogroup") { KODI->Log(LOG_INFO, "Changed setting 'radiogroup' from '%s' to '%s'", g_szRadioGroup.c_str(), (const char*) settingValue); g_szRadioGroup = (const char*) settingValue; } else if (str == "resolvertsphostname") { KODI->Log(LOG_INFO, "Changed setting 'resolvertsphostname' from %u to %u", g_bResolveRTSPHostname, *(bool*) settingValue); g_bResolveRTSPHostname = *(bool*) settingValue; } else if (str == "readgenre") { KODI->Log(LOG_INFO, "Changed setting 'readgenre' from %u to %u", g_bReadGenre, *(bool*) settingValue); g_bReadGenre = *(bool*) settingValue; } else if (str == "enableoldseriesdlg") { KODI->Log(LOG_INFO, "Changed setting 'enableoldseriesdlg' from %u to %u", g_bEnableOldSeriesDlg, *(bool*)settingValue); g_bEnableOldSeriesDlg = *(bool*)settingValue; } else if (str == "keepmethodtype") { if (g_KeepMethodType != *(TvDatabase::KeepMethodType*)settingValue) { KODI->Log(LOG_INFO, "Changed setting 'keepmethodtype' from %u to %u", g_KeepMethodType, *(int*)settingValue); g_KeepMethodType = *(TvDatabase::KeepMethodType*)settingValue; } } else if (str == "defaultrecordinglifetime") { if (g_DefaultRecordingLifeTime != *(int*)settingValue) { KODI->Log(LOG_INFO, "Changed setting 'defaultrecordinglifetime' from %u to %u", g_DefaultRecordingLifeTime, *(int*)settingValue); g_DefaultRecordingLifeTime = *(int*)settingValue; } } else if (str == "sleeponrtspurl") { KODI->Log(LOG_INFO, "Changed setting 'sleeponrtspurl' from %u to %u", g_iSleepOnRTSPurl, *(int*) settingValue); g_iSleepOnRTSPurl = *(int*) settingValue; } else if (str == "smbusername") { KODI->Log(LOG_INFO, "Changed setting 'smbusername' from '%s' to '%s'", g_szSMBusername.c_str(), (const char*) settingValue); g_szSMBusername = (const char*) settingValue; } else if (str == "smbpassword") { KODI->Log(LOG_INFO, "Changed setting 'smbpassword' from '%s' to '%s'", g_szSMBpassword.c_str(), (const char*) settingValue); g_szSMBpassword = (const char*) settingValue; } else if (str == "fastchannelswitch") { KODI->Log(LOG_INFO, "Changed setting 'fastchannelswitch' from %u to %u", g_bFastChannelSwitch, *(bool*) settingValue); g_bFastChannelSwitch = *(bool*) settingValue; } else if (str == "streamingmethod") { if (g_eStreamingMethod != *(eStreamingMethod*) settingValue) { KODI->Log(LOG_INFO, "Changed setting 'streamingmethod' from %u to %u", g_eStreamingMethod, *(int*) settingValue); g_eStreamingMethod = *(eStreamingMethod*) settingValue; /* Switching between ffmpeg and tsreader mode requires a restart due to different channel streams */ return ADDON_STATUS_NEED_RESTART; } } else if (str == "usertsp") { KODI->Log(LOG_INFO, "Changed setting 'usertsp' from %u to %u", g_bUseRTSP, *(bool*) settingValue); g_bUseRTSP = *(bool*) settingValue; } return ADDON_STATUS_OK; } /*********************************************************** * PVR Client AddOn specific public library functions ***********************************************************/ void OnSystemSleep() { } void OnSystemWake() { } void OnPowerSavingActivated() { } void OnPowerSavingDeactivated() { } //-- GetAddonCapabilities ----------------------------------------------------- // Tell Kodi our requirements //----------------------------------------------------------------------------- PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities) { KODI->Log(LOG_DEBUG, "->GetAddonCapabilities()"); memset(pCapabilities, 0, sizeof(PVR_ADDON_CAPABILITIES)); pCapabilities->bSupportsEPG = true; pCapabilities->bSupportsEPGEdl = false; pCapabilities->bSupportsTV = true; pCapabilities->bSupportsRadio = g_bRadioEnabled; pCapabilities->bSupportsRecordings = true; pCapabilities->bSupportsRecordingsUndelete = false; pCapabilities->bSupportsTimers = true; pCapabilities->bSupportsChannelGroups = true; pCapabilities->bSupportsChannelScan = false; pCapabilities->bSupportsChannelSettings = false; pCapabilities->bHandlesInputStream = true; pCapabilities->bHandlesDemuxing = false; pCapabilities->bSupportsRecordingPlayCount = (g_iTVServerKodiBuild < 117) ? false : true; pCapabilities->bSupportsLastPlayedPosition = (g_iTVServerKodiBuild < 121) ? false : true; pCapabilities->bSupportsRecordingEdl = false; pCapabilities->bSupportsRecordingsRename = true; pCapabilities->bSupportsRecordingsLifetimeChange = false; pCapabilities->bSupportsDescrambleInfo = false; pCapabilities->bSupportsAsyncEPGTransfer = false; pCapabilities->iRecordingsLifetimesSize = 0; return PVR_ERROR_NO_ERROR; } PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* UNUSED(pProperties)) { return PVR_ERROR_NOT_IMPLEMENTED; } //-- GetBackendName ----------------------------------------------------------- // Return the Name of the Backend //----------------------------------------------------------------------------- const char * GetBackendName(void) { if (g_client) return g_client->GetBackendName(); else return ""; } //-- GetBackendVersion -------------------------------------------------------- // Return the Version of the Backend as String //----------------------------------------------------------------------------- const char * GetBackendVersion(void) { if (g_client) return g_client->GetBackendVersion(); else return ""; } //-- GetConnectionString ------------------------------------------------------ // Return a String with connection info, if available //----------------------------------------------------------------------------- const char * GetConnectionString(void) { if (g_client) return g_client->GetConnectionString(); else return "addon error!"; } //-- GetBackendHostname ------------------------------------------------------- // Return a String with the backend host name //----------------------------------------------------------------------------- const char * GetBackendHostname(void) { return g_szHostname.c_str(); } //-- GetDriveSpace ------------------------------------------------------------ // Return the Total and Free Drive space on the PVR Backend //----------------------------------------------------------------------------- PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetDriveSpace(iTotal, iUsed); } PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetBackendTime(localTime, gmtOffset); } PVR_ERROR OpenDialogChannelScan() { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR CallMenuHook(const PVR_MENUHOOK& UNUSED(menuhook), const PVR_MENUHOOK_DATA& UNUSED(item)) { return PVR_ERROR_NOT_IMPLEMENTED; } /*******************************************/ /** PVR EPG Functions **/ PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetEpg(handle, channel, iStart, iEnd); } /*******************************************/ /** PVR Channel Functions **/ int GetChannelsAmount() { if (!g_client) return 0; else return g_client->GetNumChannels(); } PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetChannels(handle, bRadio); } PVR_ERROR DeleteChannel(const PVR_CHANNEL& UNUSED(channel)) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR RenameChannel(const PVR_CHANNEL& UNUSED(channel)) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR OpenDialogChannelSettings(const PVR_CHANNEL& UNUSED(channelinfo)) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL& UNUSED(channelinfo)) { return PVR_ERROR_NOT_IMPLEMENTED; } /*******************************************/ /** PVR Channel group Functions **/ int GetChannelGroupsAmount(void) { if (!g_client) return 0; else return g_client->GetChannelGroupsAmount(); } PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetChannelGroups(handle, bRadio); } PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetChannelGroupMembers(handle, group); } /*******************************************/ /** PVR Recording Functions **/ int GetRecordingsAmount(bool deleted) { if (!g_client) return 0; else return g_client->GetNumRecordings(); } PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetRecordings(handle); } PVR_ERROR DeleteRecording(const PVR_RECORDING &recording) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->DeleteRecording(recording); } PVR_ERROR RenameRecording(const PVR_RECORDING &recording) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->RenameRecording(recording); } PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING &recording, int count) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->SetRecordingPlayCount(recording, count); } PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->SetRecordingLastPlayedPosition(recording, lastplayedposition); } int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetRecordingLastPlayedPosition(recording); } /*******************************************/ /** PVR Timer Functions **/ PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int *size) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetTimerTypes(types, size); } int GetTimersAmount(void) { if (!g_client) return 0; else return g_client->GetNumTimers(); } PVR_ERROR GetTimers(ADDON_HANDLE handle) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->GetTimers(handle); } PVR_ERROR AddTimer(const PVR_TIMER &timer) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->AddTimer(timer); } PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->DeleteTimer(timer, bForceDelete); } PVR_ERROR UpdateTimer(const PVR_TIMER &timer) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->UpdateTimer(timer); } /*******************************************/ /** PVR Live Stream Functions **/ bool OpenLiveStream(const PVR_CHANNEL &channelinfo) { if (!g_client) return false; else return g_client->OpenLiveStream(channelinfo); } void CloseLiveStream() { if (g_client) g_client->CloseLiveStream(); } int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) { if (!g_client) return 0; else return g_client->ReadLiveStream(pBuffer, iBufferSize); } long long SeekLiveStream(long long iPosition, int iWhence) { if (!g_client) return -1; else return g_client->SeekLiveStream(iPosition, iWhence); } long long LengthLiveStream(void) { if (!g_client) return -1; else return g_client->LengthLiveStream(); } bool IsRealTimeStream(void) { if (g_client == NULL) return false; return g_client->IsRealTimeStream(); } PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus) { if (!g_client) return PVR_ERROR_SERVER_ERROR; else return g_client->SignalStatus(signalStatus); } /*******************************************/ /** PVR Recording Stream Functions **/ bool OpenRecordedStream(const PVR_RECORDING &recording) { if (!g_client) return false; else return g_client->OpenRecordedStream(recording); } void CloseRecordedStream(void) { if (g_client) g_client->CloseRecordedStream(); } int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize) { if (!g_client) return 0; else return g_client->ReadRecordedStream(pBuffer, iBufferSize); } long long SeekRecordedStream(long long iPosition, int iWhence) { if (!g_client) return -1; else return g_client->SeekRecordedStream(iPosition, iWhence); } long long LengthRecordedStream(void) { if (!g_client) return -1; else return g_client->LengthRecordedStream(); } bool CanPauseStream(void) { if (g_client) return g_client->CanPauseAndSeek(); return false; } void PauseStream(bool bPaused) { if (g_client) g_client->PauseStream(bPaused); } bool CanSeekStream(void) { if (g_client) return g_client->CanPauseAndSeek(); return false; } PVR_ERROR GetChannelStreamProperties(const PVR_CHANNEL* channel, PVR_NAMED_VALUE* properties, unsigned int* iPropertiesCount) { if ((!channel) || (!properties) || (!iPropertiesCount) || (!g_client)) { return PVR_ERROR_FAILED; } return g_client->GetChannelStreamProperties(channel, properties, iPropertiesCount); } PVR_ERROR GetRecordingStreamProperties(const PVR_RECORDING* recording, PVR_NAMED_VALUE* properties, unsigned int* iPropertiesCount) { if ((!recording) || (!properties) || (!iPropertiesCount) || (!g_client)) { return PVR_ERROR_FAILED; } return g_client->GetRecordingStreamProperties(recording, properties, iPropertiesCount); } PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES* stream_times) { if ((!stream_times) || (!g_client)) { return PVR_ERROR_INVALID_PARAMETERS; } return g_client->GetStreamTimes(stream_times); } PVR_ERROR GetStreamReadChunkSize(int* chunksize) { if ((!chunksize) || (!g_client)) { return PVR_ERROR_INVALID_PARAMETERS; } return g_client->GetStreamReadChunkSize(chunksize); } /** UNUSED API FUNCTIONS */ DemuxPacket* DemuxRead(void) { return NULL; } void DemuxAbort(void) {} void DemuxReset(void) {} void DemuxFlush(void) {} PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY[], int*) { return PVR_ERROR_NOT_IMPLEMENTED; }; bool SeekTime(double,bool,double*) { return false; } void SetSpeed(int) {}; bool IsTimeshifting(void) { return false; } PVR_ERROR UndeleteRecording(const PVR_RECORDING& UNUSED(recording)) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR DeleteAllRecordingsFromTrash() { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetDescrambleInfo(PVR_DESCRAMBLE_INFO*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR SetRecordingLifetime(const PVR_RECORDING*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR IsEPGTagRecordable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR IsEPGTagPlayable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetEPGTagStreamProperties(const EPG_TAG*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetEPGTagEdl(const EPG_TAG* epgTag, PVR_EDL_ENTRY edl[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } } //end extern "C" pvr.mediaportal.tvserver-3.5.18-Leia/src/client.h000066400000000000000000000062061346756700600217060ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #ifndef CLIENT_H #define CLIENT_H #include "libXBMC_addon.h" #include "libXBMC_pvr.h" #include "libKODI_guilib.h" #include "timers.h" enum eStreamingMethod { TSReader = 0, ffmpeg = 1 }; #define DEFAULT_HOST "127.0.0.1" #define DEFAULT_PORT 9596 #define DEFAULT_FTA_ONLY false #define DEFAULT_RADIO true #define DEFAULT_TIMEOUT 10 #define DEFAULT_HANDLE_MSG false #define DEFAULT_RESOLVE_RTSP_HOSTNAME false #define DEFAULT_READ_GENRE false #define DEFAULT_SLEEP_RTSP_URL 0 #define DEFAULT_USE_REC_DIR false #define DEFAULT_TVGROUP "" #define DEFAULT_RADIOGROUP "" #define DEFAULT_DIRECT_TS_FR false #define DEFAULT_SMBUSERNAME "Guest" #define DEFAULT_SMBPASSWORD "" extern std::string g_szUserPath; ///< The Path to the user directory inside user profile extern std::string g_szClientPath; ///< The Path where this driver is located /* Client Settings */ extern std::string g_szHostname; extern int g_iPort; extern int g_iConnectTimeout; extern int g_iSleepOnRTSPurl; extern bool g_bOnlyFTA; extern bool g_bRadioEnabled; extern bool g_bHandleMessages; extern bool g_bResolveRTSPHostname; extern bool g_bReadGenre; extern bool g_bEnableOldSeriesDlg; extern bool g_bFastChannelSwitch; extern bool g_bUseRTSP; ///< Use RTSP streaming when using the tsreader extern std::string g_szTVGroup; extern std::string g_szRadioGroup; extern std::string g_szSMBusername; extern std::string g_szSMBpassword; extern eStreamingMethod g_eStreamingMethod; extern TvDatabase::KeepMethodType g_KeepMethodType; extern int g_DefaultRecordingLifeTime; extern ADDON::CHelper_libXBMC_addon *KODI; extern CHelper_libXBMC_pvr *PVR; extern CHelper_libKODI_guilib *GUI; extern int g_iTVServerKodiBuild; /*! * @brief PVR macros for string exchange */ #define PVR_STRCPY(dest, source) do { strncpy(dest, source, sizeof(dest)-1); dest[sizeof(dest)-1] = '\0'; } while(0) #define PVR_STRCLR(dest) memset(dest, 0, sizeof(dest)) #endif /* CLIENT_H */ pvr.mediaportal.tvserver-3.5.18-Leia/src/epg.cpp000066400000000000000000000104201346756700600215270ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . * */ #include #include #include using namespace std; #include "epg.h" #include "utils.h" #include "client.h" #include "DateTime.h" using namespace ADDON; cEpg::cEpg() { m_genretable = NULL; Reset(); } cEpg::~cEpg() { } void cEpg::Reset() { m_genre.clear(); m_title.clear(); m_description.clear(); m_episodePart.clear(); m_episodeName.clear(); m_uid = PVR_TIMER_NO_EPG_UID; // 0 is defined as invalid EPG id according to the PVR_TIMER definitions m_originalAirDate = 0; m_duration = 0; m_genre_type = 0; m_genre_subtype = 0; m_seriesNumber = 0; m_episodeNumber = 0; m_starRating = 0; m_parentalRating = 0; } bool cEpg::ParseLine(string& data) { try { vector epgfields; Tokenize(data, epgfields, "|"); if( epgfields.size() >= 5 ) { //KODI->Log(LOG_DEBUG, "%s: %s", epgfields[0].c_str(), epgfields[2].c_str()); // field 0 = start date + time // field 1 = end date + time // field 2 = title // field 3 = description // field 4 = genre string // field 5 = idProgram (int) // field 6 = idChannel (int) // field 7 = seriesNum (string) // field 8 = episodeNumber (string) // field 9 = episodeName (string) // field 10 = episodePart (string) // field 11 = originalAirDate (date + time) // field 12 = classification (string) // field 13 = starRating (int) // field 14 = parentalRating (int) if( m_startTime.SetFromDateTime(epgfields[0]) == false ) { KODI->Log(LOG_ERROR, "cEpg::ParseLine: Unable to convert start time '%s' into date+time", epgfields[0].c_str()); return false; } if( m_endTime.SetFromDateTime(epgfields[1]) == false ) { KODI->Log(LOG_ERROR, "cEpg::ParseLine: Unable to convert end time '%s' into date+time", epgfields[1].c_str()); return false; } m_duration = m_endTime - m_startTime; m_title = epgfields[2]; m_description = epgfields[3]; m_genre = epgfields[4]; if (m_genretable) m_genretable->GenreToTypes(m_genre, m_genre_type, m_genre_subtype); if( epgfields.size() >= 15 ) { // Since TVServerKodi v1.x.x.104 m_uid = (unsigned int) cKodiEpgIndexOffset + atol(epgfields[5].c_str()); m_seriesNumber = atoi(epgfields[7].c_str()); m_episodeNumber = atoi(epgfields[8].c_str()); m_episodeName = epgfields[9]; m_episodePart = epgfields[10]; m_starRating = atoi(epgfields[13].c_str()); m_parentalRating = atoi(epgfields[14].c_str()); //originalAirDate if( m_originalAirDate.SetFromDateTime(epgfields[11]) == false ) { KODI->Log(LOG_ERROR, "cEpg::ParseLine: Unable to convert original air date '%s' into date+time", epgfields[11].c_str()); return false; } } return true; } } catch(std::exception &e) { KODI->Log(LOG_ERROR, "Exception '%s' during parse EPG data string.", e.what()); } return false; } void cEpg::SetGenreTable(CGenreTable* genretable) { m_genretable = genretable; } time_t cEpg::StartTime(void) const { time_t retval = m_startTime.GetAsTime(); return retval; } time_t cEpg::EndTime(void) const { time_t retval = m_endTime.GetAsTime(); return retval; } time_t cEpg::OriginalAirDate(void) const { time_t retval = m_endTime.GetAsTime(); return retval; } const char *cEpg::PlotOutline(void) const { if (m_episodeName.empty()) return m_title.c_str(); else return m_episodeName.c_str(); } pvr.mediaportal.tvserver-3.5.18-Leia/src/epg.h000066400000000000000000000052351346756700600212040ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #ifndef __EPG_H #define __EPG_H #include #include #include "libXBMC_addon.h" #include "libXBMC_pvr.h" #include "GenreTable.h" #include "DateTime.h" const int cKodiEpgIndexOffset = (PVR_TIMER_NO_EPG_UID + 1); ///> Offset used to map the MediaPortal schedule id's to the iClientIndex values class cEpg { private: unsigned int m_uid; std::string m_title; std::string m_description; MPTV::CDateTime m_startTime; MPTV::CDateTime m_endTime; MPTV::CDateTime m_originalAirDate; int m_duration; std::string m_genre; int m_genre_type; int m_genre_subtype; int m_episodeNumber; std::string m_episodePart; std::string m_episodeName; int m_seriesNumber; int m_starRating; int m_parentalRating; CGenreTable* m_genretable; public: cEpg(); virtual ~cEpg(); void Reset(); bool ParseLine(std::string& data); unsigned int UniqueId(void) const { return m_uid; } time_t StartTime(void) const; time_t EndTime(void) const; time_t Duration(void) const { return m_duration; } time_t OriginalAirDate(void) const; const char *Title(void) const { return m_title.c_str(); } const char *PlotOutline(void) const; const char *Description(void) const { return m_description.c_str(); } const char *Genre(void) const { return m_genre.c_str(); } int GenreType(void) const { return m_genre_type; } int GenreSubType(void) const { return m_genre_subtype; } int SeriesNumber(void) const { return m_seriesNumber; }; int EpisodeNumber(void) const { return m_episodeNumber; }; const char* EpisodeName(void) const { return m_episodeName.c_str(); }; const char* EpisodePart(void) const { return m_episodePart.c_str(); }; int StarRating(void) const { return m_starRating; }; int ParentalRating(void) const { return m_parentalRating; }; void SetGenreTable(CGenreTable* genremap); }; #endif //__EPG_H pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/000077500000000000000000000000001346756700600210215ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/000077500000000000000000000000001346756700600222175ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/000077500000000000000000000000001346756700600264525ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/BasicHashTable.cpp000066400000000000000000000167601346756700600317650ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler.cpp000066400000000000000000000141471346756700600326700ustar00rootroot00000000000000/********** 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; } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/BasicTaskScheduler0.cpp000066400000000000000000000120111346756700600327340ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/BasicUsageEnvironment.cpp000066400000000000000000000045751346756700600334240ustar00rootroot00000000000000/********** 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; } BasicUsageEnvironment0.cpp000066400000000000000000000050761346756700600334220ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/********** 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); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/COPYING000066400000000000000000000000121346756700600274760ustar00rootroot00000000000000../COPYINGpvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/DelayQueue.cpp000066400000000000000000000144251346756700600312270ustar00rootroot00000000000000/********** 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); pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/include/000077500000000000000000000000001346756700600300755ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/include/BasicHashTable.hh000066400000000000000000000066231346756700600332220ustar00rootroot00000000000000/********** 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 BasicUsageEnvironment.hh000066400000000000000000000045401346756700600345750ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/include/********** 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 BasicUsageEnvironment0.hh000066400000000000000000000060661346756700600346620ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/include/********** 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 BasicUsageEnvironment_version.hh000066400000000000000000000005421346756700600363400ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/include// 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/include/DelayQueue.hh000066400000000000000000000110601346756700600324570ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/BasicUsageEnvironment/include/HandlerSet.hh000066400000000000000000000041121346756700600324450ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/000077500000000000000000000000001346756700600255105ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/COPYING000066400000000000000000000000121346756700600265340ustar00rootroot00000000000000../COPYINGpvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/HashTable.cpp000066400000000000000000000026231346756700600300520ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/UsageEnvironment.cpp000066400000000000000000000037671346756700600315220ustar00rootroot00000000000000/********** 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(); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/include/000077500000000000000000000000001346756700600271335ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/include/Boolean.hh000066400000000000000000000023631346756700600310370ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/include/HashTable.hh000066400000000000000000000047371346756700600313220ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/include/UsageEnvironment.hh000066400000000000000000000117761346756700600327610ustar00rootroot00000000000000/********** 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 UsageEnvironment_version.hh000066400000000000000000000005111346756700600344300ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/include// 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/include/strDup.hh000066400000000000000000000026371346756700600307450ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/UsageEnvironment/strDup.cpp000066400000000000000000000027511346756700600275020ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/000077500000000000000000000000001346756700600242335ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/COPYING000066400000000000000000000000121346756700600252570ustar00rootroot00000000000000../COPYINGpvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/GroupEId.cpp000066400000000000000000000055711346756700600264250ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/Groupsock.cpp000066400000000000000000000453651346756700600267300ustar00rootroot00000000000000/********** 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(); }; pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/GroupsockHelper.cpp000066400000000000000000000564351346756700600300700ustar00rootroot00000000000000/********** 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 = false; 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) { keepTrying = true; 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; imr.imr_multiaddr.s_addr = groupAddress; imr.imr_sourceaddr.s_addr = sourceFilterAddr; imr.imr_interface.s_addr = ReceivingInterfaceAddr; 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; imr.imr_multiaddr.s_addr = groupAddress; imr.imr_sourceaddr.s_addr = sourceFilterAddr; imr.imr_interface.s_addr = ReceivingInterfaceAddr; 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/IOHandlers.cpp000066400000000000000000000037341346756700600267360ustar00rootroot00000000000000/********** 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(); } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/NetAddress.cpp000066400000000000000000000212201346756700600267700ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/NetInterface.cpp000066400000000000000000000110571346756700600273120ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/000077500000000000000000000000001346756700600256565ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/GroupEId.hh000066400000000000000000000054601346756700600276620ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/Groupsock.hh000066400000000000000000000150601346756700600301550ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/GroupsockHelper.hh000066400000000000000000000113641346756700600313200ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/IOHandlers.hh000066400000000000000000000021421346756700600301660ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/NetAddress.hh000066400000000000000000000110131346756700600302270ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/NetCommon.h000066400000000000000000000065131346756700600277330ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/NetInterface.hh000066400000000000000000000074041346756700600305530ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/TunnelEncaps.hh000066400000000000000000000065771346756700600306150ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/include/groupsock_version.hh000066400000000000000000000004461346756700600317640ustar00rootroot00000000000000// 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/groupsock/inet.c000066400000000000000000000345251346756700600253470ustar00rootroot00000000000000#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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/000077500000000000000000000000001346756700600241165ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/Base64.cpp000066400000000000000000000107171346756700600256540ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/BasicUDPSource.cpp000066400000000000000000000056041346756700600274020ustar00rootroot00000000000000/********** 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 } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/COPYING000066400000000000000000000000121346756700600251420ustar00rootroot00000000000000../COPYINGpvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/DigestAuthentication.cpp000066400000000000000000000115641346756700600307500ustar00rootroot00000000000000/********** 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); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/FramedFilter.cpp000066400000000000000000000032111346756700600271630ustar00rootroot00000000000000/********** 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(); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/FramedSource.cpp000066400000000000000000000100111346756700600271720ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/Locale.cpp000066400000000000000000000037421346756700600260270ustar00rootroot00000000000000/********** 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 } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/MPEG2TransportStreamFramer.cpp000066400000000000000000000223371346756700600316710ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/Media.cpp000066400000000000000000000107031346756700600256420ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/MediaSession.cpp000066400000000000000000001421231346756700600272100ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/MediaSink.cpp000066400000000000000000000144571346756700600265010ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/MediaSource.cpp000066400000000000000000000050521346756700600270240ustar00rootroot00000000000000/********** 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(""); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/MultiFramedRTPSink.cpp000066400000000000000000000375301346756700600302560ustar00rootroot00000000000000/********** 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(); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/MultiFramedRTPSource.cpp000066400000000000000000000455241346756700600306140ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/RTCP.cpp000066400000000000000000000744311346756700600254030ustar00rootroot00000000000000/********** 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); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/RTPInterface.cpp000066400000000000000000000345201346756700600271140ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/RTPSink.cpp000066400000000000000000000270021346756700600261150ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/RTPSource.cpp000066400000000000000000000306031346756700600264520ustar00rootroot00000000000000/********** 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; } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/RTSPClient.cpp000066400000000000000000002405241346756700600265600ustar00rootroot00000000000000/********** 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: "); } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/RTSPCommon.cpp000066400000000000000000000126141346756700600265670ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/SimpleRTPSink.cpp000066400000000000000000000064241346756700600272740ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/SimpleRTPSource.cpp000066400000000000000000000046431346756700600276310ustar00rootroot00000000000000/********** 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; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/000077500000000000000000000000001346756700600255415ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/Base64.hh000066400000000000000000000032761346756700600271160ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/BasicUDPSource.hh000066400000000000000000000032731346756700600306420ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/DigestAuthentication.hh000066400000000000000000000052611346756700600322050ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/FramedFilter.hh000066400000000000000000000033261346756700600304320ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/FramedSource.hh000066400000000000000000000057571346756700600304570ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/Locale.hh000066400000000000000000000052241346756700600272640ustar00rootroot00000000000000/********** 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 #if !defined(XLOCALE_NOT_USED) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 26)) #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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/MPEG2TransportStreamFramer.hh000066400000000000000000000045761346756700600331360ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/Media.hh000066400000000000000000000074121346756700600271050ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/MediaSession.hh000066400000000000000000000316711346756700600304550ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/MediaSink.hh000066400000000000000000000107511346756700600277320ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/MediaSource.hh000066400000000000000000000036751346756700600302750ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/MultiFramedRTPSink.hh000066400000000000000000000126751346756700600315210ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/MultiFramedRTPSource.hh000066400000000000000000000124471346756700600320520ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/RTCP.hh000066400000000000000000000155531346756700600266430ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/RTPInterface.hh000066400000000000000000000073651346756700600303630ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/RTPSink.hh000066400000000000000000000172061346756700600273620ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/RTPSource.hh000066400000000000000000000204041346756700600277100ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/RTSPClient.hh000066400000000000000000000235261346756700600300210ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/RTSPCommon.hh000066400000000000000000000034131346756700600300240ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/SimpleRTPSink.hh000066400000000000000000000054741346756700600305400ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/SimpleRTPSource.hh000066400000000000000000000047651346756700600310760ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/liveMedia.hh000066400000000000000000000077371346756700600277770ustar00rootroot00000000000000/********** 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/include/liveMedia_version.hh000066400000000000000000000004461346756700600315320ustar00rootroot00000000000000// 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/our_md5.c000066400000000000000000000231041346756700600256340ustar00rootroot00000000000000/* * 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)); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/our_md5.h000066400000000000000000000040521346756700600256420ustar00rootroot00000000000000/* 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_ */ pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/our_md5hl.c000066400000000000000000000030601346756700600261570ustar00rootroot00000000000000#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); } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/rtcp_from_spec.c000066400000000000000000000244521346756700600272760ustar00rootroot00000000000000/* 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; } } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/live555/liveMedia/rtcp_from_spec.h000066400000000000000000000044041346756700600272760ustar00rootroot00000000000000/* 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/sources.txt000066400000000000000000000002061346756700600232430ustar00rootroot00000000000000tsreader: Sources adapted from MediaPortal v1.2.0 tsreader https://github.com/MediaPortal/MediaPortal-1/tree/master/DirectShowFilters pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/000077500000000000000000000000001346756700600226325ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/ChannelInfo.cpp000066400000000000000000000027361346756700600255320ustar00rootroot00000000000000/* * Copyright (C) 2006 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "ChannelInfo.h" namespace MPTV { CChannelInfo::CChannelInfo(void) { Reset(); } CChannelInfo::~CChannelInfo(void) { } void CChannelInfo::Reset() { PatVersion = -1; LCN = 10000; NetworkId = 0; TransportId = 0; ServiceId = 0; EIT_schedule_flag = 0; EIT_present_following_flag = 0; RunningStatus = 0; FreeCAMode = 0; ServiceType = 0; MajorChannel = 0; MinorChannel = 0; Frequency = 0; Modulation = 0; strcpy(ProviderName, ""); strcpy(ServiceName, ""); } }pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/ChannelInfo.h000066400000000000000000000027351346756700600251760ustar00rootroot00000000000000/* * Copyright (C) 2006 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once #include "PidTable.h" namespace MPTV { class CChannelInfo { public: CChannelInfo(void); virtual ~CChannelInfo(void); void Reset(); int NetworkId; int TransportId; int ServiceId; int MajorChannel; int MinorChannel; int Frequency; int EIT_schedule_flag; int EIT_present_following_flag; int RunningStatus; int FreeCAMode; int ServiceType; int Modulation; int LCN; char ProviderName[255]; char ServiceName[255]; CPidTable PidTable; int PatVersion; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/DeMultiplexer.cpp000066400000000000000000000210201346756700600261140ustar00rootroot00000000000000/* * Copyright (C) 2005-2013 Team Kodi * https://kodi.tv * * 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 ************************************************************************* * This file originates from TSFileSource, a GPL directshow push * source filter that provides an MPEG transport stream output. * Copyright (C) 2005-2006 nate, bear * http://forums.dvbowners.com/ */ #include "DeMultiplexer.h" #include "utils.h" //UNUSED() #include "client.h" //KODI->Log() #include "TSReader.h" #define MAX_BUF_SIZE 8000 #define BUFFER_LENGTH 0x1000 #define READ_SIZE (1316*30) #define INITIAL_READ_SIZE (READ_SIZE * 1024) using namespace ADDON; namespace MPTV { CDeMultiplexer::CDeMultiplexer(CTsReader& filter) :m_filter(filter), m_LastDataFromRtsp(0), m_bEndOfFile(false), m_reader(NULL), m_iPatVersion(-1), m_ReqPatVersion(-1), m_WaitNewPatTmo(0), m_receivedPackets(0), m_bStarting(false), m_bAudioAtEof(false), m_bVideoAtEof(false), m_bGotNewChannel(false) { m_patParser.SetCallBack(this); } CDeMultiplexer::~CDeMultiplexer() { } /// Starts the demuxer /// This method will read the file until we found the pat/sdt /// with all the audio/video pids void CDeMultiplexer::Start() { //reset some values m_bStarting = true; m_receivedPackets = 0; m_bEndOfFile = false; m_iPatVersion = -1; m_ReqPatVersion = -1; unsigned long long m_Time = GetTickCount64(); while ((GetTickCount64() - m_Time) < 5000 && m_bGotNewChannel == false) { size_t BytesRead = ReadFromFile(); if (0 == BytesRead) usleep(10000); } m_bStarting = false; } void CDeMultiplexer::SetFileReader(FileReader* reader) { m_reader = reader; } /// This method reads the next READ_SIZE bytes from the file /// and processes the raw data /// When a TS packet has been discovered, OnTsPacket(byte* tsPacket) gets called // which in its turn deals with the packet size_t CDeMultiplexer::ReadFromFile() { if (m_filter.IsSeeking()) return 0; // Ambass : to check P8PLATFORM::CLockObject lock(m_sectionRead); if (NULL == m_reader) return 0; byte buffer[READ_SIZE]; size_t dwReadBytes = 0; // if we are playing a RTSP stream if (m_reader->IsBuffer()) { // and, the current buffer holds data size_t nBytesToRead = m_reader->HasData(); if (nBytesToRead > sizeof(buffer)) { nBytesToRead = sizeof(buffer); } else { m_bAudioAtEof = true; m_bVideoAtEof = true; } if (nBytesToRead) { // then read raw data from the buffer if (SUCCEEDED(m_reader->Read(buffer, nBytesToRead, &dwReadBytes))) { if (dwReadBytes > 0) { // yes, then process the raw data //result=true; OnRawData(buffer, dwReadBytes); m_LastDataFromRtsp = GetTickCount64(); } } else { KODI->Log(LOG_DEBUG, "%s: Read failed...", __FUNCTION__); } } else { if (!m_filter.IsTimeShifting()) { KODI->Log(LOG_DEBUG, "%s: endoffile... %llu", __FUNCTION__, GetTickCount64() - m_LastDataFromRtsp); //set EOF flag and return if (GetTickCount64() - m_LastDataFromRtsp > 2000 && m_filter.State() != State_Paused) // A bit crappy, but no better idea... { KODI->Log(LOG_DEBUG, "%s: endoffile!", __FUNCTION__); m_bEndOfFile = true; return 0; } } } return dwReadBytes; } else { // playing a local file. // read raw data from the file if (SUCCEEDED(m_reader->Read(buffer, sizeof(buffer), &dwReadBytes))) { if ((m_filter.IsTimeShifting()) && (dwReadBytes < sizeof(buffer))) { m_bAudioAtEof = true; m_bVideoAtEof = true; } if (dwReadBytes > 0) { // succeeded, process data OnRawData(buffer, (int)dwReadBytes); } else { if (!m_filter.IsTimeShifting()) { // set EOF flag and return KODI->Log(LOG_DEBUG, "%s: endoffile!", __FUNCTION__); m_bEndOfFile = true; return 0; } } // and return return dwReadBytes; } else { KODI->Log(LOG_DEBUG, "%s: Read failed...", __FUNCTION__); } } return 0; } /// This method gets called via ReadFile() when a new TS packet has been received /// if will : /// - decode any new pat/pmt/sdt /// - decode any audio/video packets and put the PES packets in the appropiate buffers void CDeMultiplexer::OnTsPacket(byte* tsPacket) { CTsHeader header(tsPacket); m_patParser.OnTsPacket(tsPacket); if (m_iPatVersion == -1) { // First Pat not found return; } // Wait for new PAT if required. if ((m_iPatVersion & 0x0F) != (m_ReqPatVersion & 0x0F)) { if (m_ReqPatVersion == -1) { // Now, unless channel change, m_ReqPatVersion = m_iPatVersion; // Initialize Pat Request. m_WaitNewPatTmo = GetTickCount64(); // Now, unless channel change request,timeout will be always true. } if (GetTickCount64() < m_WaitNewPatTmo) { // Timeout not reached. return; } } } void CDeMultiplexer::RequestNewPat(void) { if (m_reader) { m_ReqPatVersion++; m_ReqPatVersion &= 0x0F; KODI->Log(LOG_DEBUG, "Request new PAT = %d", m_ReqPatVersion); m_WaitNewPatTmo = GetTickCount64() + 10000; size_t dwBytesProcessed = 0; unsigned long long m_Time = GetTickCount64(); m_bGotNewChannel = false; while ((GetTickCount64() - m_Time) < 5000 && m_bGotNewChannel == false) { size_t BytesRead = ReadFromFile(); if (0 == BytesRead) usleep(10000); dwBytesProcessed += BytesRead; } KODI->Log(LOG_DEBUG, "Found a new channel after processing %li bytes. File position: %I64d\n", dwBytesProcessed, m_reader->GetFilePointer()); } } /// This method gets called-back from the pat parser when a new PAT/PMT/SDT has been received /// In this method we check if any audio/video/subtitle pid or format has changed /// If not, we simply return /// If something has changed we ask the MP to rebuild the graph void CDeMultiplexer::OnNewChannel(CChannelInfo& UNUSED(info)) { m_bGotNewChannel = true; } }pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/DeMultiplexer.h000066400000000000000000000050001346756700600255610ustar00rootroot00000000000000/* * Copyright (C) 2005-2013 Team Kodi * https://kodi.tv * * 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 ************************************************************************* * This file originates from TSFileSource, a GPL directshow push * source filter that provides an MPEG transport stream output. * Copyright (C) 2005-2006 nate, bear * http://forums.dvbowners.com/ */ #pragma once #include "MultiFileReader.h" #include "PacketSync.h" #include "TSHeader.h" #include "PatParser.h" #include "p8-platform/threads/mutex.h" namespace MPTV { class CTsReader; class CDeMultiplexer : public CPacketSync, public IPatParserCallback { public: CDeMultiplexer(CTsReader& filter); virtual ~CDeMultiplexer(void); void Start(); void OnTsPacket(byte* tsPacket); void OnNewChannel(CChannelInfo& info); void SetFileReader(FileReader* reader); void RequestNewPat(void); size_t ReadFromFile(); private: unsigned long long m_LastDataFromRtsp; bool m_bEndOfFile; P8PLATFORM::CMutex m_sectionRead; FileReader* m_reader; CPatParser m_patParser; CTsReader& m_filter; int m_iPatVersion; int m_ReqPatVersion; unsigned long long m_WaitNewPatTmo; int m_receivedPackets; bool m_bStarting; bool m_bAudioAtEof; bool m_bVideoAtEof; // Kodi specific bool m_bGotNewChannel; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/DvbUtil.cpp000066400000000000000000000173651346756700600247230ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "DvbUtil.h" namespace MPTV { /* CRC table for PSI sections */ static uint32_t crc_table[256] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; //******************************************************************* //* calculate crc for a data block //* data : block of data //* len : length of data //******************************************************************* uint32_t crc32(char *data, int len) { register int i; uint32_t crc = 0xffffffff; for (i = 0; i < len; i++) crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *data++) & 0xff]; return crc; } CDvbUtil::CDvbUtil(void) { } CDvbUtil::~CDvbUtil(void) { } void CDvbUtil::getString468A(byte *buf, size_t bufLen, char *text, size_t textLen) { byte c; uint16_t w; size_t bufIndex = 0, textIndex = 0; if (buf == NULL) return; if (bufLen < 1) return; if (text == NULL) return; if (textLen < 2) return; // reserve place for terminating 0 textLen--; c = buf[bufIndex++]; if (c != 0x11) { bufIndex--; // need to start from 1st byte // check for character coding info byte if (c == 0x10) { // three byte encoding if (textLen >= 3) { text[textIndex++] = 0x10; text[textIndex++] = buf[2]; text[textIndex] = 0; bufIndex += 2; } else return; } while ((bufIndex < bufLen) && (textIndex < textLen)) { c = buf[bufIndex++]; if (c == 0x8A) { c = '\r'; } else if (((c > 0x05) && (c <= 0x1F)) || ((c >= 0x80) && (c < 0x9F))) //0x1-0x5 = choose character set, must keep this byte! c = 0; // ignore if (c != 0) text[textIndex++] = c; } } else { // process 2 byte unicode characters by reencoding it // to UTF-8 to avoid zero bytes inside string text[textIndex++] = 0x15; text[textIndex] = 0; while (bufIndex + 1 < bufLen) { w = (buf[bufIndex++] << 8); w |= buf[bufIndex++]; if (w == 0xE08A) w = '\r'; else if (((w > 0x05) && (w <= 0x1F)) || ((w >= 0xE080) && (w < 0xE09F))) w = 0; if (w != 0) { if (w < 0x80) c = 1; else if (w < 0x800) c = 2; else c = 3; if (textIndex + c >= textLen) break; if (w < 0x80) text[textIndex++] = (char)w; else if (w < 0x800) { text[textIndex++] = (char)((w >> 6) | 0xC0); text[textIndex++] = (char)((w & 0x3F) | 0x80); } else { text[textIndex++] = (char)((w >> 12) | 0xE0); text[textIndex++] = (char)(((w >> 6) & 0x3F) | 0x80); text[textIndex++] = (char)((w & 0x3F) | 0x80); } } } } text[textIndex] = 0; } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/DvbUtil.h000066400000000000000000000022201346756700600243500ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once #include "os-dependent.h" namespace MPTV { uint32_t crc32(char *data, int len); class CDvbUtil { public: CDvbUtil(void); void getString468A(byte *buf, size_t bufLen, char *text, size_t textLen); public: virtual ~CDvbUtil(void); }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/FileReader.cpp000066400000000000000000000157371346756700600253550ustar00rootroot00000000000000/* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 ************************************************************************* * This file originates from TSFileSource, a GPL directshow push * source filter that provides an MPEG transport stream output. * Copyright (C) 2005-2006 nate, bear * http://forums.dvbowners.com/ */ #include "FileReader.h" #include "client.h" //for KODI->Log #include "TSDebug.h" #include "p8-platform/threads/threads.h" #include //std::min, std::max #include "p8-platform/util/timeutils.h" // for usleep #include "p8-platform/util/util.h" #include "utils.h" #include using namespace ADDON; /* indicate that caller can handle truncated reads, where function returns before entire buffer has been filled */ #define READ_TRUNCATED 0x01 /* indicate that that caller support read in the minimum defined chunk size, this disables internal cache then */ #define READ_CHUNKED 0x02 /* use cache to access this file */ #define READ_CACHED 0x04 /* open without caching. regardless to file type. */ #define READ_NO_CACHE 0x08 /* calcuate bitrate for file while reading */ #define READ_BITRATE 0x10 namespace MPTV { FileReader::FileReader() : m_hFile(NULL), m_fileSize(0), m_fileName("") { } FileReader::~FileReader() { CloseFile(); } long FileReader::GetFileName(std::string& fileName) { fileName = m_fileName; return S_OK; } long FileReader::SetFileName(const std::string& fileName) { m_fileName = ToKodiPath(fileName); return S_OK; } // // OpenFile // // Opens the file ready for streaming // long FileReader::OpenFile(const std::string& fileName) { SetFileName(fileName); return OpenFile(); } long FileReader::OpenFile() { int Tmo = 25; //5 in MediaPortal // Is the file already opened if (!IsFileInvalid()) { KODI->Log(LOG_NOTICE, "FileReader::OpenFile() file already open"); return S_OK; } // Has a filename been set yet if (m_fileName.empty()) { KODI->Log(LOG_ERROR, "FileReader::OpenFile() no filename"); return ERROR_INVALID_NAME; } do { KODI->Log(LOG_INFO, "FileReader::OpenFile() %s.", m_fileName.c_str()); void* fileHandle = KODI->OpenFile(m_fileName.c_str(), READ_CHUNKED); if (fileHandle) { m_hFile = fileHandle; break; } else { struct __stat64 buffer; int statResult = KODI->StatFile(m_fileName.c_str(), &buffer); if (statResult < 0) { if (errno == EACCES) { KODI->Log(LOG_ERROR, "Permission denied. Check the file or share access rights for '%s'", m_fileName.c_str()); KODI->QueueNotification(QUEUE_ERROR, "Permission denied"); Tmo = 0; break; } } } usleep(20000); } while (--Tmo); if (Tmo) { if (Tmo < 4) // 1 failed + 1 succeded is quasi-normal, more is a bit suspicious ( disk drive too slow or problem ? ) KODI->Log(LOG_DEBUG, "FileReader::OpenFile(), %d tries to succeed opening %ws.", 6 - Tmo, m_fileName.c_str()); } else { KODI->Log(LOG_ERROR, "FileReader::OpenFile(), open file %s failed.", m_fileName.c_str()); return S_FALSE; } KODI->Log(LOG_DEBUG, "%s: OpenFile(%s) succeeded.", __FUNCTION__, m_fileName.c_str()); SetFilePointer(0, SEEK_SET); return S_OK; } // Open // // CloseFile // // Closes any dump file we have opened // long FileReader::CloseFile() { if (m_hFile) { KODI->CloseFile(m_hFile); m_hFile = NULL; } return S_OK; } // CloseFile inline bool FileReader::IsFileInvalid() { return (m_hFile == NULL); } int64_t FileReader::SetFilePointer(int64_t llDistanceToMove, unsigned long dwMoveMethod) { TSDEBUG(LOG_DEBUG, "%s: distance %d method %d.", __FUNCTION__, llDistanceToMove, dwMoveMethod); int64_t rc = KODI->SeekFile(m_hFile, llDistanceToMove, dwMoveMethod); TSDEBUG(LOG_DEBUG, "%s: distance %d method %d returns %d.", __FUNCTION__, llDistanceToMove, dwMoveMethod, rc); return rc; } int64_t FileReader::GetFilePointer() { return KODI->GetFilePosition(m_hFile); } long FileReader::Read(unsigned char* pbData, size_t lDataLength, size_t *dwReadBytes) { ssize_t read_bytes = KODI->ReadFile(m_hFile, (void*)pbData, lDataLength);//Read file data into buffer if (read_bytes < 0) { TSDEBUG(LOG_DEBUG, "%s: ReadFile function failed.", __FUNCTION__); *dwReadBytes = 0; return S_FALSE; } *dwReadBytes = static_cast(read_bytes); TSDEBUG(LOG_DEBUG, "%s: requested read length %d actually read %d.", __FUNCTION__, lDataLength, *dwReadBytes); if (*dwReadBytes < lDataLength) { KODI->Log(LOG_NOTICE, "%s: requested %d bytes, read only %d bytes.", __FUNCTION__, lDataLength, *dwReadBytes); return S_FALSE; } return S_OK; } int64_t FileReader::GetFileSize() { return KODI->GetFileLength(m_hFile); } int64_t FileReader::OnChannelChange(void) { return KODI->GetFilePosition(m_hFile); } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/FileReader.h000066400000000000000000000051171346756700600250110ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 ************************************************************************* * This file originates from TSFileSource, a GPL directshow push * source filter that provides an MPEG transport stream output. * Copyright (C) 2005-2006 nate, bear * http://forums.dvbowners.com/ */ #include "p8-platform/os.h" // for __stat #include namespace MPTV { class FileReader { public: FileReader(); virtual ~FileReader(); // Open and write to the file virtual long GetFileName(std::string& fileName); virtual long SetFileName(const std::string& fileName); virtual long OpenFile(const std::string& fileName); virtual long OpenFile(); virtual long CloseFile(); virtual long Read(unsigned char* pbData, size_t lDataLength, size_t *dwReadBytes); virtual bool IsFileInvalid(); virtual int64_t SetFilePointer(int64_t llDistanceToMove, unsigned long dwMoveMethod); virtual int64_t GetFilePointer(); virtual int64_t GetFileSize(); virtual bool IsBuffer() { return false; }; virtual int64_t OnChannelChange(void); virtual size_t HasData(){ return 0; }; protected: void* m_hFile; // Handle to file for streaming std::string m_fileName; // The filename where we read from int64_t m_fileSize; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/ISectionCallback.h000066400000000000000000000031431346756700600261360ustar00rootroot00000000000000/* * 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 ************************************************************************* * This file originates from TSFileSource, a GPL directshow push * source filter that provides an MPEG transport stream output. * Copyright (C) 2005-2006 nate, bear * http://forums.dvbowners.com/ */ #pragma once #include "Section.h" namespace MPTV { class ISectionCallback { public: virtual void OnNewSection(int pid, int tableId, CSection& section) = 0; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MemoryBuffer.cpp000066400000000000000000000114221346756700600257400ustar00rootroot00000000000000/* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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; } // KODI->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()) { KODI->Log(LOG_DEBUG, "memorybuffer: read:empty buffer\n"); return 0; } BufferItem *item = m_Array.at(0); if (NULL == item) { KODI->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) { KODI->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 unsigned char[lDataLength]; memcpy(item->data, pbData, lDataLength); bool sleep = false; { P8PLATFORM::CLockObject BufferLock(m_BufferLock); m_Array.push_back(item); m_BytesInBuffer += lDataLength; //KODI->Log(LOG_DEBUG, "add..%d/%d",lDataLength,m_BytesInBuffer); while (m_BytesInBuffer > MAX_MEMORY_BUFFER_SIZE) { sleep = true; KODI->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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MemoryBuffer.h000066400000000000000000000035271346756700600254140ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MemoryReader.cpp000066400000000000000000000035051346756700600257340ustar00rootroot00000000000000/* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 "MemoryReader.h" namespace MPTV { CMemoryReader::CMemoryReader(CMemoryBuffer& buffer) :m_buffer(buffer) { } CMemoryReader::~CMemoryReader(void) { } long CMemoryReader::Read(unsigned char* pbData, size_t lDataLength, size_t *dwReadBytes) { *dwReadBytes = m_buffer.ReadFromBuffer(pbData, lDataLength); if ((*dwReadBytes) == 0) return S_FALSE; return S_OK; } int64_t CMemoryReader::SetFilePointer(int64_t llDistanceToMove, unsigned long dwMoveMethod) { return 0; } size_t CMemoryReader::HasData() { return (m_buffer.Size()); } } #endif //LIVE555 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MemoryReader.h000066400000000000000000000035271346756700600254050ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 "FileReader.h" #include "MemoryBuffer.h" namespace MPTV { class CMemoryReader : public FileReader { public: CMemoryReader(CMemoryBuffer& buffer); virtual ~CMemoryReader(void); virtual long Read(unsigned char* pbData, size_t lDataLength, size_t *dwReadBytes); virtual int64_t SetFilePointer(int64_t llDistanceToMove, unsigned long dwMoveMethod); virtual bool IsBuffer() { return true; }; virtual long CloseFile() { return S_OK; }; virtual size_t HasData(); private: CMemoryBuffer& m_buffer; CMemoryReader& operator=(const CMemoryReader& memoryreader) {}; }; } #endif //LIVE555 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MemorySink.cpp000066400000000000000000000056571346756700600254500ustar00rootroot00000000000000/* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 unsigned char[bufferSize]; m_pSubmitBuffer = new unsigned char[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) { KODI->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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MemorySink.h000066400000000000000000000043601346756700600251030ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MepoRTSPClient.cpp000066400000000000000000000435401346756700600261140ustar00rootroot00000000000000/* * Copyright (C) 2005-2010 Team Kodi * https://kodi.tv * * 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" #include "os-dependent.h" using namespace ADDON; CRTSPClient::CRTSPClient() { KODI->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() { KODI->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) { KODI->Log(LOG_DEBUG, "CRTSPClient::createClient()"); return RTSPClient::createNew(env, verbosityLevel, applicationName,tunnelOverHTTPPortNum); } char* CRTSPClient::getOptionsResponse(Medium* client, char const* url,char* username, char* password) { KODI->Log(LOG_DEBUG, "CRTSPClient::getOptionsResponse()"); RTSPClient* rtspClient = (RTSPClient*)client; char* optionsResponse = rtspClient->sendOptionsCmd(url, username, password); if (optionsResponse == NULL) { KODI->Log(LOG_DEBUG, "CRTSPClient::getOptionsResponse(): \"OPTIONS\" request failed: %s", m_env->getResultMsg()); } else { KODI->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*/) { KODI->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() { KODI->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); KODI->Log(LOG_DEBUG, "CRTSPClient::getSDPDescription() statusCode = %d", rtspClient->describeStatus()); Medium::close(rtspClient); return result; } bool CRTSPClient::clientSetupSubsession(Medium* client, MediaSubsession* subsession, bool streamUsingTCP) { KODI->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) { KODI->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; } KODI->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) { KODI->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)) { KODI->Log(LOG_DEBUG, "CRTSPClient::subsessionAfterPlaying()"); } void my_subsessionByeHandler(void* UNUSED(clientData)) { KODI->Log(LOG_DEBUG, "CRTSPClient::subsessionByeHandler()"); } void CRTSPClient::closeMediaSinks() { if (m_session == NULL) return; KODI->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; KODI->Log(LOG_DEBUG, "CRTSPClient::tearDownStreams()"); clientTearDownSession(m_ourClient, m_session); } bool CRTSPClient::setupStreams() { // Setup streams KODI->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)) { KODI->Log(LOG_ERROR, "Failed to setup %s %s %s", subsession->mediumName(), subsession->codecName(), m_env->getResultMsg() ); } else { KODI->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() { KODI->Log(LOG_DEBUG, "CRTSPClient::startPlayingStreams()"); if (!clientStartPlayingSession(m_ourClient, m_session)) { KODI->Log(LOG_ERROR, "Failed to start playing session :%s", m_env ->getResultMsg() ); shutdown(); return false; } else { KODI->Log(LOG_DEBUG, "Started playing session"); } return true; } void CRTSPClient::shutdown() { KODI->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) { KODI->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) { KODI->Log(LOG_ERROR, "Failed to create %s %s", clientProtocolName, m_env->getResultMsg() ); shutdown(); return false; } return true; } bool CRTSPClient::OpenStream(const char* url) { KODI->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) { KODI->Log(LOG_ERROR, "Failed to get a SDP description from URL %s %s", url, m_env->getResultMsg() ); shutdown(); return false; } KODI->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); KODI->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) { KODI->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()) { KODI->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) { KODI->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)) { KODI->Log(LOG_ERROR, "Unable to create receiver for %s %s %s", subsession->mediumName(), subsession->codecName(), m_env->getResultMsg()); } else { KODI->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(); KODI->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); KODI->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) { KODI->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) { KODI->Log(LOG_DEBUG, "Failed to create FileSink %s", m_env->getResultMsg()); shutdown(); return false; } KODI->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() { KODI->Log(LOG_DEBUG, "CRTSPClient:Stop"); if (m_BufferThreadActive) { StopBufferThread(); } shutdown(); m_buffer->Clear(); KODI->Log(LOG_DEBUG, "CRTSPClient:Stop done"); } void CRTSPClient::StartBufferThread() { KODI->Log(LOG_DEBUG, "CRTSPClient::StartBufferThread"); if (!m_BufferThreadActive) { CreateThread(); m_BufferThreadActive = true; } KODI->Log(LOG_DEBUG, "CRTSPClient::StartBufferThread done"); } void CRTSPClient::StopBufferThread() { KODI->Log(LOG_DEBUG, "CRTSPClient::StopBufferThread"); m_bRunning = false; if (!m_BufferThreadActive) return; StopThread(); m_BufferThreadActive = false; KODI->Log(LOG_DEBUG, "CRTSPClient::StopBufferThread done"); } bool CRTSPClient::IsRunning() { return m_BufferThreadActive; } long CRTSPClient::Duration() { return m_duration; } void CRTSPClient::FillBuffer(unsigned long byteCount) { KODI->Log(LOG_DEBUG, "CRTSPClient::Fillbuffer...%d\n", byteCount); unsigned long long tickCount = GetTickCount64(); while ( IsRunning() && m_buffer->Size() < byteCount) { usleep(5000); if (GetTickCount64() - tickCount > 3000) break; } KODI->Log(LOG_DEBUG, "CRTSPClient::Fillbuffer...%d/%d\n", byteCount, m_buffer->Size() ); } void *CRTSPClient::Process() { m_BufferThreadActive = true; m_bRunning = true; KODI->Log(LOG_DEBUG, "CRTSPClient:: thread started"); while (m_env != NULL && !IsStopped()) { m_env->taskScheduler().doEventLoop(); if (m_bRunning == false) break; } KODI->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() { KODI->Log(LOG_DEBUG, "CRTSPClient::Pause()"); if (m_ourClient != NULL && m_session != NULL) { KODI->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 ???? KODI->Log(LOG_DEBUG, "CRTSPClient::Pause() thread stopped"); RTSPClient* rtspClient=(RTSPClient*)m_ourClient; rtspClient->pauseMediaSession(*m_session); m_bPaused = true; } KODI->Log(LOG_DEBUG, "CRTSPClient::Pause() done"); return true; } bool CRTSPClient::Play(double fStart,double fDuration) { KODI->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) { KODI->Log(LOG_ERROR, "UpdateStreamDuration: Failed to get a SDP description from URL %s %s", m_url, m_env->getResultMsg() ); return false; } //KODI->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); //KODI->Log(LOG_DEBUG, "rangestart:%f rangeend:%f", Start,End); m_duration = (long) ((End-Start)*1000.0); } } return true; } #endif //LIVE555 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MepoRTSPClient.h000066400000000000000000000070701346756700600255570ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2013 Team Kodi * https://kodi.tv * * 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 pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MultiFileReader.cpp000066400000000000000000000554411346756700600263640ustar00rootroot00000000000000/* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 ************************************************************************* * This file originates from TSFileSource, a GPL directshow push * source filter that provides an MPEG transport stream output. * Copyright (C) 2005-2006 nate, bear * http://forums.dvbowners.com/ */ #include "MultiFileReader.h" #include "client.h" //for KODI->Log #include "TSDebug.h" #include #include "utils.h" #include #include "p8-platform/util/timeutils.h" #include "p8-platform/threads/threads.h" #include using namespace ADDON; 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 namespace MPTV { MultiFileReader::MultiFileReader() : m_TSBufferFile(), m_TSFile(), m_startPosition(0), m_currentFileStartOffset(0), m_endPosition(0), m_currentPosition(0), m_lastZapPosition(0), m_filesAdded(0), m_filesRemoved(0), m_TSFileId(0), m_bDelay(0) { } MultiFileReader::~MultiFileReader() { //CloseFile called by ~FileReader } long MultiFileReader::SetFileName(const std::string& fileName) { return m_TSBufferFile.SetFileName(fileName); } long MultiFileReader::OpenFile(const std::string& fileName) { SetFileName(fileName); return OpenFile(); } // // OpenFile // long MultiFileReader::OpenFile() { long hResult = m_TSBufferFile.OpenFile(); KODI->Log(LOG_DEBUG, "MultiFileReader: buffer file opened return code %d.", hResult); if (hResult != S_OK) return hResult; m_lastZapPosition = 0; m_currentFileStartOffset = 0; int retryCount = 0; while ((m_TSBufferFile.GetFileSize() == 0) && (retryCount < 50)) { retryCount++; KODI->Log(LOG_DEBUG, "MultiFileReader: buffer file has zero length, closing, waiting 100 ms and re-opening. Attempt: %d.", retryCount); m_TSBufferFile.CloseFile(); usleep(100000); hResult = m_TSBufferFile.OpenFile(); KODI->Log(LOG_DEBUG, "MultiFileReader: buffer file opened return code %d.", hResult); } if (RefreshTSBufferFile() == S_FALSE) { // For radio the buffer sometimes needs some time to become available, so wait and try it more than once P8PLATFORM::CTimeout timeout(MAX_BUFFER_TIMEOUT); do { usleep(100000); if (timeout.TimeLeft() == 0) { KODI->Log(LOG_ERROR, "MultiFileReader: timed out while waiting for buffer file to become available"); KODI->QueueNotification(QUEUE_ERROR, "Time out while waiting for buffer file"); return S_FALSE; } } while (RefreshTSBufferFile() == S_FALSE); } m_currentPosition = 0; return hResult; } // // CloseFile // long MultiFileReader::CloseFile() { long hr; std::vector::iterator it; m_TSBufferFile.CloseFile(); hr = m_TSFile.CloseFile(); for (it = m_tsFiles.begin(); it < m_tsFiles.end(); ++it) { delete (*it); } m_tsFiles.clear(); m_TSFileId = 0; return hr; } bool MultiFileReader::IsFileInvalid() { return m_TSBufferFile.IsFileInvalid(); } int64_t MultiFileReader::SetFilePointer(int64_t llDistanceToMove, unsigned long dwMoveMethod) { RefreshTSBufferFile(); if (dwMoveMethod == FILE_END) { m_currentPosition = m_endPosition + llDistanceToMove; } else if (dwMoveMethod == FILE_CURRENT) { m_currentPosition += llDistanceToMove; } else // if (dwMoveMethod == FILE_BEGIN) { m_currentPosition = m_startPosition + llDistanceToMove; } if (m_currentPosition < m_startPosition) m_currentPosition = m_startPosition; if (m_currentPosition > m_endPosition) { KODI->Log(LOG_ERROR, "Seeking beyond the end position: %I64d > %I64d", m_currentPosition, m_endPosition); m_currentPosition = m_endPosition; } return m_currentPosition; } int64_t MultiFileReader::SetCurrentFilePointer(int64_t timeShiftBufferFilePos, long timeshiftBufferFileID) { RefreshTSBufferFile(); if (m_TSFileId != timeshiftBufferFileID) { // We have to switch to a different buffer file TSDEBUG(LOG_DEBUG, "Change buffer file from %i to %i", m_TSFileId, timeshiftBufferFileID); MultiFileReaderFile *file = NULL; std::vector::iterator it = m_tsFiles.begin(); for (; it < m_tsFiles.end(); ++it) { file = *it; if (file->filePositionId == timeshiftBufferFileID) break; }; if (!file) { KODI->Log(LOG_ERROR, "MultiFileReader::no buffer file with id=%i", timeshiftBufferFileID); KODI->QueueNotification(QUEUE_ERROR, "No buffer file"); return m_currentPosition; } if (m_currentPosition < (file->startPosition + timeShiftBufferFilePos)) { m_TSFile.CloseFile(); m_TSFile.SetFileName(file->filename.c_str()); m_TSFile.OpenFile(); m_TSFileId = file->filePositionId; m_currentFileStartOffset = file->startPosition; TSDEBUG(LOG_DEBUG, "MultiFileReader::Read() Current File Changed to %s TS file id=%i\n", file->filename.c_str(), m_TSFileId); } } // Reposition the read pointer within the current timeshift buffer file TSDEBUG(LOG_DEBUG, "Move read pointer within buffer file %i; %I64d -> %i64d", m_TSFileId, m_currentPosition, m_currentFileStartOffset + timeShiftBufferFilePos); m_currentPosition = m_currentFileStartOffset + timeShiftBufferFilePos; if (m_currentPosition > m_endPosition) { KODI->Log(LOG_ERROR, "Seeking beyond the end position: %I64d > %I64d", m_currentPosition, m_endPosition); m_currentPosition = m_endPosition; } return m_currentPosition; } int64_t MultiFileReader::GetFilePointer() { return m_currentPosition; } long MultiFileReader::Read(unsigned char* pbData, size_t lDataLength, size_t *dwReadBytes) { // If the file has already been closed, don't continue if (m_TSBufferFile.IsFileInvalid()) return S_FALSE; RefreshTSBufferFile(); if (m_currentPosition < m_startPosition) { KODI->Log(LOG_INFO, "%s: current position adjusted from %%I64dd to %%I64dd.", __FUNCTION__, m_currentPosition, m_startPosition); m_currentPosition = m_startPosition; } // Find out which file the currentPosition is in. MultiFileReaderFile *file = NULL; std::vector::iterator it = m_tsFiles.begin(); for (; it < m_tsFiles.end(); ++it) { file = *it; if (m_currentPosition < (file->startPosition + file->length)) break; }; // KODI->Log(LOG_DEBUG, "%s: reading %ld bytes. File %s, start %d, current %d, end %d.", __FUNCTION__, lDataLength, file->filename.c_str(), m_startPosition, m_currentPosition, m_endPosition); if (!file) { KODI->Log(LOG_ERROR, "MultiFileReader::no file"); KODI->QueueNotification(QUEUE_ERROR, "No buffer file"); return S_FALSE; } if (m_currentPosition < (file->startPosition + file->length)) { if (m_TSFileId != file->filePositionId) { m_TSFile.CloseFile(); m_TSFile.SetFileName(file->filename.c_str()); if (m_TSFile.OpenFile() != S_OK) { KODI->Log(LOG_ERROR, "MultiFileReader: can't open %s\n", file->filename.c_str()); return S_FALSE; } m_TSFileId = file->filePositionId; m_currentFileStartOffset = file->startPosition; TSDEBUG(LOG_DEBUG, "MultiFileReader::Read() Current File Changed to %s TS file id=%i\n", file->filename.c_str(), m_TSFileId); } int64_t seekPosition = m_currentPosition - file->startPosition; m_TSFile.SetFilePointer(seekPosition, FILE_BEGIN); int64_t posSeeked = m_TSFile.GetFilePointer(); if (posSeeked != seekPosition) { m_TSFile.SetFilePointer(seekPosition, FILE_BEGIN); posSeeked = m_TSFile.GetFilePointer(); if (posSeeked != seekPosition) { KODI->Log(LOG_ERROR, "SEEK FAILED"); return S_FALSE; } } size_t bytesRead = 0; long hr; int64_t bytesToRead = file->length - seekPosition; if ((int64_t)lDataLength > bytesToRead) { // KODI->Log(LOG_DEBUG, "%s: datalength %lu bytesToRead %lli.", __FUNCTION__, lDataLength, bytesToRead); hr = m_TSFile.Read(pbData, (size_t)bytesToRead, &bytesRead); if (FAILED(hr)) { KODI->Log(LOG_ERROR, "READ FAILED1"); return S_FALSE; } m_currentPosition += bytesToRead; hr = this->Read(pbData + bytesToRead, lDataLength - (size_t)bytesToRead, dwReadBytes); if (FAILED(hr)) { KODI->Log(LOG_ERROR, "READ FAILED2"); } *dwReadBytes += bytesRead; } else { hr = m_TSFile.Read(pbData, lDataLength, dwReadBytes); if (FAILED(hr)) { KODI->Log(LOG_ERROR, "READ FAILED3"); } m_currentPosition += lDataLength; } } else { // The current position is past the end of the last file *dwReadBytes = 0; } // KODI->Log(LOG_DEBUG, "%s: read %lu bytes. start %lli, current %lli, end %lli.", __FUNCTION__, *dwReadBytes, m_startPosition, m_currentPosition, m_endPosition); return S_OK; } long MultiFileReader::RefreshTSBufferFile() { if (m_TSBufferFile.IsFileInvalid()) { KODI->Log(LOG_ERROR, "%s: buffer file is invalid.", __FUNCTION__); return S_FALSE; } size_t bytesRead; MultiFileReaderFile *file; int64_t currentPosition; int32_t filesAdded, filesRemoved; int32_t filesAdded2, filesRemoved2; long Error = 0; long Loop = 10; Wchar_t* pBuffer = NULL; do { Error = 0; currentPosition = -1; filesAdded = -1; filesRemoved = -1; filesAdded2 = -2; filesRemoved2 = -2; int64_t fileLength = m_TSBufferFile.GetFileSize(); // Min file length is Header ( int64_t + int32_t + int32_t ) + filelist ( > 0 ) + Footer ( int32_t + int32_t ) int64_t minimumlength = (int64_t)(sizeof(currentPosition) + sizeof(filesAdded) + sizeof(filesRemoved) + sizeof(Wchar_t) + sizeof(filesAdded2) + sizeof(filesRemoved2)); if (fileLength <= minimumlength) { KODI->Log(LOG_ERROR, "%s: TSBufferFile too short. Minimum length %ld, current length %ld", __FUNCTION__, minimumlength, fileLength); return S_FALSE; } m_TSBufferFile.SetFilePointer(0, FILE_BEGIN); size_t readLength = sizeof(currentPosition) + sizeof(filesAdded) + sizeof(filesRemoved); unsigned char* readBuffer = new unsigned char[readLength]; long result = m_TSBufferFile.Read(readBuffer, readLength, &bytesRead); if (!SUCCEEDED(result) || bytesRead != readLength) Error |= 0x02; if (Error == 0) { currentPosition = *((int64_t*)(readBuffer + 0)); filesAdded = *((int32_t*)(readBuffer + sizeof(currentPosition))); filesRemoved = *((int32_t*)(readBuffer + sizeof(currentPosition) + sizeof(filesAdded))); } delete[] readBuffer; // If no files added or removed, break the loop ! if ((m_filesAdded == filesAdded) && (m_filesRemoved == filesRemoved)) break; int64_t remainingLength = fileLength - sizeof(currentPosition) - sizeof(filesAdded) - sizeof(filesRemoved) - sizeof(filesAdded2) - sizeof(filesRemoved2); // Above 100kb seems stupid and figure out a problem !!! if (remainingLength > 100000) Error |= 0x10; pBuffer = (Wchar_t*) new char[(unsigned int)remainingLength]; result = m_TSBufferFile.Read((unsigned char*)pBuffer, (uint32_t)remainingLength, &bytesRead); if (!SUCCEEDED(result) || (int64_t)bytesRead != remainingLength) Error |= 0x20; readLength = sizeof(filesAdded) + sizeof(filesRemoved); readBuffer = new unsigned char[readLength]; result = m_TSBufferFile.Read(readBuffer, readLength, &bytesRead); if (!SUCCEEDED(result) || bytesRead != readLength) Error |= 0x40; if (Error == 0) { filesAdded2 = *((int32_t*)(readBuffer + 0)); filesRemoved2 = *((int32_t*)(readBuffer + sizeof(filesAdded2))); } delete[] readBuffer; if ((filesAdded2 != filesAdded) || (filesRemoved2 != filesRemoved)) { Error |= 0x80; KODI->Log(LOG_ERROR, "MultiFileReader has error 0x%x in Loop %d. Try to clear SMB Cache.", Error, 10 - Loop); KODI->Log(LOG_DEBUG, "%s: filesAdded %d, filesAdded2 %d, filesRemoved %d, filesRemoved2 %d.", __FUNCTION__, filesAdded, filesAdded2, filesRemoved, filesRemoved2); // try to clear local / remote SMB file cache. This should happen when we close the filehandle m_TSBufferFile.CloseFile(); m_TSBufferFile.OpenFile(); usleep(5000); } if (Error) delete[] pBuffer; Loop--; } while (Error && Loop); // If Error is set, try again...until Loop reaches 0. if (Loop < 8) { KODI->Log(LOG_DEBUG, "MultiFileReader has waited %d times for TSbuffer integrity.", 10 - Loop); if (Error) { KODI->Log(LOG_ERROR, "MultiFileReader has failed for TSbuffer integrity. Error : %x", Error); return E_FAIL; } } if ((m_filesAdded != filesAdded) || (m_filesRemoved != filesRemoved)) { int32_t filesToRemove = filesRemoved - m_filesRemoved; int32_t filesToAdd = filesAdded - m_filesAdded; int32_t fileID = filesRemoved; int64_t nextStartPosition = 0; TSDEBUG(LOG_DEBUG, "MultiFileReader: Files Added %i, Removed %i\n", filesToAdd, filesToRemove); // Removed files that aren't present anymore. while ((filesToRemove > 0) && (!m_tsFiles.empty())) { file = m_tsFiles.at(0); TSDEBUG(LOG_DEBUG, "MultiFileReader: Removing file %s\n", file->filename.c_str()); SAFE_DELETE(file); m_tsFiles.erase(m_tsFiles.begin()); filesToRemove--; } // Figure out what the start position of the next new file will be if (!m_tsFiles.empty()) { file = m_tsFiles.back(); if (filesToAdd > 0) { // If we're adding files the changes are the one at the back has a partial length // so we need update it. GetFileLength(file->filename.c_str(), file->length); } nextStartPosition = file->startPosition + file->length; } // Get the real path of the buffer file std::string sFilename; std::string path; m_TSBufferFile.GetFileName(sFilename); //size_t pos = sFilename.find_last_of(PATH_SEPARATOR_CHAR); size_t pos = sFilename.find_last_of('/'); path = sFilename.substr(0, pos + 1); // Create a list of files in the .tsbuffer file. std::vector filenames; Wchar_t* pwCurrFile = pBuffer; //Get a pointer to the first wchar filename string in pBuffer size_t length = WcsLen(pwCurrFile); //KODI->Log(LOG_DEBUG, "%s: WcsLen(%d), sizeof(Wchar_t) == %d sizeof(wchar_t) == %d.", __FUNCTION__, length, sizeof(Wchar_t), sizeof(wchar_t)); while (length > 0) { // Convert the current filename (wchar to normal char) char* wide2normal = new char[length + 1]; WcsToMbs(wide2normal, pwCurrFile, length); wide2normal[length] = '\0'; std::string sCurrFile = wide2normal; //KODI->Log(LOG_DEBUG, "%s: filename %s (%s).", __FUNCTION__, wide2normal, sCurrFile.c_str()); delete[] wide2normal; // Modify filename path here to include the real (local) path pos = sCurrFile.find_last_of(92); std::string name = sCurrFile.substr(pos + 1); if (path.length() > 0 && name.length() > 0) { // Replace the original path with our local path filenames.push_back(path + name); } else { // Keep existing path filenames.push_back(sCurrFile); } // Move the wchar buffer pointer to the next wchar string pwCurrFile += (length + 1); length = WcsLen(pwCurrFile); } // Go through files std::vector::iterator itFiles = m_tsFiles.begin(); std::vector::iterator itFilenames = filenames.begin(); while (itFiles < m_tsFiles.end()) { file = *itFiles; ++itFiles; fileID++; if (itFilenames < filenames.end()) { // TODO: Check that the filenames match. ( Ambass : With buffer integrity check, probably no need to do this !) ++itFilenames; } else { KODI->Log(LOG_DEBUG, "MultiFileReader: Missing files!!\n"); } } while (itFilenames < filenames.end()) { std::string pFilename = *itFilenames; TSDEBUG(LOG_DEBUG, "%s: Adding file %s (%" PRId64 ")\n", __FUNCTION__, pFilename.c_str(), nextStartPosition); file = new MultiFileReaderFile(); file->filename = pFilename; file->startPosition = nextStartPosition; fileID++; file->filePositionId = fileID; GetFileLength(file->filename.c_str(), file->length); m_tsFiles.push_back(file); nextStartPosition = file->startPosition + file->length; ++itFilenames; } m_filesAdded = filesAdded; m_filesRemoved = filesRemoved; delete[] pBuffer; } if (!m_tsFiles.empty()) { file = m_tsFiles.front(); m_startPosition = file->startPosition; // Since the buffer file may be re-used when a channel is changed, we // want the start position to reflect the position in the file after the last // channel change, or the real start position, whichever is larger if (m_lastZapPosition > m_startPosition) { m_startPosition = m_lastZapPosition; } file = m_tsFiles.back(); file->length = currentPosition; m_endPosition = file->startPosition + currentPosition; TSDEBUG(LOG_DEBUG, "StartPosition %lli, EndPosition %lli, CurrentPosition %lli\n", m_startPosition, m_endPosition, m_currentPosition); } else { m_startPosition = 0; m_endPosition = 0; } return S_OK; } long MultiFileReader::GetFileLength(const char* pFilename, int64_t &length) { //USES_CONVERSION; length = 0; // Try to open the file void* hFile; if (((hFile = KODI->OpenFile(pFilename, 0)) != NULL)) { length = KODI->GetFileLength(hFile); KODI->CloseFile(hFile); } else { KODI->Log(LOG_ERROR, "Failed to open file %s : 0x%x(%s)\n", pFilename, errno, strerror(errno)); KODI->QueueNotification(QUEUE_ERROR, "Failed to open file %s", pFilename); return S_FALSE; } return S_OK; } int64_t MultiFileReader::GetFileSize() { RefreshTSBufferFile(); return m_endPosition - m_startPosition; } int64_t MultiFileReader::OnChannelChange(void) { m_lastZapPosition = m_currentPosition; return m_currentPosition; } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/MultiFileReader.h000066400000000000000000000060501346756700600260210ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 ************************************************************************* * This file originates from TSFileSource, a GPL directshow push * source filter that provides an MPEG transport stream output. * Copyright (C) 2005-2006 nate, bear * http://forums.dvbowners.com/ */ #include "FileReader.h" #include #include namespace MPTV { class MultiFileReaderFile { public: std::string filename; int64_t startPosition; int64_t length; long filePositionId; }; class MultiFileReader : public FileReader { public: MultiFileReader(); virtual ~MultiFileReader(); virtual long SetFileName(const std::string& fileName); virtual long OpenFile(); virtual long OpenFile(const std::string& fileName); virtual long CloseFile(); virtual long Read(unsigned char* pbData, size_t lDataLength, size_t *dwReadBytes); virtual bool IsFileInvalid(); virtual int64_t SetFilePointer(int64_t llDistanceToMove, unsigned long dwMoveMethod); virtual int64_t GetFilePointer(); virtual int64_t GetFileSize(); virtual int64_t OnChannelChange(void); int64_t SetCurrentFilePointer(int64_t timeShiftBufferFilePos, long timeshiftBufferFileID); protected: long RefreshTSBufferFile(); long GetFileLength(const char* pFilename, int64_t &length); FileReader m_TSBufferFile; int64_t m_startPosition; int64_t m_currentFileStartOffset; int64_t m_endPosition; int64_t m_currentPosition; int64_t m_lastZapPosition; int32_t m_filesAdded; int32_t m_filesRemoved; std::vector m_tsFiles; FileReader m_TSFile; long m_TSFileId; bool m_bDelay; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/PacketSync.cpp000066400000000000000000000052441346756700600254070ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "PacketSync.h" #include "utils.h" namespace MPTV { CPacketSync::CPacketSync(void) { m_tempBufferPos = -1; } CPacketSync::~CPacketSync(void) { } void CPacketSync::Reset(void) { m_tempBufferPos = -1; } // Ambass : Now, need to have 2 consecutive TS_PACKET_SYNC to try avoiding bad synchronisation. // In case of data flow change ( Seek, tv Zap .... ) Reset() should be called first to flush buffer. void CPacketSync::OnRawData(byte* pData, size_t nDataLen) { size_t syncOffset = 0; if (m_tempBufferPos > 0) { if (pData[TS_PACKET_LEN - m_tempBufferPos] == TS_PACKET_SYNC) { syncOffset = TS_PACKET_LEN - m_tempBufferPos; if (syncOffset) memcpy(&m_tempBuffer[m_tempBufferPos], pData, syncOffset); OnTsPacket(m_tempBuffer); } m_tempBufferPos = 0; } while (syncOffset + TS_PACKET_LEN < nDataLen) { if ((pData[syncOffset] == TS_PACKET_SYNC) && (pData[syncOffset + TS_PACKET_LEN] == TS_PACKET_SYNC)) { OnTsPacket(&pData[syncOffset]); syncOffset += TS_PACKET_LEN; } else syncOffset++; } // Here we have less than 188+1 bytes while (syncOffset < nDataLen) { if (pData[syncOffset] == TS_PACKET_SYNC) { m_tempBufferPos = nDataLen - syncOffset; memcpy(m_tempBuffer, &pData[syncOffset], m_tempBufferPos); return; } else syncOffset++; } m_tempBufferPos = 0; } void CPacketSync::OnTsPacket(byte* UNUSED(tsPacket)) { } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/PacketSync.h000066400000000000000000000024511346756700600250510ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once #define TS_PACKET_SYNC 0x47 #define TS_PACKET_LEN 188 #include "os-dependent.h" namespace MPTV { class CPacketSync { public: CPacketSync(void); public: virtual ~CPacketSync(void); void OnRawData(byte* pData, size_t nDataLen); virtual void OnTsPacket(byte* tsPacket); void Reset(void); private: byte m_tempBuffer[200]; ssize_t m_tempBufferPos; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/PatParser.cpp000066400000000000000000000152051346756700600252420ustar00rootroot00000000000000/* * Copyright (C) 2006 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "os-dependent.h" #include #include "client.h" //KODI->Log #include #include "PatParser.h" #include "TSHeader.h" using namespace ADDON; namespace MPTV { CPatParser::CPatParser(void) { m_packetsToSkip = 0; m_packetsReceived = 0; m_pCallback = NULL; Reset(); SetPid(0); m_iState = Idle; } CPatParser::~CPatParser(void) { CleanUp(); } void CPatParser::SetCallBack(IPatParserCallback* callback) { m_pCallback = callback; } void CPatParser::CleanUp() { for (size_t i = 0; i < m_pmtParsers.size(); ++i) { CPmtParser* parser = m_pmtParsers[i]; delete parser; } m_pmtParsers.clear(); m_iPatTableVersion = -1; } void CPatParser::Reset() { // Dump(); KODI->Log(LOG_DEBUG, "PatParser:Reset()"); CSectionDecoder::Reset(); CleanUp(); m_packetsReceived = 0; m_iPatTableVersion = -1; m_iState = Parsing; } size_t CPatParser::Count() { size_t count = m_pmtParsers.size(); if (count == 0) return 0; for (size_t i = 0; i < m_pmtParsers.size(); ++i) { CPmtParser* parser = m_pmtParsers[i]; if (true == parser->IsReady()) { return count; } } return 0; } bool CPatParser::GetChannel(size_t index, CChannelInfo& info) { static CChannelInfo unknownChannel; if (index > Count()) { return false; } CPmtParser* parser = m_pmtParsers[index]; if (false == parser->IsReady()) { return false; } info.PidTable = parser->GetPidInfo(); m_iState = Idle; return true; } void CPatParser::SkipPacketsAtStart(int64_t packets) { m_packetsToSkip = packets; m_packetsReceived = 0; } void CPatParser::OnTsPacket(byte* tsPacket) { m_packetsReceived++; if (m_packetsReceived > m_packetsToSkip) { for (size_t i = 0; i < m_pmtParsers.size(); ++i) { CPmtParser* parser = m_pmtParsers[i]; parser->OnTsPacket(tsPacket); } CSectionDecoder::OnTsPacket(tsPacket); } if (m_iState == Parsing && m_pCallback != NULL) { for (size_t i = 0; i < m_pmtParsers.size(); ++i) { CPmtParser* parser = m_pmtParsers[i]; if (true == parser->IsReady()) { CChannelInfo info; if (GetChannel(i, info)) { m_iState = Idle; info.PatVersion = m_iPatTableVersion; m_pCallback->OnNewChannel(info); m_iState = Parsing; return; } } } } } void CPatParser::OnNewSection(CSection& section) { if (section.table_id != 0) return; try { //int section_syntax_indicator = (section.Data[1]>>7) & 1; //int transport_stream_id = section.table_id_extension; if (section.version_number != m_iPatTableVersion) { KODI->Log(LOG_DEBUG, "PatParser: new pat table %d->%d", m_iPatTableVersion, section.version_number); //was commented out CleanUp(); m_iPatTableVersion = section.version_number; m_iState = Parsing; } //KODI->Log(LOG_DEBUG, "DecodePat %d section:%d lastsection:%d sectionlen:%d", // version_number,section_number,last_section_number,section_length); int loop = (section.section_length - 9) / 4; for (int i = 0; i < loop; i++) { int offset = (8 + (i * 4)); int pmtPid = ((section.Data[offset + 2] & 0x1F) << 8) + section.Data[offset + 3]; if (pmtPid < 0x10 || pmtPid >= 0x1fff) { //invalid pmt pid return; } bool found = false; for (size_t idx = 0; idx < m_pmtParsers.size(); idx++) { CPmtParser* pmtParser = m_pmtParsers[idx]; if (pmtParser->GetPid() == pmtPid) { found = true; break; } } if (!found && pmtPid >= 0x10) { CPmtParser* pmtParser = new CPmtParser(); pmtParser->SetPid(pmtPid); //pmtParser->SetPmtCallBack(this); m_pmtParsers.push_back(pmtParser); KODI->Log(LOG_DEBUG, "PatParser: add pmt# %u pid: %x", (unsigned int) m_pmtParsers.size(), pmtPid); } } } catch (...) { KODI->Log(LOG_DEBUG, "Exception in PatParser"); } } void CPatParser::Dump() { for (size_t i = 0; i < Count(); ++i) { CChannelInfo info; if (GetChannel(i, info)) { KODI->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 { KODI->Log(LOG_DEBUG, "%u) not found", (unsigned int) i); } } } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/PatParser.h000066400000000000000000000036651346756700600247160ustar00rootroot00000000000000/* * Copyright (C) 2006 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once #include "SectionDecoder.h" #include "PmtParser.h" #include "ChannelInfo.h" #include namespace MPTV { class IPatParserCallback { public: virtual void OnNewChannel(CChannelInfo& info) = 0; }; class CPatParser : public CSectionDecoder { public: enum PatState { Idle, Parsing, }; CPatParser(void); virtual ~CPatParser(void); void SkipPacketsAtStart(int64_t packets); void OnTsPacket(byte* tsPacket); void Reset(); void OnNewSection(CSection& section); 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; PatState m_iState; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/PidTable.cpp000066400000000000000000000126331346756700600250270ustar00rootroot00000000000000/* * Copyright (C) 2006-2009 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "PidTable.h" #include "client.h" //KODI->Log #include "utils.h" using namespace ADDON; namespace MPTV { CPidTable::CPidTable(const CPidTable& pids) { Copy(pids); } CPidTable::CPidTable(void) { Reset(); } CPidTable::~CPidTable(void) { } bool CPidTable::operator ==(const CPidTable& other) const { // Not all members are compared, this is how DeMultiplexer class has // been comparing "PMTs" to detect channel changes. if (subtitlePids != other.subtitlePids || audioPids != other.audioPids || videoPids != other.videoPids || PcrPid != other.PcrPid || PmtPid != other.PmtPid) { return false; } else { return true; } } void CPidTable::Reset() { //KODI->Log(LOG_DEBUG, "Pid table reset"); PcrPid = 0; PmtPid = 0; ServiceId = -1; videoPids.clear(); audioPids.clear(); subtitlePids.clear(); TeletextPid = 0; // no reason to reset TeletextSubLang } CPidTable& CPidTable::operator = (const CPidTable &pids) { if (&pids == this) { return *this; } Copy(pids); return *this; } void CPidTable::Copy(const CPidTable &pids) { //KODI->Log(LOG_DEBUG, "Pid table copy"); ServiceId = pids.ServiceId; PcrPid = pids.PcrPid; PmtPid = pids.PmtPid; videoPids = pids.videoPids; audioPids = pids.audioPids; subtitlePids = pids.subtitlePids; TeletextPid = pids.TeletextPid; //TeletextInfo=pids.TeletextInfo; } bool CPidTable::HasTeletextPageInfo(int UNUSED(page)) { //MG: todo //std::vector::iterator vit = TeletextInfo.begin(); //while(vit != TeletextInfo.end()) //{ // is the page already registrered // TeletextServiceInfo& info = *vit; // if(info.page == page) // { // return true; // break; // } // else vit++; //} return false; } void CPidTable::LogPIDs() { KODI->Log(LOG_DEBUG, " pcr pid: %4x ", PcrPid); KODI->Log(LOG_DEBUG, " pmt pid: %4x ", PmtPid); // Log all video streams (Blu-ray can have multiple video streams) for (unsigned int i(0); i < videoPids.size(); i++) { KODI->Log(LOG_DEBUG, " video pid: %4x type: %s", videoPids[i].Pid, StreamFormatAsString(videoPids[i].VideoServiceType)); } // Log all audio streams for (unsigned int i(0); i < audioPids.size(); i++) { KODI->Log(LOG_DEBUG, " audio pid: %4x language: %3s type: %s", audioPids[i].Pid, audioPids[i].Lang, StreamFormatAsString(audioPids[i].AudioServiceType)); } // Log all subtitle streams for (unsigned int i(0); i < subtitlePids.size(); i++) { KODI->Log(LOG_DEBUG, " Subtitle pid: %4x language: %3s type: %s", subtitlePids[i].Pid, subtitlePids[i].Lang, StreamFormatAsString(subtitlePids[i].SubtitleServiceType)); } } const char* CPidTable::StreamFormatAsString(int streamType) { switch (streamType) { case 0x01: return "MPEG1"; case 0x02: return "MPEG2"; case 0x03: return "MPEG1 - audio"; case 0x04: return "MPEG2 - audio"; case 0x05: return "DVB subtitle 1"; case 0x06: return "DVB subtitle 2"; case 0x10: return "MPEG4"; case 0x1B: return "H264"; case 0xEA: return "VC1"; case 0x80: return "LPCM"; case 0x81: return "AC3"; case 0x82: return "DTS"; case 0x83: return "MLP"; case 0x84: return "DD+"; case 0x85: return "DTS-HD"; case 0x86: return "DTS-HD Master Audio"; case 0x0f: return "AAC"; case 0x11: return "LATM AAC"; case 0xA1: return "DD+"; case 0xA2: return "DTS-HD"; case 0x90: return "PGS"; case 0x91: return "IG"; case 0x92: return "Text"; default: return "Unknown"; } } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/PidTable.h000066400000000000000000000106401346756700600244700ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once #include "os-dependent.h" #include namespace MPTV { // This class used to store subtitle stream specific information class SubtitlePid { public: SubtitlePid() { Pid = -1; SubtitleServiceType = -1; Lang[0] = 'U'; Lang[1] = 'N'; Lang[2] = 'K'; Lang[3] = 0; } bool operator == (const SubtitlePid& other) const { if (Pid != other.Pid || Lang[0] != other.Lang[0] || Lang[1] != other.Lang[1] || Lang[2] != other.Lang[2] || Lang[3] != other.Lang[3]) { return false; } else { return true; } } short Pid; short SubtitleServiceType; byte Lang[4]; }; // This class used to store audio stream specific information class AudioPid { public: AudioPid() { Pid = -1; AudioServiceType = -1; Lang[0] = 'U'; Lang[1] = 'N'; Lang[2] = 'K'; Lang[3] = 0; Lang[4] = 0; Lang[5] = 0; Lang[6] = 0; } bool operator == (const AudioPid& other) const { if (Pid != other.Pid || Lang[0] != other.Lang[0] || Lang[1] != other.Lang[1] || Lang[2] != other.Lang[2] || Lang[3] != other.Lang[3] || Lang[4] != other.Lang[4] || Lang[5] != other.Lang[5] || Lang[6] != other.Lang[6] || AudioServiceType != other.AudioServiceType) { return false; } else { return true; } } short Pid; byte Lang[7]; short AudioServiceType; }; // This class used to store video stream specific information class VideoPid { public: VideoPid() { Pid = -1; VideoServiceType = -1; } bool operator == (const VideoPid& other) const { if (Pid != other.Pid || VideoServiceType != other.VideoServiceType) { return false; } else { return true; } } short Pid; int VideoServiceType; }; class TempPid { public: TempPid() { Pid = -1; Lang[0] = 'U'; Lang[1] = 'N'; Lang[2] = 'K'; Lang[3] = 0; Lang[4] = 0; Lang[5] = 0; Lang[6] = 0; } short Pid; byte Lang[7]; }; class CPidTable { public: CPidTable(); CPidTable(const CPidTable& pids); virtual ~CPidTable(); void Reset(); void LogPIDs(); const char* StreamFormatAsString(int streamType); bool HasTeletextPageInfo(int page); // do we have a TeletextServiceInfo entry for that page CPidTable& operator = (const CPidTable& pids); bool operator==(const CPidTable& other) const; void Copy(const CPidTable &pids); unsigned long PcrPid; unsigned long PmtPid; short TeletextPid; // which PID contains the teletext data int ServiceId; std::vector videoPids; std::vector audioPids; std::vector subtitlePids; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/PmtParser.cpp000066400000000000000000000403701346756700600252570ustar00rootroot00000000000000/* * Copyright (C) 2006 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "os-dependent.h" #include "client.h" // KODI->Log #include "PmtParser.h" #include "ChannelInfo.h" #include using namespace ADDON; namespace MPTV { CPmtParser::CPmtParser() { m_pmtCallback = NULL; m_isFound = false; } CPmtParser::~CPmtParser(void) { } void CPmtParser::SetPmtCallBack(IPmtCallBack* callback) { m_pmtCallback = callback; } bool CPmtParser::IsReady() { return m_isFound; } void CPmtParser::OnNewSection(CSection& section) { if (section.table_id != 2) { return; } try { bool lpcm_audio_found = false; int program_number = section.table_id_extension; int pcr_pid = ((section.Data[8] & 0x1F) << 8) + section.Data[9]; int program_info_length = ((section.Data[10] & 0xF) << 8) + section.Data[11]; int len2 = program_info_length; int pointer = 12; int len1 = section.section_length - (9 + program_info_length + 4); int x; if (!m_isFound) { KODI->Log(LOG_DEBUG, "got pmt:%x service id:%x", GetPid(), program_number); m_isFound = true; if (m_pmtCallback != NULL) { m_pmtCallback->OnPmtReceived(GetPid()); } } // loop 1 while (len2 > 0) { //int indicator=section.Data[pointer]; int descriptorLen = section.Data[pointer + 1]; len2 -= (descriptorLen + 2); pointer += (descriptorLen + 2); } // loop 2 int stream_type = 0; short elementary_PID = 0; short ES_info_length = 0; std::vector tempPids; m_pidInfo.Reset(); m_pidInfo.PmtPid = GetPid(); m_pidInfo.ServiceId = program_number; while (len1 > 0) { //if (start+pointer+4>=sectionLen+9) return ; stream_type = section.Data[pointer]; elementary_PID = ((section.Data[pointer + 1] & 0x1F) << 8) + section.Data[pointer + 2]; ES_info_length = ((section.Data[pointer + 3] & 0xF) << 8) + section.Data[pointer + 4]; KODI->Log(LOG_DEBUG, "pmt: pid:%x type:%x", elementary_PID, stream_type); if (stream_type == SERVICE_TYPE_VIDEO_MPEG1 || stream_type == SERVICE_TYPE_VIDEO_MPEG2 || stream_type == SERVICE_TYPE_VIDEO_MPEG4 || stream_type == SERVICE_TYPE_VIDEO_H264) { VideoPid pid; pid.Pid = elementary_PID; pid.VideoServiceType = stream_type; m_pidInfo.videoPids.push_back(pid); } if (stream_type == SERVICE_TYPE_AUDIO_MPEG1 || stream_type == SERVICE_TYPE_AUDIO_MPEG2 || stream_type == SERVICE_TYPE_AUDIO_AC3 || stream_type == SERVICE_TYPE_AUDIO_AAC || stream_type == SERVICE_TYPE_AUDIO_LATM_AAC || stream_type == SERVICE_TYPE_AUDIO_DD_PLUS) { AudioPid pid; pid.Pid = elementary_PID; pid.AudioServiceType = (short)stream_type; m_pidInfo.audioPids.push_back(pid); } m_pidInfo.PcrPid = pcr_pid; pointer += 5; len1 -= 5; len2 = ES_info_length; while (len2 > 0) { if (pointer + 1 >= section.section_length) { KODI->Log(LOG_DEBUG, "pmt parser check1"); return; } int indicator = section.Data[pointer]; x = section.Data[pointer + 1] + 2; if (indicator == DESCRIPTOR_DVB_AC3 || indicator == DESCRIPTOR_DVB_E_AC3) { AudioPid pid; pid.Pid = elementary_PID; pid.AudioServiceType = (indicator == DESCRIPTOR_DVB_AC3) ? SERVICE_TYPE_AUDIO_AC3 : SERVICE_TYPE_AUDIO_DD_PLUS; for (size_t i(0); i < tempPids.size(); i++) { if (tempPids[i].Pid == elementary_PID) { pid.Lang[0] = tempPids[i].Lang[0]; pid.Lang[1] = tempPids[i].Lang[1]; pid.Lang[2] = tempPids[i].Lang[2]; pid.Lang[3] = tempPids[i].Lang[3]; // should be null if no extra data is available pid.Lang[4] = tempPids[i].Lang[4]; pid.Lang[5] = tempPids[i].Lang[5]; tempPids.pop_back(); break; } } m_pidInfo.audioPids.push_back(pid); } // audio and subtitle languages if (indicator == DESCRIPTOR_MPEG_ISO639_Lang) { if (pointer + 4 >= section.section_length) { KODI->Log(LOG_DEBUG, "pmt parser check2"); return; } bool pidFound(false); // Find corresponding audio stream by PID, if not found // the stream type should be unknown to us for (unsigned int i(0); i < m_pidInfo.audioPids.size(); i++) { if (m_pidInfo.audioPids[i].Pid == elementary_PID) { int descriptorLen = section.Data[pointer + 1]; m_pidInfo.audioPids[i].Lang[0] = section.Data[pointer + 2]; m_pidInfo.audioPids[i].Lang[1] = section.Data[pointer + 3]; m_pidInfo.audioPids[i].Lang[2] = section.Data[pointer + 4]; m_pidInfo.audioPids[i].Lang[3] = 0; // Get the additional language descriptor data (NORSWE etc.) if (descriptorLen == 8) { m_pidInfo.audioPids[i].Lang[3] = section.Data[pointer + 6]; m_pidInfo.audioPids[i].Lang[4] = section.Data[pointer + 7]; m_pidInfo.audioPids[i].Lang[5] = section.Data[pointer + 8]; m_pidInfo.audioPids[i].Lang[6] = 0; } pidFound = true; } // Find corresponding subtitle stream by PID, if not found // the stream type is be unknown to us for (unsigned int j(0); j < m_pidInfo.subtitlePids.size(); j++) { if (m_pidInfo.subtitlePids[j].Pid == elementary_PID) { m_pidInfo.subtitlePids[j].Lang[0] = section.Data[pointer + 2]; m_pidInfo.subtitlePids[j].Lang[1] = section.Data[pointer + 3]; m_pidInfo.subtitlePids[j].Lang[2] = section.Data[pointer + 4]; m_pidInfo.subtitlePids[j].Lang[3] = 0; pidFound = true; } } if (!pidFound) { int descriptorLen = section.Data[pointer + 1]; TempPid pid; pid.Pid = elementary_PID; pid.Lang[0] = section.Data[pointer + 2]; pid.Lang[1] = section.Data[pointer + 3]; pid.Lang[2] = section.Data[pointer + 4]; // Get the additional language descriptor data (NORSWE etc.) if (descriptorLen == 8) { pid.Lang[3] = section.Data[pointer + 6]; pid.Lang[4] = section.Data[pointer + 7]; pid.Lang[5] = section.Data[pointer + 8]; } else { pid.Lang[3] = 0; pid.Lang[4] = 0; pid.Lang[5] = 0; } tempPids.push_back(pid); } } } if (indicator == DESCRIPTOR_VBI_TELETEXT) { KODI->Log(LOG_DEBUG, "VBI teletext descriptor"); } if (indicator == DESCRIPTOR_DVB_TELETEXT /*&& m_pidInfo.TeletextPid==0*/) { m_pidInfo.TeletextPid = elementary_PID; assert(section.Data[pointer + 0] == DESCRIPTOR_DVB_TELETEXT); int descriptorLen = section.Data[pointer + 1]; int varBytes = 5; // 4 additional fields for a total of 32 bits (see 6.2.40) assert(descriptorLen % varBytes == 0); // there shouldnt be any left over bytes :) int N = descriptorLen / varBytes; //BYTE b = 0x02 << 3; KODI->Log(LOG_DEBUG, "Descriptor length %i, N= %i", descriptorLen, N); for (int j = 0; j < N; j++) { //BYTE ISO_639_language_code[3]; //ISO_639_language_code[0] = section.Data[pointer + varBytes*j + 2]; //ISO_639_language_code[1] = section.Data[pointer + varBytes*j + 3]; //ISO_639_language_code[2] = section.Data[pointer + varBytes*j + 4]; byte b3 = section.Data[pointer + varBytes*j + 5]; byte teletext_type = (b3 & 0xF8) >> 3; // 5 first(msb) bits assert(teletext_type <= 0x05); // 0x06 and upwards reserved for future use and shouldnt appear //for (int i = 0; i < 8; i++){ // if( ((b3 << i) & 128) != 0) LogDebug("1"); // else LogDebug("0"); //} int teletext_magazine_number = (b3 & 0x07); // last(lsb) 3 bits int teletext_page_number = (section.Data[pointer + varBytes*j + 6]); int real_page_tens = (teletext_page_number & 0xF0) >> 4; int real_page_units = teletext_page_number & 0x0F; int real_page = teletext_magazine_number * 100 + real_page_tens * 10 + real_page_units; //KODI->Log(LOG_DEBUG, "Mag: %i, tens %i, units %i, total ?= %i", teletext_magazine_number,real_page_tens,real_page_units,real_page); //if its a teletext subtitle service (standard / hard of hearing respectively) if (teletext_type == 0x02 || teletext_type == 0x05) { if (!m_pidInfo.HasTeletextPageInfo(real_page)) { KODI->Log(LOG_DEBUG, "TODO: Teletext subtitles in PMT: PID %i, mag %i, page %i", elementary_PID, teletext_magazine_number, real_page); //KODI->Log(LOG_DEBUG, "Teletext subtitles in PMT: PID %i, mag %i, page %i, prevPage %i", elementary_PID, teletext_magazine_number, real_page, m_pidInfo.TeletextSubPage); //TeletextServiceInfo info; //info.page = real_page; //info.type = teletext_type; //info.lang[0] = ISO_639_language_code[0]; //info.lang[1] = ISO_639_language_code[1]; //info.lang[2] = ISO_639_language_code[2]; //m_pidInfo.TeletextInfo.push_back(info); } } else { KODI->Log(LOG_DEBUG, "Teletext SI: Page %i Type %X", real_page, teletext_type); } } } if (indicator == DESCRIPTOR_DVB_SUBTITLING) { if (stream_type == SERVICE_TYPE_DVB_SUBTITLES2) { SubtitlePid pid; pid.Pid = elementary_PID; pid.Lang[0] = section.Data[pointer + 2]; pid.Lang[1] = section.Data[pointer + 3]; pid.Lang[2] = section.Data[pointer + 4]; pid.Lang[3] = 0; pid.SubtitleServiceType = SERVICE_TYPE_DVB_SUBTITLES2; m_pidInfo.subtitlePids.push_back(pid); } } if (indicator == DESCRIPTOR_REGISTRATION) { if (section.Data[pointer + 2] == 'H' && section.Data[pointer + 3] == 'D' && section.Data[pointer + 4] == 'M' && section.Data[pointer + 5] == 'V' && stream_type == SERVICE_TYPE_DCII_OR_LPCM) { AudioPid pid; pid.Pid = elementary_PID; pid.AudioServiceType = (short)stream_type; m_pidInfo.audioPids.push_back(pid); lpcm_audio_found = true; } } len2 -= x; len1 -= x; pointer += x; } if (stream_type == SERVICE_TYPE_DCII_OR_LPCM && !lpcm_audio_found) { VideoPid pid; pid.Pid = elementary_PID; pid.VideoServiceType = SERVICE_TYPE_VIDEO_MPEG2; m_pidInfo.videoPids.push_back(pid); } } if (m_pmtCallback != NULL) { KODI->Log(LOG_DEBUG, "DecodePMT pid:0x%x pcrpid:0x%x sid:%x", m_pidInfo.PmtPid, m_pidInfo.PcrPid, m_pidInfo.ServiceId); m_pmtCallback->OnPidsReceived(m_pidInfo); } } catch (...) { KODI->Log(LOG_DEBUG, "Exception in PmtParser"); } } CPidTable& CPmtParser::GetPidInfo() { return m_pidInfo; } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/PmtParser.h000066400000000000000000000050721346756700600247240ustar00rootroot00000000000000/* * Copyright (C) 2006 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once #include "SectionDecoder.h" #include "TSHeader.h" #include "PidTable.h" #include #define SERVICE_TYPE_VIDEO_UNKNOWN -1 #define SERVICE_TYPE_VIDEO_MPEG1 0x01 #define SERVICE_TYPE_VIDEO_MPEG2 0x02 #define SERVICE_TYPE_DCII_OR_LPCM 0x80 // can be DC-II MPEG2 Video OR LPCM Audio if registration descriptor=HDMV #define SERVICE_TYPE_VIDEO_MPEG4 0x10 #define SERVICE_TYPE_VIDEO_H264 0x1b #define SERVICE_TYPE_AUDIO_UNKNOWN -1 #define SERVICE_TYPE_AUDIO_MPEG1 0x03 #define SERVICE_TYPE_AUDIO_MPEG2 0x04 #define SERVICE_TYPE_AUDIO_AC3 0x81 //fake #define SERVICE_TYPE_AUDIO_AAC 0x0f #define SERVICE_TYPE_AUDIO_LATM_AAC 0x11 //LATM AAC audio #define SERVICE_TYPE_AUDIO_DD_PLUS 0x84 #define SERVICE_TYPE_DVB_SUBTITLES1 0x05 #define SERVICE_TYPE_DVB_SUBTITLES2 0x06 #define DESCRIPTOR_REGISTRATION 0x05 #define DESCRIPTOR_VBI_TELETEXT 0x46 #define DESCRIPTOR_DVB_AC3 0x6a #define DESCRIPTOR_DVB_E_AC3 0x7a #define DESCRIPTOR_DVB_TELETEXT 0x56 #define DESCRIPTOR_DVB_SUBTITLING 0x59 #define DESCRIPTOR_MPEG_ISO639_Lang 0x0a namespace MPTV { class IPmtCallBack { public: virtual void OnPmtReceived(int pmtPid) = 0; virtual void OnPidsReceived(const CPidTable& info) = 0; }; class CPmtParser : public CSectionDecoder { public: CPmtParser(void); virtual ~CPmtParser(void); void OnNewSection(CSection& section); void SetPmtCallBack(IPmtCallBack* callback); bool IsReady(); CPidTable& GetPidInfo(); private: bool m_isFound; IPmtCallBack* m_pmtCallback; CTsHeader m_tsHeader; CPidTable m_pidInfo; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/Section.cpp000066400000000000000000000064621346756700600247520ustar00rootroot00000000000000/* * Copyright (C) 2006-2010 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "os-dependent.h" #include "Section.h" namespace MPTV { CSection::CSection(void) { Reset(); } CSection::~CSection(void) { } void CSection::Reset() { table_id = -1; table_id_extension = -1; section_length = -1; section_number = -1; version_number = -1; section_syntax_indicator = -1; BufferPos = 0; } CSection& CSection::operator = (const CSection §ion) { if (§ion == this) { return *this; } Copy(section); return *this; } void CSection::Copy(const CSection §ion) { table_id = section.table_id; table_id_extension = section.table_id_extension; section_length = section.section_length; section_number = section.section_number; version_number = section.version_number; section_syntax_indicator = section.section_syntax_indicator; memcpy(Data, section.Data, sizeof(Data)); BufferPos = 0; } int CSection::CalcSectionLength(byte* tsPacket, int start) { if (start >= 188) return 0; if (BufferPos < 3) { byte bHi = 0; byte bLow = 0; if (BufferPos == 1) { bHi = tsPacket[start]; bLow = tsPacket[start + 1]; } else if (BufferPos == 2) { bHi = Data[1]; bLow = tsPacket[start]; } section_length = (int)(((bHi & 0xF) << 8) + bLow); } else section_length = (int)(((Data[1] & 0xF) << 8) + Data[2]); return section_length; } bool CSection::DecodeHeader() { if (BufferPos < 8) return false; table_id = Data[0]; section_syntax_indicator = ((Data[1] >> 7) & 1); if (section_length == -1) section_length = (((Data[1] & 0xF) << 8) + Data[2]); table_id_extension = ((Data[3] << 8) + Data[4]); version_number = ((Data[5] >> 1) & 0x1F); section_number = Data[6]; section_syntax_indicator = ((Data[1] >> 7) & 1); return true; } bool CSection::SectionComplete() { if (!DecodeHeader() && BufferPos - 3 > section_length && section_length > 0) return true; if (!DecodeHeader()) return false; return (BufferPos - 3 >= section_length); } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/Section.h000066400000000000000000000027751346756700600244220ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once #define MAX_SECTION_LENGTH 4300 namespace MPTV { class CSection { public: CSection(void); virtual ~CSection(void); void Reset(); bool DecodeHeader(); int CalcSectionLength(byte* tsPacket, int start); bool SectionComplete(); CSection& operator = (const CSection& section); void Copy(const CSection §ion); int table_id; int table_id_extension; int section_length; int section_number; int version_number; int section_syntax_indicator; int BufferPos; byte Data[MAX_SECTION_LENGTH * 5]; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/SectionDecoder.cpp000066400000000000000000000173001346756700600262310ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "os-dependent.h" #include "client.h" //KODI->Log #include #include #include "SectionDecoder.h" #include "TSHeader.h" #include "utils.h" using namespace ADDON; namespace MPTV { CSectionDecoder::CSectionDecoder(void) { m_pid = -1; m_iContinuityCounter = 0; m_section.Reset(); m_pCallback = NULL; m_bLog = false; m_bCrcCheck = true; } CSectionDecoder::~CSectionDecoder(void) { } void CSectionDecoder::EnableLogging(bool onOff) { m_bLog = onOff; } void CSectionDecoder::SetCallBack(ISectionCallback* callback) { m_pCallback = callback; } void CSectionDecoder::SetPid(int pid) { m_pid = pid; } int CSectionDecoder::GetPid() { return m_pid; } void CSectionDecoder::Reset() { m_section.Reset(); } void CSectionDecoder::EnableCrcCheck(bool onOff) { m_bCrcCheck = onOff; } void CSectionDecoder::OnTsPacket(byte* tsPacket) { if (m_pid < 0) return; if (tsPacket == NULL) return; m_header.Decode(tsPacket); OnTsPacket(m_header, tsPacket); } int CSectionDecoder::StartNewSection(byte* tsPacket, int index, int sectionLen) { int newstart = -1; int len = -1; if (sectionLen > -1) { if (index + sectionLen < 185) { len = sectionLen + 3; newstart = index + sectionLen + 3; } else { newstart = 188; len = 188 - index; } } else { newstart = 188; len = 188 - index; } m_section.Reset(); memcpy(m_section.Data, &tsPacket[index], len); m_section.BufferPos = len; m_section.DecodeHeader(); return newstart; } int CSectionDecoder::AppendSection(byte* tsPacket, int index, int sectionLen) { int newstart = -1; int len = -1; if (index + sectionLen < 185) { len = sectionLen + 3; newstart = index + sectionLen + 3; } else { newstart = 188; len = 188 - index; } memcpy(&m_section.Data[m_section.BufferPos], &tsPacket[index], len); m_section.BufferPos += len; return newstart; } int CSectionDecoder::SnapshotSectionLength(byte* tsPacket, int start) { if (start >= 184) return -1; return (int)(((tsPacket[start + 1] & 0xF) << 8) + tsPacket[start + 2]); } void CSectionDecoder::OnTsPacket(CTsHeader& header, byte* tsPacket) { try { if (header.TransportError) { m_section.Reset(); // Will force us to wait for new PayloadUnitStart return; } if (m_pid >= 0x1fff) return; if (header.Pid != m_pid) return; if (!header.HasPayload) return; int start = header.PayLoadStart; int pointer_field = 0; if (header.PayloadUnitStart) { if (start >= 188) return; pointer_field = start + tsPacket[start] + 1; if (m_section.BufferPos == 0) start += tsPacket[start] + 1; else start++; } int numloops = 0; while (start < 188) { numloops++; if (m_section.BufferPos == 0) { if (!header.PayloadUnitStart) return; if (tsPacket[start] == 0xFF) return; int section_length = SnapshotSectionLength(tsPacket, start); start = StartNewSection(tsPacket, start, section_length); } else { if (m_section.section_length == -1) m_section.CalcSectionLength(tsPacket, start); if (m_section.section_length == 0) { if (m_bLog) KODI->Log(LOG_DEBUG, "!!! CSectionDecoder::OnTsPacket got a section with section length: 0 on pid: 0x%X tableid: 0x%X bufferpos: %d start: %d - Discarding whole packet.", header.Pid, m_section.Data[0], m_section.BufferPos, start); m_section.Reset(); return; } int len = m_section.section_length - m_section.BufferPos; if (pointer_field != 0 && ((start + len) > pointer_field)) { // We have an incomplete section here len = pointer_field - start; start = AppendSection(tsPacket, start, len); m_section.section_length = m_section.BufferPos - 1; start = pointer_field; } else start = AppendSection(tsPacket, start, len); } if (m_section.SectionComplete() && m_section.section_length > 0) { uint32_t crc = 0; // Only long syntax (section_syntax_indicator == 1) has a CRC // Short syntax may have CRC e.g. TOT, but that is part of the specific section if (m_section.section_syntax_indicator == 1) crc = crc32((char*)m_section.Data, m_section.section_length + 3); if (crc == 0 || (m_bCrcCheck == false)) { OnNewSection(m_section); if (m_pCallback != NULL) m_pCallback->OnNewSection(header.Pid, m_section.table_id, m_section); } else { // If the section is complete and the CRC fails, then this section is crap! m_section.Reset(); return; } m_section.Reset(); } pointer_field = 0; if (numloops > 100) { KODI->Log(LOG_DEBUG, "!!! CSectionDecoder::OnTsPacket Entered infinite loop. pid: %X start: %d BufferPos: %d SectionLength: %d - Discarding section and moving to next packet", header.Pid, start, m_section.BufferPos, m_section.section_length); m_section.Reset(); return; } } } catch (...) { KODI->Log(LOG_DEBUG, "exception in CSectionDecoder::OnTsPacket"); } } void CSectionDecoder::OnNewSection(CSection& UNUSED(section)) { } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/SectionDecoder.h000066400000000000000000000040201346756700600256710ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once #include "ISectionCallback.h" #include "DvbUtil.h" #include "Section.h" #include "TSHeader.h" #define MAX_SECTIONS 256 namespace MPTV { class CSectionDecoder : public CDvbUtil { public: CSectionDecoder(void); ~CSectionDecoder(void); void SetCallBack(ISectionCallback* callback); void OnTsPacket(byte* tsPacket); void OnTsPacket(CTsHeader& header, byte* tsPacket); void SetPid(int pid); int GetPid(); void Reset(); void EnableLogging(bool onOff); void EnableCrcCheck(bool onOff); virtual void OnNewSection(CSection& section); protected: private: int StartNewSection(byte* tsPacket, int index, int sectionLen); int AppendSection(byte* tsPacket, int index, int sectionLen); int SnapshotSectionLength(byte* tsPacket, int start); bool m_bLog; bool m_bCrcCheck; int m_pid; CSection m_section; int m_iContinuityCounter; ISectionCallback* m_pCallback; CTsHeader m_header; CTsHeader m_headerSection; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/TSDebug.h000066400000000000000000000015551346756700600243060ustar00rootroot00000000000000/* * Copyright (C) 2005-2013 Team Kodi * https://kodi.tv * * 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 . * */ #pragma once #ifdef TSREADER_DEBUG #define TSDEBUG KODI->Log #else #ifdef _MSC_VER #define TSDEBUG #else #define TSDEBUG(...) #endif #endif pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/TSHeader.cpp000066400000000000000000000122521346756700600247770ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #include "os-dependent.h" #include "client.h" //KODI->Log #include "TSHeader.h" #define PAYLOADONLY 1 #define ADAPTIONFIELDONLY 2 #define ADAPTIONFIELDANDPAYLOAD 3 using namespace ADDON; namespace MPTV { CTsHeader::CTsHeader() : SyncByte(0), TransportError(false), PayloadUnitStart(false), TransportPriority(false), Pid(0), TScrambling(0), AdaptionControl(0), ContinuityCounter(0), AdaptionFieldLength(0), PayLoadStart(0), HasAdaptionField(false), HasPayload(false), m_packet(NULL) { } CTsHeader::CTsHeader(byte* tsPacket) { Decode(tsPacket); } CTsHeader::~CTsHeader(void) { } bool CTsHeader::PayLoadOnly() { return (AdaptionControl == 1); } bool CTsHeader::AdaptionFieldOnly() { return (AdaptionControl == 2); } bool CTsHeader::AdaptionFieldAndPayLoad() { return (AdaptionControl == 3); } void CTsHeader::Decode(byte *data) { m_packet = data; //47 40 d2 10 // bits byteNo mask //SyncByte : 8 0 0xff 11111111 //TransportError : 1 1 0x80 10000000 //PayloadUnitStart : 1 1 0x40 01000000 //TransportPriority : 1 1 0x20 00100000 //Pid : 13 1&2 00011111 11111111 //Transport Scrambling Control : 2 3 0xc0 11000000 //Adaption Field Control : 2 3 0x30 00110000 //ContinuityCounter : 4 3 0xf 00001111 //Two adaption field control bits which may take four values: // 1. 01 no adaptation field, payload only 0x10 1 // 2. 10 adaptation field only, no payload 0x20 2 // 3. 11 adaptation field followed by payload 0x30 3 // 4. 00 - RESERVED for future use 0x00 SyncByte = data[0]; if (SyncByte != 0x47) { TransportError = true; return; } TransportError = (data[1] & 0x80) > 0 ? true : false; PayloadUnitStart = (data[1] & 0x40) > 0 ? true : false; TransportPriority = (data[1] & 0x20) > 0 ? true : false; Pid = ((data[1] & 0x1F) << 8) + data[2]; TScrambling = data[3] & 0x80; AdaptionControl = (data[3] >> 4) & 0x3; HasAdaptionField = ((data[3] & 0x20) == 0x20); HasPayload = ((data[3] & 0x10) == 0x10); ContinuityCounter = data[3] & 0x0F; AdaptionFieldLength = 0; PayLoadStart = 4; if (HasAdaptionField) { AdaptionFieldLength = data[4]; // Only set payload start if it starts in this packet if ((5 + AdaptionFieldLength) < 188) PayLoadStart = 5 + AdaptionFieldLength; } if (PayloadUnitStart && !HasPayload) PayloadUnitStart = false; } void CTsHeader::LogHeader() { KODI->Log(LOG_DEBUG, "tsheader:%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x", m_packet[0], m_packet[1], m_packet[2], m_packet[3], m_packet[4], m_packet[5], m_packet[6], m_packet[7], m_packet[8], m_packet[9]); KODI->Log(LOG_DEBUG, " SyncByte :%x", SyncByte); KODI->Log(LOG_DEBUG, " TransportError :%x", TransportError); KODI->Log(LOG_DEBUG, " PayloadUnitStart :%d", PayloadUnitStart); KODI->Log(LOG_DEBUG, " TransportPriority :%x", TransportPriority); KODI->Log(LOG_DEBUG, " Pid :%x", Pid); KODI->Log(LOG_DEBUG, " TScrambling :%x", TScrambling); KODI->Log(LOG_DEBUG, " AdaptionControl :%x", AdaptionControl); KODI->Log(LOG_DEBUG, " ContinuityCounter :%x", ContinuityCounter); KODI->Log(LOG_DEBUG, " AdaptionFieldLength:%d", AdaptionFieldLength); KODI->Log(LOG_DEBUG, " PayLoadStart :%d", PayLoadStart); KODI->Log(LOG_DEBUG, " PayLoadOnly :%d", PayLoadOnly()); KODI->Log(LOG_DEBUG, " AdaptionFieldOnly :%d", AdaptionFieldOnly()); KODI->Log(LOG_DEBUG, " AdaptionFieldAndPayLoad:%d", AdaptionFieldAndPayLoad()); } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/TSHeader.h000066400000000000000000000030631346756700600244440ustar00rootroot00000000000000/* * Copyright (C) 2006-2008 Team MediaPortal * http://www.team-mediaportal.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #pragma once namespace MPTV { class CTsHeader { public: CTsHeader(); CTsHeader(byte* tsPacket); virtual ~CTsHeader(void); void Decode(byte *data); void LogHeader(); bool PayLoadOnly(); bool AdaptionFieldOnly(); bool AdaptionFieldAndPayLoad(); byte SyncByte; bool TransportError; bool PayloadUnitStart; bool TransportPriority; unsigned short Pid; byte TScrambling; byte AdaptionControl; byte ContinuityCounter; byte AdaptionFieldLength; byte PayLoadStart; bool HasAdaptionField; bool HasPayload; private: byte* m_packet; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/TSReader.cpp000066400000000000000000000415011346756700600250100ustar00rootroot00000000000000/* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 . * ************************************************************************* * Parts of this file originate 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 *************************************************************************/ #include "TSReader.h" #include "client.h" //for KODI->Log #include "MultiFileReader.h" #include "utils.h" #include "TSDebug.h" #include "p8-platform/util/timeutils.h" #include "p8-platform/util/StringUtils.h" #ifdef LIVE555 #include "MemoryReader.h" #include "MepoRTSPClient.h" #include "MemoryBuffer.h" #endif #include "FileUtils.h" using namespace std; using namespace ADDON; namespace MPTV { CTsReader::CTsReader() : m_demultiplexer(*this), m_fileName(""), m_startTickCount(), m_startTime(0) { m_fileReader = NULL; m_fileDuration = NULL; m_bLiveTv = false; m_bTimeShifting = false; m_bIsRTSP = false; m_cardSettings = NULL; m_cardId = -1; m_State = State_Stopped; m_lastPause = 0; m_WaitForSeekToEof = 0; m_bRecording = false; #ifdef LIVE555 m_rtspClient = NULL; m_buffer = NULL; #endif } CTsReader::~CTsReader(void) { SAFE_DELETE(m_fileReader); #ifdef LIVE555 SAFE_DELETE(m_buffer); SAFE_DELETE(m_rtspClient); #endif } std::string CTsReader::TranslatePath(const char* pszFileName) { std::string sFileName = pszFileName; #if defined (TARGET_WINDOWS_DESKTOP) // Can we access the given file already? if (OS::CFile::Exists(pszFileName)) { KODI->Log(LOG_DEBUG, "Found the timeshift buffer at: %s\n", pszFileName); return ToKodiPath(sFileName); } KODI->Log(LOG_NOTICE, "Cannot access '%s' directly. Assuming multiseat mode. Need to translate to UNC filename.", pszFileName); #elif defined (TARGET_WINDOWS_STORE) KODI->Log(LOG_DEBUG, "WindowsStore: need to translate '%s' to UNC filename.", pszFileName); #else KODI->Log(LOG_DEBUG, "Multiseat mode; need to translate '%s' to UNC filename.", pszFileName); #endif bool bFound = false; // Card Id given? (only for Live TV / Radio). Check for an UNC path (e.g. \\tvserver\timeshift) if (m_cardId >= 0) { Card tscard; if ((m_cardSettings) && (m_cardSettings->GetCard(m_cardId, tscard))) { if (!tscard.TimeshiftFolderUNC.empty()) { StringUtils::Replace(sFileName, tscard.TimeshiftFolder.c_str(), tscard.TimeshiftFolderUNC.c_str()); bFound = true; } else { KODI->Log(LOG_ERROR, "No timeshift share known for card %i '%s'. Check your TVServerKodi settings!", tscard.IdCard, tscard.Name.c_str()); } } } else { // No Card Id given. This is a recording. Check for an UNC path (e.g. \\tvserver\recordings) size_t found = string::npos; if ((m_cardSettings) && (m_cardSettings->size() > 0)) { for (CCards::iterator it = m_cardSettings->begin(); it < m_cardSettings->end(); ++it) { // Determine whether the first part of the recording filename is shared with this card found = sFileName.find(it->RecordingFolder); if (found != string::npos) { if (!it->RecordingFolderUNC.empty()) { // Remove the original base path and replace it with the given path StringUtils::Replace(sFileName, it->RecordingFolder.c_str(), it->RecordingFolderUNC.c_str()); bFound = true; break; } } } } } sFileName = ToKodiPath(sFileName); if (bFound) { KODI->Log(LOG_NOTICE, "Translate path %s -> %s", pszFileName, sFileName.c_str()); } else { KODI->Log(LOG_ERROR, "Could not find a network share for '%s'. Check your TVServerKodi settings!", pszFileName); if (!KODI->FileExists(pszFileName, false)) { KODI->Log(LOG_ERROR, "Cannot access '%s'", pszFileName); KODI->QueueNotification(QUEUE_ERROR, "Cannot access: %s", pszFileName); sFileName.clear(); return sFileName; } } #if defined (TARGET_WINDOWS_DESKTOP) // Can we now access the given file? long errCode; if (!OS::CFile::Exists(sFileName, &errCode)) { switch (errCode) { case ERROR_FILE_NOT_FOUND: KODI->Log(LOG_ERROR, "File not found: %s.\n", sFileName.c_str()); break; case ERROR_ACCESS_DENIED: { char strUserName[256]; DWORD lLength = 256; if (GetUserNameA(strUserName, &lLength)) { KODI->Log(LOG_ERROR, "Access denied on %s. Check share access rights for user '%s' or connect as a different user using the Explorer.\n", sFileName.c_str(), strUserName); } else { KODI->Log(LOG_ERROR, "Access denied on %s. Check share access rights.\n", sFileName.c_str()); } KODI->QueueNotification(QUEUE_ERROR, "Access denied: %s", sFileName.c_str()); break; } default: KODI->Log(LOG_ERROR, "Cannot find or access file: %s. Check share access rights.", sFileName.c_str()); } sFileName.clear(); } #elif defined TARGET_WINDOWS_STORE if (!KODI->FileExists(sFileName.c_str(), false)) { KODI->Log(LOG_ERROR, "Cannot find or access file: %s. Did you enable the vfs.smb2 plugin?\n", sFileName.c_str()); } #endif return sFileName; } long CTsReader::Open(const char* pszFileName) { KODI->Log(LOG_NOTICE, "TsReader open '%s'", pszFileName); m_fileName = pszFileName; if (m_State != State_Stopped) Close(); // check file type size_t length = m_fileName.length(); if ((length > 7) && (strnicmp(m_fileName.c_str(), "rtsp://", 7) == 0)) { // rtsp:// stream // open stream KODI->Log(LOG_DEBUG, "open rtsp: %s", m_fileName.c_str()); #ifdef LIVE555 //strcpy(m_rtspClient.m_outFileName, "e:\\temp\\rtsptest.ts"); delete m_buffer; m_buffer = new CMemoryBuffer(); delete m_rtspClient; m_rtspClient = new CRTSPClient(); m_rtspClient->Initialize(m_buffer); if ( !m_rtspClient->OpenStream(m_fileName.c_str()) ) { SAFE_DELETE(m_rtspClient); SAFE_DELETE(m_buffer); return E_FAIL; } m_bIsRTSP = true; m_bTimeShifting = true; m_bLiveTv = true; // are we playing a recording via RTSP if (m_fileName.find_first_of("/stream") == string::npos ) { // yes, then we're not timeshifting m_bTimeShifting = false; m_bLiveTv = false; } // play m_rtspClient->Play(0.0,0.0); delete m_fileReader; m_fileReader = new CMemoryReader(*m_buffer); m_State = State_Running; #else KODI->Log(LOG_ERROR, "Failed to open %s. PVR client is compiled without LIVE555 RTSP support.", m_fileName.c_str()); KODI->QueueNotification(QUEUE_ERROR, "PVR client has no RTSP support: %s", m_fileName.c_str()); return E_FAIL; #endif //LIVE555 } else { if ((length < 9) || (strnicmp(&m_fileName.c_str()[length - 9], ".tsbuffer", 9) != 0)) { // local .ts file m_bTimeShifting = false; m_bLiveTv = false; m_bIsRTSP = false; m_fileReader = new FileReader(); } else { // local timeshift buffer file file m_bTimeShifting = true; m_bLiveTv = true; m_bIsRTSP = false; m_fileReader = new MultiFileReader(); } // Translate path (e.g. Local filepath to smb://user:pass@share) m_fileName = TranslatePath(m_fileName.c_str()); if (m_fileName.empty()) return S_FALSE; // open file long retval = m_fileReader->OpenFile(m_fileName); if (retval != S_OK) { KODI->Log(LOG_ERROR, "Failed to open file '%s' as '%s'", pszFileName, m_fileName.c_str()); return retval; } // detect audio/video pids m_demultiplexer.SetFileReader(m_fileReader); m_demultiplexer.Start(); m_fileReader->SetFilePointer(0LL, FILE_BEGIN); m_State = State_Running; time(&m_startTime); m_startTickCount = GetTickCount64(); } return S_OK; } long CTsReader::Read(unsigned char* pbData, size_t lDataLength, size_t *dwReadBytes) { if (m_fileReader) { return m_fileReader->Read(pbData, lDataLength, dwReadBytes); } *dwReadBytes = 0; return S_FALSE; } void CTsReader::Close() { if (m_fileReader) { if (m_bIsRTSP) { #ifdef LIVE555 KODI->Log(LOG_NOTICE, "TsReader: closing RTSP client"); m_rtspClient->Stop(); SAFE_DELETE(m_rtspClient); SAFE_DELETE(m_buffer); #endif } else { KODI->Log(LOG_NOTICE, "TsReader: closing file"); m_fileReader->CloseFile(); } SAFE_DELETE(m_fileReader); m_State = State_Stopped; } } bool CTsReader::OnZap(const char* pszFileName, int64_t timeShiftBufferPos, long timeshiftBufferID) { string newFileName; KODI->Log(LOG_NOTICE, "TsReader: OnZap(%s)", pszFileName); // Check whether the new channel url/timeshift buffer is changed // In case of a new url/timeshift buffer file, close the old one first newFileName = TranslatePath(pszFileName); if (newFileName != m_fileName) { Close(); return (S_OK == Open(pszFileName)); } else { if (m_fileReader) { KODI->Log(LOG_DEBUG, "%s: request new PAT", __FUNCTION__); int64_t pos_before, pos_after; MultiFileReader* fileReader = dynamic_cast(m_fileReader); if (!fileReader) { return false; } pos_before = fileReader->GetFilePointer(); if ((timeShiftBufferPos > 0) && (timeshiftBufferID != -1)) { pos_after = fileReader->SetCurrentFilePointer(timeShiftBufferPos, timeshiftBufferID); } else { if (timeShiftBufferPos < 0) { pos_after = m_fileReader->SetFilePointer(0LL, FILE_BEGIN); } else { pos_after = m_fileReader->SetFilePointer(0LL, FILE_END); if ((timeShiftBufferPos > 0) && (pos_after > timeShiftBufferPos)) { /* Move backward */ pos_after = fileReader->SetFilePointer((timeShiftBufferPos - pos_after), FILE_CURRENT); } } } m_demultiplexer.RequestNewPat(); fileReader->OnChannelChange(); KODI->Log(LOG_DEBUG, "%s:: move from %I64d to %I64d tsbufpos %I64d", __FUNCTION__, pos_before, pos_after, timeShiftBufferPos); usleep(100000); // Set the stream start times to this new channel time(&m_startTime); m_startTickCount = GetTickCount64(); return true; } return false; } } void CTsReader::SetCardSettings(CCards* cardSettings) { m_cardSettings = cardSettings; } void CTsReader::SetDirectory(string& directory) { std::string tmp = directory; #ifdef TARGET_WINDOWS_DESKTOP if (tmp.find("smb://") != string::npos) { // Convert XBMC smb share name back to a real windows network share... StringUtils::Replace(tmp, "smb://", "\\\\"); StringUtils::Replace(tmp, "/", "\\"); } #else //TODO: do something useful... #endif m_basePath = tmp; } void CTsReader::SetCardId(int id) { m_cardId = id; } bool CTsReader::IsTimeShifting() { return m_bTimeShifting; } long CTsReader::Pause(bool UNUSED(bPaused)) { KODI->Log(LOG_DEBUG, "TsReader: Pause - IsTimeShifting = %d - state = %d", IsTimeShifting(), m_State); if (m_State == State_Running) { m_lastPause = GetTickCount64(); #ifdef LIVE555 // Are we using rtsp? if (m_bIsRTSP) { KODI->Log(LOG_DEBUG, "CTsReader::Pause() ->pause rtsp"); // at position: %f", (m_seekTime.Millisecs() / 1000.0f)); m_rtspClient->Pause(); } #endif //LIVE555 m_State = State_Paused; } else if (m_State == State_Paused) { #ifdef LIVE555 // Are we using rtsp? if (m_bIsRTSP) { KODI->Log(LOG_DEBUG, "CTsReader::Pause() is paused, continue rtsp"); // at position: %f", (m_seekTime.Millisecs() / 1000.0f)); m_rtspClient->Continue(); KODI->Log(LOG_DEBUG, "CTsReader::Pause() rtsp running"); // at position: %f", (m_seekTime.Millisecs() / 1000.0f)); } m_State = State_Running; #endif //LIVE555 } KODI->Log(LOG_DEBUG, "TsReader: Pause - END - state = %d", m_State); return S_OK; } bool CTsReader::IsSeeking() { return (m_WaitForSeekToEof > 0); } int64_t CTsReader::GetFileSize() { return m_fileReader->GetFileSize(); } int64_t CTsReader::GetFilePointer() { return m_fileReader->GetFilePointer(); } time_t CTsReader::GetStartTime() { return m_startTime; } int64_t CTsReader::GetPtsBegin() { return 0; } int64_t CTsReader::GetPtsEnd() { return (GetTickCount64() - m_startTickCount) * 1000; // useconds } int64_t CTsReader::SetFilePointer(int64_t llDistanceToMove, unsigned long dwMoveMethod) { // Are we using rtsp? if (m_bIsRTSP) { // TODO: fixme... // Need to translate the distance to move (bytes) to a time // then ask live555 to seek to that time return m_fileReader->GetFilePointer(); } else { return m_fileReader->SetFilePointer(llDistanceToMove, dwMoveMethod); } } } pvr.mediaportal.tvserver-3.5.18-Leia/src/lib/tsreader/TSReader.h000066400000000000000000000103261346756700600244560ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * 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 . * ************************************************************************* * Parts of this file originate 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 *************************************************************************/ #include "client.h" #include "FileReader.h" #include "DeMultiplexer.h" #include "Cards.h" #ifdef LIVE555 #include "MepoRTSPClient.h" #include "MemoryBuffer.h" #endif namespace MPTV { typedef enum _TsReaderState { State_Stopped = 0, State_Paused = 1, State_Running = 2 } TsReaderState; class CTsReader { public: CTsReader(); ~CTsReader(void); long Open(const char* pszFileName); long Read(unsigned char* pbData, size_t lDataLength, size_t *dwReadBytes); void Close(); int64_t SetFilePointer(int64_t llDistanceToMove, unsigned long dwMoveMethod); int64_t GetFileSize(); int64_t GetFilePointer(); time_t GetStartTime(); int64_t GetPtsBegin(); int64_t GetPtsEnd(); bool OnZap(const char* pszFileName, int64_t timeShiftBufferPos, long timeshiftBufferID); /** * \brief Pass a pointer to the MediaPortal card settings to this class * \param the cardSettings */ void SetCardSettings(CCards* cardSettings); /** * \brief Override the search directory for timeshift buffer files * \param the new search directory */ void SetDirectory(std::string& directory); void SetCardId(int id); bool IsTimeShifting(); bool IsSeeking(); long Pause(bool bPaused); TsReaderState State() { return m_State; }; private: /** * \brief Translate the given path using the m_basePath setting * \param The original (local) timeshift buffer file path on the TV server side */ std::string TranslatePath(const char* pszFileName); bool m_bTimeShifting; bool m_bRecording; bool m_bLiveTv; bool m_bIsRTSP; std::string m_fileName; FileReader* m_fileReader; FileReader* m_fileDuration; CDeMultiplexer m_demultiplexer; #ifdef LIVE555 CRTSPClient* m_rtspClient; CMemoryBuffer* m_buffer; #endif CCards* m_cardSettings; ///< Pointer to the MediaPortal card settings. Will be used to determine the base path of the timeshift buffer int m_cardId; ///< Card id for the current Card used for this timeshift buffer std::string m_basePath; ///< The base path shared by all timeshift buffers (to be determined from the Card settings) TsReaderState m_State; ///< The current state of the TsReader unsigned long long m_lastPause; ///< Last time instance at which the playback was paused unsigned long long m_startTickCount; ///< Start tick count of the time shift buffer time_t m_startTime; ///< Start time of the time shift buffer in UTC int m_WaitForSeekToEof; }; } pvr.mediaportal.tvserver-3.5.18-Leia/src/os-dependent.h000066400000000000000000000026121346756700600230120ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2012 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "p8-platform/os.h" #ifdef TARGET_LINUX // Retrieve the number of milliseconds that have elapsed since the system was started #include inline unsigned long long GetTickCount64(void) { struct timespec ts; if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { return 0; } return (unsigned long long)( (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000) ); }; #elif defined(TARGET_DARWIN) #include inline unsigned long long GetTickCount64(void) { struct timeval tv; gettimeofday(&tv, NULL); return (unsigned long long)( (tv.tv_sec * 1000) + (tv.tv_usec / 1000) ); }; #endif /* TARGET_LINUX || TARGET_DARWIN */ // Additional typedefs typedef uint8_t byte; pvr.mediaportal.tvserver-3.5.18-Leia/src/pvrclient-mediaportal.cpp000066400000000000000000002215541346756700600252750ustar00rootroot00000000000000/* * Copyright (C) 2005-2013 Team Kodi * https://kodi.tv * * 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 . * */ #include #include #include #include #include "p8-platform/util/timeutils.h" #include "p8-platform/util/StringUtils.h" #include "client.h" #include "timers.h" #include "channels.h" #include "recordings.h" #include "epg.h" #include "utils.h" #include "pvrclient-mediaportal.h" #include "lib/tsreader/TSReader.h" #ifdef TARGET_WINDOWS #include "FileUtils.h" #endif #include "GUIDialogRecordSettings.h" using namespace std; using namespace ADDON; using namespace MPTV; /* Globals */ int g_iTVServerKodiBuild = 0; /* TVServerKodi plugin supported versions */ #define TVSERVERKODI_MIN_VERSION_STRING "1.1.7.107" #define TVSERVERKODI_MIN_VERSION_BUILD 107 #define TVSERVERKODI_RECOMMENDED_VERSION_STRING "1.2.3.122 till 1.20.0.140" #define TVSERVERKODI_RECOMMENDED_VERSION_BUILD 140 /************************************************************/ /** Class interface */ cPVRClientMediaPortal::cPVRClientMediaPortal() : m_state(PVR_CONNECTION_STATE_UNKNOWN) { m_iCurrentChannel = -1; m_bCurrentChannelIsRadio = false; m_iCurrentCard = -1; m_tcpclient = new MPTV::Socket(MPTV::af_unspec, MPTV::pf_inet, MPTV::sock_stream, MPTV::tcp); m_bStop = true; m_bTimeShiftStarted = false; m_bSkipCloseLiveStream = false; m_BackendUTCoffset = 0; m_BackendTime = 0; m_tsreader = NULL; m_genretable = NULL; m_iLastRecordingUpdate = 0; m_signalStateCounter = 0; m_iSignal = 0; m_iSNR = 0; m_lastSelectedRecording = NULL; /* Generate the recording life time strings */ Timer::lifetimeValues = new cLifeTimeValues(); } cPVRClientMediaPortal::~cPVRClientMediaPortal() { KODI->Log(LOG_DEBUG, "->~cPVRClientMediaPortal()"); Disconnect(); SAFE_DELETE(Timer::lifetimeValues); SAFE_DELETE(m_tcpclient); SAFE_DELETE(m_genretable); SAFE_DELETE(m_lastSelectedRecording); } string cPVRClientMediaPortal::SendCommand(const char* command) { std::string cmd(command); return SendCommand(cmd); } string cPVRClientMediaPortal::SendCommand(const string& command) { 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 (TryConnect() == ADDON_STATUS_OK) { // Resend the command if (!m_tcpclient->send(command)) { KODI->Log(LOG_ERROR, "SendCommand('%s') failed.", command.c_str()); return ""; } } else { KODI->Log(LOG_ERROR, "SendCommand: reconnect failed."); return ""; } } } string result; if ( !m_tcpclient->ReadLine( result ) ) { KODI->Log(LOG_ERROR, "SendCommand - Failed."); return ""; } if (result.find("[ERROR]:") != std::string::npos) { KODI->Log(LOG_ERROR, "TVServerKodi error: %s", result.c_str()); } return result; } bool cPVRClientMediaPortal::SendCommand2(const string& command, vector& lines) { string result = SendCommand(command); if (result.empty()) { return false; } Tokenize(result, lines, ","); return true; } ADDON_STATUS cPVRClientMediaPortal::TryConnect() { /* Open Connection to MediaPortal Backend TV Server via the TVServerKodi plugin */ KODI->Log(LOG_INFO, "Mediaportal pvr addon " STR(MPTV_VERSION) " 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: KODI->Log(LOG_ERROR, "Could not connect to MediaPortal TV Server backend."); // Start background thread for connecting to the backend if (!IsRunning()) { KODI->Log(LOG_INFO, "Waiting for a connection in the background."); CreateThread(); } return ADDON_STATUS_LOST_CONNECTION; case PVR_CONNECTION_STATE_CONNECTING: case PVR_CONNECTION_STATE_CONNECTED: break; } return ADDON_STATUS_OK; } PVR_CONNECTION_STATE cPVRClientMediaPortal::Connect(bool updateConnectionState) { P8PLATFORM::CLockObject critsec(m_connectionMutex); string result; if (!m_tcpclient->create()) { KODI->Log(LOG_ERROR, "Could not connect create socket"); if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_UNKNOWN); } return PVR_CONNECTION_STATE_UNKNOWN; } if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_CONNECTING); } if (!m_tcpclient->connect(g_szHostname, (unsigned short) g_iPort)) { if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_SERVER_UNREACHABLE); } return PVR_CONNECTION_STATE_SERVER_UNREACHABLE; } m_tcpclient->set_non_blocking(1); KODI->Log(LOG_INFO, "Connected to %s:%i", g_szHostname.c_str(), g_iPort); result = SendCommand("PVRclientXBMC:0-1\n"); if (result.length() == 0) { if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_UNKNOWN); } return PVR_CONNECTION_STATE_UNKNOWN; } if(result.find("Unexpected protocol") != std::string::npos) { KODI->Log(LOG_ERROR, "TVServer does not accept protocol: PVRclientXBMC:0-1"); if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_SERVER_MISMATCH); } return PVR_CONNECTION_STATE_SERVER_MISMATCH; } vector fields; int major = 0, minor = 0, revision = 0; // Check the version of the TVServerKodi plugin: Tokenize(result, fields, "|"); if(fields.size() < 2) { KODI->Log(LOG_ERROR, "Your TVServerKodi version is too old. Please upgrade to '%s' or higher!", TVSERVERKODI_MIN_VERSION_STRING); KODI->QueueNotification(QUEUE_ERROR, KODI->GetLocalizedString(30051), TVSERVERKODI_MIN_VERSION_STRING); if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_VERSION_MISMATCH); } return PVR_CONNECTION_STATE_VERSION_MISMATCH; } // Ok, this TVServerKodi version answers with a version string int count = sscanf(fields[1].c_str(), "%5d.%5d.%5d.%5d", &major, &minor, &revision, &g_iTVServerKodiBuild); if( count < 4 ) { KODI->Log(LOG_ERROR, "Could not parse the TVServerKodi version string '%s'", fields[1].c_str()); if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_VERSION_MISMATCH); } return PVR_CONNECTION_STATE_VERSION_MISMATCH; } // Check for the minimal requirement: 1.1.0.70 if( g_iTVServerKodiBuild < TVSERVERKODI_MIN_VERSION_BUILD ) //major < 1 || minor < 1 || revision < 0 || build < 70 { KODI->Log(LOG_ERROR, "Your TVServerKodi version '%s' is too old. Please upgrade to '%s' or higher!", fields[1].c_str(), TVSERVERKODI_MIN_VERSION_STRING); KODI->QueueNotification(QUEUE_ERROR, KODI->GetLocalizedString(30050), fields[1].c_str(), TVSERVERKODI_MIN_VERSION_STRING); if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_VERSION_MISMATCH); } return PVR_CONNECTION_STATE_VERSION_MISMATCH; } else { KODI->Log(LOG_INFO, "Your TVServerKodi version is '%s'", fields[1].c_str()); // Advice to upgrade: if( g_iTVServerKodiBuild < TVSERVERKODI_RECOMMENDED_VERSION_BUILD ) { KODI->Log(LOG_INFO, "It is adviced to upgrade your TVServerKodi version '%s' to '%s' or higher!", fields[1].c_str(), TVSERVERKODI_RECOMMENDED_VERSION_STRING); } } /* Store connection string */ char buffer[512]; snprintf(buffer, 512, "%s:%i", g_szHostname.c_str(), g_iPort); m_ConnectionString = buffer; if (updateConnectionState) { SetConnectionState(PVR_CONNECTION_STATE_CONNECTED); } /* Load additional settings */ LoadGenreTable(); LoadCardSettings(); /* The pvr addon cannot access Kodi's current locale settings, so just use the system default */ setlocale(LC_ALL, ""); return PVR_CONNECTION_STATE_CONNECTED; } void cPVRClientMediaPortal::Disconnect() { string result; KODI->Log(LOG_INFO, "Disconnect"); if (IsRunning()) { StopThread(1000); } if (m_tcpclient->is_valid() && m_bTimeShiftStarted) { result = SendCommand("IsTimeshifting:\n"); if (result.find("True") != std::string::npos ) { if ((g_eStreamingMethod==TSReader) && (m_tsreader != NULL)) { m_tsreader->Close(); SAFE_DELETE(m_tsreader); } SendCommand("StopTimeshift:\n"); } } m_bStop = true; m_tcpclient->close(); SetConnectionState(PVR_CONNECTION_STATE_DISCONNECTED); } /* IsUp() * \brief Check whether we still have a connection with the TVServer. If not, try * to reconnect * \return True when a connection is available, False when even a reconnect failed */ bool cPVRClientMediaPortal::IsUp() { if (m_state == PVR_CONNECTION_STATE_CONNECTED) { return true; } else { return false; } } void* cPVRClientMediaPortal::Process(void) { KODI->Log(LOG_DEBUG, "Background thread started."); bool keepWaiting = true; PVR_CONNECTION_STATE state; while (!IsStopped() && keepWaiting) { state = Connect(false); switch (state) { 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; break; case PVR_CONNECTION_STATE_CONNECTED: keepWaiting = false; break; default: break; } if (keepWaiting) { // Wait for 1 minute before re-trying usleep(60000000); } } SetConnectionState(state); KODI->Log(LOG_DEBUG, "Background thread finished."); return NULL; } /************************************************************/ /** General handling */ // Used among others for the server name string in the "Recordings" view const char* cPVRClientMediaPortal::GetBackendName(void) { if (!IsUp()) { return g_szHostname.c_str(); } KODI->Log(LOG_DEBUG, "->GetBackendName()"); if (m_BackendName.length() == 0) { m_BackendName = "MediaPortal TV-server ("; m_BackendName += SendCommand("GetBackendName:\n"); m_BackendName += ")"; } return m_BackendName.c_str(); } const char* cPVRClientMediaPortal::GetBackendVersion(void) { if (!IsUp()) return "0.0"; if(m_BackendVersion.length() == 0) { m_BackendVersion = SendCommand("GetVersion:\n"); } KODI->Log(LOG_DEBUG, "GetBackendVersion: %s", m_BackendVersion.c_str()); return m_BackendVersion.c_str(); } const char* cPVRClientMediaPortal::GetConnectionString(void) { if (m_ConnectionString.empty()) return ""; KODI->Log(LOG_DEBUG, "GetConnectionString: %s", m_ConnectionString.c_str()); return m_ConnectionString.c_str(); } PVR_ERROR cPVRClientMediaPortal::GetDriveSpace(long long *iTotal, long long *iUsed) { string result; vector fields; *iTotal = 0; *iUsed = 0; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; result = SendCommand("GetDriveSpace:\n"); Tokenize(result, fields, "|"); if(fields.size() >= 2) { *iTotal = (long long) atoi(fields[0].c_str()); *iUsed = (long long) atoi(fields[1].c_str()); } return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::GetBackendTime(time_t *localTime, int *gmtOffset) { string result; vector fields; int year = 0, month = 0, day = 0; int hour = 0, minute = 0, second = 0; struct tm timeinfo; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; result = SendCommand("GetTime:\n"); if (result.length() == 0) return PVR_ERROR_SERVER_ERROR; Tokenize(result, fields, "|"); if(fields.size() >= 3) { //[0] date + time TV Server //[1] UTC offset hours //[2] UTC offset minutes //From CPVREpg::CPVREpg(): Expected PVREpg GMT offset is in seconds m_BackendUTCoffset = ((atoi(fields[1].c_str()) * 60) + atoi(fields[2].c_str())) * 60; int count = sscanf(fields[0].c_str(), "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second); if(count == 6) { //timeinfo = *localtime ( &rawtime ); KODI->Log(LOG_DEBUG, "GetMPTVTime: time from MP TV Server: %d-%d-%d %d:%d:%d, offset %d seconds", year, month, day, hour, minute, second, m_BackendUTCoffset ); timeinfo.tm_hour = hour; timeinfo.tm_min = minute; timeinfo.tm_sec = second; timeinfo.tm_year = year - 1900; timeinfo.tm_mon = month - 1; timeinfo.tm_mday = day; timeinfo.tm_isdst = -1; //Actively determines whether DST is in effect from the specified time and the local time zone. // Make the other fields empty: timeinfo.tm_wday = 0; timeinfo.tm_yday = 0; m_BackendTime = mktime(&timeinfo); if(m_BackendTime < 0) { KODI->Log(LOG_DEBUG, "GetMPTVTime: Unable to convert string '%s' into date+time", fields[0].c_str()); return PVR_ERROR_SERVER_ERROR; } KODI->Log(LOG_DEBUG, "GetMPTVTime: localtime %s", asctime(localtime(&m_BackendTime))); KODI->Log(LOG_DEBUG, "GetMPTVTime: gmtime %s", asctime(gmtime(&m_BackendTime))); *localTime = m_BackendTime; *gmtOffset = m_BackendUTCoffset; return PVR_ERROR_NO_ERROR; } else { return PVR_ERROR_SERVER_ERROR; } } else return PVR_ERROR_SERVER_ERROR; } /************************************************************/ /** EPG handling */ PVR_ERROR cPVRClientMediaPortal::GetEpg(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd) { vector lines; char command[256]; string result; cEpg epg; EPG_TAG broadcast; struct tm starttime; struct tm endtime; starttime = *gmtime( &iStart ); endtime = *gmtime( &iEnd ); if (!IsUp()) return PVR_ERROR_SERVER_ERROR; // Request (extended) EPG data for the given period snprintf(command, 256, "GetEPG:%i|%04d-%02d-%02dT%02d:%02d:%02d.0Z|%04d-%02d-%02dT%02d:%02d:%02d.0Z\n", channel.iUniqueId, //Channel id starttime.tm_year + 1900, starttime.tm_mon + 1, starttime.tm_mday, //Start date [2..4] starttime.tm_hour, starttime.tm_min, starttime.tm_sec, //Start time [5..7] endtime.tm_year + 1900, endtime.tm_mon + 1, endtime.tm_mday, //End date [8..10] endtime.tm_hour, endtime.tm_min, endtime.tm_sec); //End time [11..13] result = SendCommand(command); if(result.compare(0,5, "ERROR") != 0) { if( result.length() != 0) { memset(&broadcast, 0, sizeof(EPG_TAG)); epg.SetGenreTable(m_genretable); Tokenize(result, lines, ","); KODI->Log(LOG_DEBUG, "Found %i EPG items for channel %i\n", lines.size(), channel.iUniqueId); for (vector::iterator it = lines.begin(); it < lines.end(); ++it) { string& data(*it); if( data.length() > 0) { uri::decode(data); bool isEnd = epg.ParseLine(data); if (isEnd && epg.StartTime() != 0) { broadcast.iUniqueBroadcastId = epg.UniqueId(); broadcast.strTitle = epg.Title(); broadcast.iUniqueChannelId = channel.iUniqueId; broadcast.startTime = epg.StartTime(); broadcast.endTime = epg.EndTime(); broadcast.strPlotOutline = epg.PlotOutline(); broadcast.strPlot = epg.Description(); broadcast.strIconPath = ""; broadcast.iGenreType = epg.GenreType(); broadcast.iGenreSubType = epg.GenreSubType(); broadcast.strGenreDescription = epg.Genre(); broadcast.firstAired = epg.OriginalAirDate(); broadcast.iParentalRating = epg.ParentalRating(); broadcast.iStarRating = epg.StarRating(); broadcast.bNotify = false; broadcast.iSeriesNumber = epg.SeriesNumber(); broadcast.iEpisodeNumber = epg.EpisodeNumber(); broadcast.iEpisodePartNumber = atoi(epg.EpisodePart()); broadcast.strEpisodeName = epg.EpisodeName(); broadcast.iFlags = EPG_TAG_FLAG_UNDEFINED; PVR->TransferEpgEntry(handle, &broadcast); } epg.Reset(); } } } else { KODI->Log(LOG_DEBUG, "No EPG items found for channel %i", channel.iUniqueId); } } else { KODI->Log(LOG_DEBUG, "RequestEPGForChannel(%i) %s", channel.iUniqueId, result.c_str()); } return PVR_ERROR_NO_ERROR; } /************************************************************/ /** Channel handling */ int cPVRClientMediaPortal::GetNumChannels(void) { string result; if (!IsUp()) return -1; // Get the total channel count (radio+tv) // It is only used to check whether Kodi should request the channel list result = SendCommand("GetChannelCount:\n"); return atol(result.c_str()); } PVR_ERROR cPVRClientMediaPortal::GetChannels(ADDON_HANDLE handle, bool bRadio) { vector lines; std::string command; const char * baseCommand; PVR_CHANNEL tag; std::string stream; std::string groups; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; if(bRadio) { if(!g_bRadioEnabled) { KODI->Log(LOG_INFO, "Fetching radio channels is disabled."); return PVR_ERROR_NO_ERROR; } baseCommand = "ListRadioChannels"; if (g_szRadioGroup.empty()) { KODI->Log(LOG_DEBUG, "GetChannels(radio) all channels"); } else { KODI->Log(LOG_DEBUG, "GetChannels(radio) for radio group(s): '%s'", g_szRadioGroup.c_str()); groups = uri::encode(uri::PATH_TRAITS, g_szRadioGroup); StringUtils::Replace(groups, "%7C","|"); } } else { baseCommand = "ListTVChannels"; if (g_szTVGroup.empty()) { KODI->Log(LOG_DEBUG, "GetChannels(tv) all channels"); } else { KODI->Log(LOG_DEBUG, "GetChannels(tv) for TV group(s): '%s'", g_szTVGroup.c_str()); groups = uri::encode(uri::PATH_TRAITS, g_szTVGroup); StringUtils::Replace(groups, "%7C","|"); } } if (groups.empty()) command = StringUtils::Format("%s\n", baseCommand); else command = StringUtils::Format("%s:%s\n", baseCommand, groups.c_str()); if( !SendCommand2(command, lines) ) return PVR_ERROR_SERVER_ERROR; #ifdef TARGET_WINDOWS_DESKTOP bool bCheckForThumbs = false; /* Check if we can find the MediaPortal channel logo folders on this machine */ std::string strThumbPath; std::string strProgramData; if (OS::GetProgramData(strProgramData) == true) { strThumbPath = strProgramData + "\\Team MediaPortal\\MediaPortal\\Thumbs\\"; if (bRadio) strThumbPath += "Radio\\"; else strThumbPath += "TV\\logos\\"; bCheckForThumbs = OS::CFile::Exists(strThumbPath); } #endif // TARGET_WINDOWS_DESKTOP memset(&tag, 0, sizeof(PVR_CHANNEL)); for (vector::iterator it = lines.begin(); it < lines.end(); ++it) { string& data(*it); if (data.length() == 0) { if(bRadio) KODI->Log(LOG_DEBUG, "TVServer returned no data. Empty/non existing radio group '%s'?", g_szRadioGroup.c_str()); else KODI->Log(LOG_DEBUG, "TVServer returned no data. Empty/non existing tv group '%s'?", g_szTVGroup.c_str()); break; } uri::decode(data); cChannel channel; if( channel.Parse(data) ) { // Cache this channel in our local uid-channel list // This cache is used for the GUIDialogRecordSettings m_channels[channel.UID()] = channel; // Prepare the PVR_CHANNEL struct to transfer this channel to Kodi tag.iUniqueId = channel.UID(); if (channel.MajorChannelNr() == -1) { tag.iChannelNumber = channel.ExternalID(); } else { tag.iChannelNumber = channel.MajorChannelNr(); tag.iSubChannelNumber = channel.MinorChannelNr(); } PVR_STRCPY(tag.strChannelName, channel.Name()); PVR_STRCLR(tag.strIconPath); #ifdef TARGET_WINDOWS_DESKTOP if (bCheckForThumbs) { const int ciExtCount = 5; string strIconExt [ciExtCount] = { ".png", ".jpg", ".jpeg", ".bmp", ".gif" }; string strIconName; string strIconBaseName; KODI->Log(LOG_DEBUG, "Checking for a channel thumbnail for channel %s in %s", channel.Name(), strThumbPath.c_str()); strIconBaseName = strThumbPath + ToThumbFileName(channel.Name()); for (int i=0; i < ciExtCount; i++) { strIconName = strIconBaseName + strIconExt[i]; if ( OS::CFile::Exists(strIconName) ) { PVR_STRCPY(tag.strIconPath, strIconName.c_str()); KODI->Log(LOG_DEBUG, "Found channel thumb: %s", tag.strIconPath); break; } } } #endif tag.iEncryptionSystem = channel.Encrypted(); tag.bIsRadio = bRadio; tag.bIsHidden = !channel.VisibleInGuide(); if(channel.IsWebstream()) { KODI->Log(LOG_DEBUG, "Channel '%s' has a webstream: %s. TODO fixme.", channel.Name(), channel.URL()); PVR_STRCLR(tag.strInputFormat); } else { if (g_eStreamingMethod==TSReader) { // TSReader //Use OpenLiveStream to read from the timeshift .ts file or an rtsp stream if (!bRadio) PVR_STRCPY(tag.strInputFormat, "video/mp2t"); else PVR_STRCLR(tag.strInputFormat); } else { //Use GetLiveStreamURL to fetch an rtsp stream KODI->Log(LOG_DEBUG, "Channel '%s' has a rtsp stream: %s. TODO fixme.", channel.Name(), channel.URL()); PVR_STRCLR(tag.strInputFormat); } } if( (!g_bOnlyFTA) || (tag.iEncryptionSystem==0)) { PVR->TransferChannelEntry(handle, &tag); } } } //pthread_mutex_unlock(&m_critSection); return PVR_ERROR_NO_ERROR; } /************************************************************/ /** Channel group handling **/ int cPVRClientMediaPortal::GetChannelGroupsAmount(void) { // Not directly possible at the moment KODI->Log(LOG_DEBUG, "GetChannelGroupsAmount: TODO"); if (!IsUp()) return PVR_ERROR_SERVER_ERROR; // just tell Kodi that we have groups return 1; } PVR_ERROR cPVRClientMediaPortal::GetChannelGroups(ADDON_HANDLE handle, bool bRadio) { vector lines; std::string filters; PVR_CHANNEL_GROUP tag; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; if(bRadio) { if (!g_bRadioEnabled) { KODI->Log(LOG_DEBUG, "Skipping GetChannelGroups for radio. Radio support is disabled."); return PVR_ERROR_NO_ERROR; } filters = g_szRadioGroup; KODI->Log(LOG_DEBUG, "GetChannelGroups for radio"); if (!SendCommand2("ListRadioGroups\n", lines)) return PVR_ERROR_SERVER_ERROR; } else { filters = g_szTVGroup; KODI->Log(LOG_DEBUG, "GetChannelGroups for TV"); if (!SendCommand2("ListGroups\n", lines)) return PVR_ERROR_SERVER_ERROR; } memset(&tag, 0, sizeof(PVR_CHANNEL_GROUP)); for (vector::iterator it = lines.begin(); it < lines.end(); ++it) { string& data(*it); if (data.length() == 0) { KODI->Log(LOG_DEBUG, "TVServer returned no data. No %s groups found?", ((bRadio) ? "radio" : "tv")); break; } uri::decode(data); if (data.compare("All Channels") == 0) { KODI->Log(LOG_DEBUG, "Skipping All Channels (%s) group", ((bRadio) ? "radio" : "tv"), tag.strGroupName); } else { if (!filters.empty()) { if (filters.find(data.c_str()) == string::npos) { // Skip this backend group. It is not in our filter list continue; } } tag.bIsRadio = bRadio; PVR_STRCPY(tag.strGroupName, data.c_str()); KODI->Log(LOG_DEBUG, "Adding %s group: %s", ((bRadio) ? "radio" : "tv"), tag.strGroupName); PVR->TransferChannelGroup(handle, &tag); } } return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) { //TODO: code below is similar to GetChannels code. Refactor and combine... vector lines; std::string command; PVR_CHANNEL_GROUP_MEMBER tag; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; if (group.bIsRadio) { if (g_bRadioEnabled) { KODI->Log(LOG_DEBUG, "GetChannelGroupMembers: for radio group '%s'", group.strGroupName); command = StringUtils::Format("ListRadioChannels:%s\n", uri::encode(uri::PATH_TRAITS, group.strGroupName).c_str()); } else { KODI->Log(LOG_DEBUG, "Skipping GetChannelGroupMembers for radio. Radio support is disabled."); return PVR_ERROR_NO_ERROR; } } else { KODI->Log(LOG_DEBUG, "GetChannelGroupMembers: for tv group '%s'", group.strGroupName); command = StringUtils::Format("ListTVChannels:%s\n", uri::encode(uri::PATH_TRAITS, group.strGroupName).c_str()); } if (!SendCommand2(command, lines)) return PVR_ERROR_SERVER_ERROR; memset(&tag, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); for (vector::iterator it = lines.begin(); it < lines.end(); ++it) { string& data(*it); if (data.length() == 0) { if(group.bIsRadio) KODI->Log(LOG_DEBUG, "TVServer returned no data. Empty/non existing radio group '%s'?", g_szRadioGroup.c_str()); else KODI->Log(LOG_DEBUG, "TVServer returned no data. Empty/non existing tv group '%s'?", g_szTVGroup.c_str()); break; } uri::decode(data); cChannel channel; if( channel.Parse(data) ) { tag.iChannelUniqueId = channel.UID(); if (channel.MajorChannelNr() == -1) { tag.iChannelNumber = channel.ExternalID(); } else { tag.iChannelNumber = channel.MajorChannelNr(); tag.iSubChannelNumber = channel.MinorChannelNr(); } PVR_STRCPY(tag.strGroupName, group.strGroupName); // Don't add encrypted channels when FTA only option is turned on if( (!g_bOnlyFTA) || (channel.Encrypted()==false)) { KODI->Log(LOG_DEBUG, "GetChannelGroupMembers: add channel %s to group '%s' (Backend channel uid=%d, channelnr=%d)", channel.Name(), group.strGroupName, tag.iChannelUniqueId, tag.iChannelNumber); PVR->TransferChannelGroupMember(handle, &tag); } } } return PVR_ERROR_NO_ERROR; } /************************************************************/ /** Record handling **/ int cPVRClientMediaPortal::GetNumRecordings(void) { string result; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; result = SendCommand("GetRecordingCount:\n"); return atol(result.c_str()); } PVR_ERROR cPVRClientMediaPortal::GetRecordings(ADDON_HANDLE handle) { vector lines; string result; PVR_RECORDING tag; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; if(g_bResolveRTSPHostname == false) { result = SendCommand("ListRecordings:False\n"); } else { result = SendCommand("ListRecordings\n"); } if( result.length() == 0 ) { KODI->Log(LOG_DEBUG, "Backend returned no recordings" ); return PVR_ERROR_NO_ERROR; } Tokenize(result, lines, ","); memset(&tag, 0, sizeof(PVR_RECORDING)); for (vector::iterator it = lines.begin(); it != lines.end(); ++it) { string& data(*it); uri::decode(data); KODI->Log(LOG_DEBUG, "RECORDING: %s", data.c_str() ); std::string strRecordingId; std::string strDirectory; std::string strEpisodeName; cRecording recording; recording.SetCardSettings(&m_cCards); recording.SetGenreTable(m_genretable); if (recording.ParseLine(data)) { strRecordingId = StringUtils::Format("%i", recording.Index()); strEpisodeName = recording.EpisodeName(); PVR_STRCPY(tag.strRecordingId, strRecordingId.c_str()); PVR_STRCPY(tag.strTitle, recording.Title()); 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 tag.iLifetime = recording.Lifetime(); tag.iGenreType = recording.GenreType(); tag.iGenreSubType = recording.GenreSubType(); PVR_STRCPY(tag.strGenreDescription, recording.GetGenre()); tag.iPlayCount = recording.TimesWatched(); 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) { StringUtils::Replace(strDirectory, "\\", " - "); // Kodi supports only 1 sublevel below Recordings, so flatten the MediaPortal directory structure PVR_STRCPY(tag.strDirectory, strDirectory.c_str()); // used in Kodi as directory structure below "Recordings" if ((StringUtils::EqualsNoCase(strDirectory, tag.strTitle)) && (strEpisodeName.length() > 0)) { strEpisodeName = recording.Title(); strEpisodeName+= " - "; strEpisodeName+= recording.EpisodeName(); PVR_STRCPY(tag.strTitle, strEpisodeName.c_str()); } } else { PVR_STRCLR(tag.strDirectory); } PVR_STRCLR(tag.strThumbnailPath); #ifdef TARGET_WINDOWS_DESKTOP std::string recordingUri(ToKodiPath(recording.FilePath())); if (g_bUseRTSP == false) { /* Recording thumbnail */ std::string strThumbnailName(recordingUri); StringUtils::Replace(strThumbnailName, ".ts", ".jpg"); /* Check if it exists next to the recording */ if (KODI->FileExists(strThumbnailName.c_str(), false)) { PVR_STRCPY(tag.strThumbnailPath, strThumbnailName.c_str()); } else { /* Check also: C:\ProgramData\Team MediaPortal\MediaPortal TV Server\thumbs */ std::string strThumbnailFilename = recording.FileName(); StringUtils::Replace(strThumbnailFilename, ".ts", ".jpg"); std::string strProgramData; if (OS::GetProgramData(strProgramData)) { /* MediaPortal 1 */ strThumbnailName = strProgramData + "\\Team MediaPortal\\MediaPortal TV Server\\thumbs\\" + strThumbnailFilename; if (KODI->FileExists(strThumbnailName.c_str(), false)) { PVR_STRCPY(tag.strThumbnailPath, strThumbnailName.c_str()); } else { /* MediaPortal 2 */ strThumbnailName = strProgramData + "\\Team MediaPortal\\MP2-Server\\SlimTVCore\\v3.0\\thumbs\\" + strThumbnailFilename; if (KODI->FileExists(strThumbnailName.c_str(), false)) { PVR_STRCPY(tag.strThumbnailPath, strThumbnailName.c_str()); } } } else PVR_STRCLR(tag.strThumbnailPath); } } #endif /* TARGET_WINDOWS_DESKTOP */ if (g_eStreamingMethod!=TSReader) { // Use rtsp url and Kodi's internal FFMPeg playback KODI->Log(LOG_DEBUG, "Recording '%s' has a rtsp url '%s'. TODO Fix me. ", recording.Title(), recording.Stream()); } PVR->TransferRecordingEntry(handle, &tag); } } m_iLastRecordingUpdate = P8PLATFORM::GetTimeMs(); return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::DeleteRecording(const PVR_RECORDING &recording) { char command[1200]; string result; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; snprintf(command, 1200, "DeleteRecordedTV:%s\n", recording.strRecordingId); result = SendCommand(command); if(result.find("True") == string::npos) { KODI->Log(LOG_ERROR, "Deleting recording %s [failed]", recording.strRecordingId); return PVR_ERROR_FAILED; } KODI->Log(LOG_DEBUG, "Deleting recording %s [done]", recording.strRecordingId); // Although Kodi initiates the deletion of this recording, we still have to trigger Kodi to update its // recordings list to remove the recording at the Kodi side PVR->TriggerRecordingUpdate(); return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::RenameRecording(const PVR_RECORDING &recording) { char command[1200]; string result; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; snprintf(command, 1200, "UpdateRecording:%s|%s\n", recording.strRecordingId, uri::encode(uri::PATH_TRAITS, recording.strTitle).c_str()); result = SendCommand(command); if(result.find("True") == string::npos) { KODI->Log(LOG_ERROR, "RenameRecording(%s) to %s [failed]", recording.strRecordingId, recording.strTitle); return PVR_ERROR_FAILED; } KODI->Log(LOG_DEBUG, "RenameRecording(%s) to %s [done]", recording.strRecordingId, recording.strTitle); // Although Kodi initiates the rename of this recording, we still have to trigger Kodi to update its // recordings list to see the renamed recording at the Kodi side PVR->TriggerRecordingUpdate(); return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::SetRecordingPlayCount(const PVR_RECORDING &recording, int count) { if ( g_iTVServerKodiBuild < 117 ) return PVR_ERROR_NOT_IMPLEMENTED; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; char command[512]; string result; snprintf(command, 512, "SetRecordingTimesWatched:%i|%i\n", atoi(recording.strRecordingId), count); result = SendCommand(command); if(result.find("True") == string::npos) { KODI->Log(LOG_ERROR, "%s: id=%s to %i [failed]", __FUNCTION__, recording.strRecordingId, count); return PVR_ERROR_FAILED; } KODI->Log(LOG_DEBUG, "%s: id=%s to %i [successful]", __FUNCTION__, recording.strRecordingId, count); PVR->TriggerRecordingUpdate(); return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition) { if ( g_iTVServerKodiBuild < 121 ) return PVR_ERROR_NOT_IMPLEMENTED; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; char command[512]; string result; if (lastplayedposition < 0) { lastplayedposition = 0; } snprintf(command, 512, "SetRecordingStopTime:%i|%i\n", atoi(recording.strRecordingId), lastplayedposition); result = SendCommand(command); if(result.find("True") == string::npos) { KODI->Log(LOG_ERROR, "%s: id=%s to %i [failed]", __FUNCTION__, recording.strRecordingId, lastplayedposition); return PVR_ERROR_FAILED; } KODI->Log(LOG_DEBUG, "%s: id=%s to %i [successful]", __FUNCTION__, recording.strRecordingId, lastplayedposition); PVR->TriggerRecordingUpdate(); return PVR_ERROR_NO_ERROR; } int cPVRClientMediaPortal::GetRecordingLastPlayedPosition(const PVR_RECORDING &recording) { if ( g_iTVServerKodiBuild < 121 ) return PVR_ERROR_NOT_IMPLEMENTED; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; char command[512]; string result; int lastplayedposition; snprintf(command, 512, "GetRecordingStopTime:%i\n", atoi(recording.strRecordingId)); result = SendCommand(command); if(result.find("-1") != string::npos) { KODI->Log(LOG_ERROR, "%s: id=%s fetching stoptime [failed]", __FUNCTION__, recording.strRecordingId); return 0; } lastplayedposition = atoi(result.c_str()); KODI->Log(LOG_DEBUG, "%s: id=%s stoptime=%i {s} [successful]", __FUNCTION__, recording.strRecordingId, lastplayedposition); return lastplayedposition; } /************************************************************/ /** Timer handling */ int cPVRClientMediaPortal::GetNumTimers(void) { string result; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; result = SendCommand("GetScheduleCount:\n"); return atol(result.c_str()); } PVR_ERROR cPVRClientMediaPortal::GetTimers(ADDON_HANDLE handle) { vector lines; string result; PVR_TIMER tag; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; result = SendCommand("ListSchedules:True\n"); if (result.length() > 0) { Tokenize(result, lines, ","); memset(&tag, 0, sizeof(PVR_TIMER)); for (vector::iterator it = lines.begin(); it != lines.end(); ++it) { string& data(*it); uri::decode(data); KODI->Log(LOG_DEBUG, "SCHEDULED: %s", data.c_str() ); cTimer timer; timer.SetGenreTable(m_genretable); if(timer.ParseLine(data.c_str()) == true) { timer.GetPVRtimerinfo(tag); PVR->TransferTimerEntry(handle, &tag); } } } if ( P8PLATFORM::GetTimeMs() > m_iLastRecordingUpdate + 15000) { PVR->TriggerRecordingUpdate(); } return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::GetTimerInfo(unsigned int timernumber, PVR_TIMER &timerinfo) { string result; char command[256]; KODI->Log(LOG_DEBUG, "->GetTimerInfo(%u)", timernumber); if (!IsUp()) return PVR_ERROR_SERVER_ERROR; snprintf(command, 256, "GetScheduleInfo:%u\n", timernumber); result = SendCommand(command); if (result.length() == 0) return PVR_ERROR_SERVER_ERROR; cTimer timer; if( timer.ParseLine(result.c_str()) == false ) { KODI->Log(LOG_DEBUG, "GetTimerInfo(%i) parsing server response failed. Response: %s", timernumber, result.c_str()); return PVR_ERROR_SERVER_ERROR; } timer.GetPVRtimerinfo(timerinfo); return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::GetTimerTypes(PVR_TIMER_TYPE types[], int *size) { int maxsize = *size; // the size of the types[] array when this functon is called 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; //Note: schedule priority support is not implemented here // The MediaPortal TV Server database has a priority field but their wiki // says: "This feature is yet to be enabled". // One-shot epg-based (maps to MediaPortal 'Once') memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::Once; types[count].iAttributes = MPTV_RECORD_ONCE; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30110)); /* Record once */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; if (count > maxsize) return PVR_ERROR_NO_ERROR; // Series weekly epg-based (maps to MediaPortal 'EveryTimeOnThisChannel') memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::EveryTimeOnThisChannel; types[count].iAttributes = MPTV_RECORD_EVERY_TIME_ON_THIS_CHANNEL; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30115)); /* Record every time on this channel */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; if (count > maxsize) return PVR_ERROR_NO_ERROR; // Series weekly epg-based (maps to MediaPortal 'EveryTimeOnEveryChannel') memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::EveryTimeOnEveryChannel; types[count].iAttributes = MPTV_RECORD_EVERY_TIME_ON_EVERY_CHANNEL; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30116)); /* Record every time on every channel */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; if (count > maxsize) return PVR_ERROR_NO_ERROR; // Series weekly epg-based (maps to MediaPortal 'Weekly') memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::Weekly; types[count].iAttributes = MPTV_RECORD_WEEKLY; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30117)); /* "Record every week at this time" */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; if (count > maxsize) return PVR_ERROR_NO_ERROR; // Series daily epg-based (maps to MediaPortal 'Daily') memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::Daily; types[count].iAttributes = MPTV_RECORD_DAILY; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30118)); /* Record every day at this time */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; if (count > maxsize) return PVR_ERROR_NO_ERROR; // Series Weekends epg-based (maps to MediaPortal 'WorkingDays') memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::WorkingDays; types[count].iAttributes = MPTV_RECORD_WORKING_DAYS; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30114)); /* Record weekdays */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; if (count > maxsize) return PVR_ERROR_NO_ERROR; // Series Weekends epg-based (maps to MediaPortal 'Weekends') memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::Weekends; types[count].iAttributes = MPTV_RECORD_WEEEKENDS; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30113)); /* Record Weekends */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; if (count > maxsize) return PVR_ERROR_NO_ERROR; // Series weekly epg-based (maps to MediaPortal 'WeeklyEveryTimeOnThisChannel') memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::WeeklyEveryTimeOnThisChannel; types[count].iAttributes = MPTV_RECORD_WEEKLY_EVERY_TIME_ON_THIS_CHANNEL; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30119)); /* Weekly on this channel */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; if (count > maxsize) return PVR_ERROR_NO_ERROR; /* Kodi specific 'Manual' schedule type */ memset(&types[count], 0, sizeof(types[count])); types[count].iId = cKodiTimerTypeOffset + TvDatabase::KodiManual; types[count].iAttributes = MPTV_RECORD_MANUAL; PVR_STRCPY(types[count].strDescription, KODI->GetLocalizedString(30122)); /* Manual */ Timer::lifetimeValues->SetLifeTimeValues(types[count]); count++; return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::AddTimer(const PVR_TIMER &timerinfo) { string result; #ifdef _TIME32_T_DEFINED KODI->Log(LOG_DEBUG, "->AddTimer Channel: %i, starttime: %i endtime: %i program: %s", timerinfo.iClientChannelUid, timerinfo.startTime, timerinfo.endTime, timerinfo.strTitle); #else KODI->Log(LOG_DEBUG, "->AddTimer Channel: %i, 64 bit times not yet supported!", timerinfo.iClientChannelUid); #endif if (!IsUp()) return PVR_ERROR_SERVER_ERROR; cTimer timer(timerinfo); if (g_bEnableOldSeriesDlg && (timerinfo.startTime > 0) && (timerinfo.iEpgUid != PVR_TIMER_NO_EPG_UID) && ((timerinfo.iTimerType - cKodiTimerTypeOffset) == (unsigned int) TvDatabase::Once) ) { /* New scheduled recording, not an instant or manual recording * Present a custom dialog with advanced recording settings */ std::string strChannelName; if (timerinfo.iClientChannelUid >= 0) { strChannelName = m_channels[timerinfo.iClientChannelUid].Name(); } CGUIDialogRecordSettings dlgRecSettings( timerinfo, timer, strChannelName); int dlogResult = dlgRecSettings.DoModal(); if (dlogResult == 0) return PVR_ERROR_NO_ERROR; // user canceled timer in dialog } result = SendCommand(timer.AddScheduleCommand()); if(result.find("True") == string::npos) { KODI->Log(LOG_DEBUG, "AddTimer for channel: %i [failed]", timerinfo.iClientChannelUid); return PVR_ERROR_FAILED; } KODI->Log(LOG_DEBUG, "AddTimer for channel: %i [done]", timerinfo.iClientChannelUid); // Although Kodi adds this timer, we still have to trigger Kodi to update its timer list to // see this new timer at the Kodi side PVR->TriggerTimerUpdate(); if ( timerinfo.startTime <= 0) { // Refresh the recordings list to see the newly created recording usleep(100000); PVR->TriggerRecordingUpdate(); } return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::DeleteTimer(const PVR_TIMER &timer, bool UNUSED(bForceDelete)) { char command[256]; string result; if (!IsUp()) return PVR_ERROR_SERVER_ERROR; // Check if this timer has a parent schedule and a program id // When true, it has no real schedule at the Mediaportal side. // The best we can do in that case is disable the timer for this program only if ((timer.iParentClientIndex > 0) && (timer.iEpgUid > 0)) { // Don't delete this timer, but disable it only PVR_TIMER disableMe = timer; disableMe.state = PVR_TIMER_STATE_DISABLED; return UpdateTimer(disableMe); } cTimer mepotimer(timer); snprintf(command, 256, "DeleteSchedule:%i\n", mepotimer.Index()); KODI->Log(LOG_DEBUG, "DeleteTimer: About to delete MediaPortal schedule index=%i", mepotimer.Index()); result = SendCommand(command); if(result.find("True") == string::npos) { KODI->Log(LOG_DEBUG, "DeleteTimer %i [failed]", mepotimer.Index()); return PVR_ERROR_FAILED; } KODI->Log(LOG_DEBUG, "DeleteTimer %i [done]", mepotimer.Index()); // Although Kodi deletes this timer, we still have to trigger Kodi to update its timer list to // remove the timer from the Kodi list PVR->TriggerTimerUpdate(); return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::UpdateTimer(const PVR_TIMER &timerinfo) { string result; #ifdef _TIME32_T_DEFINED KODI->Log(LOG_DEBUG, "->UpdateTimer Index: %i Channel: %i, starttime: %i endtime: %i program: %s", timerinfo.iClientIndex, timerinfo.iClientChannelUid, timerinfo.startTime, timerinfo.endTime, timerinfo.strTitle); #else KODI->Log(LOG_DEBUG, "->UpdateTimer Channel: %i, 64 bit times not yet supported!", timerinfo.iClientChannelUid); #endif if (!IsUp()) return PVR_ERROR_SERVER_ERROR; cTimer timer(timerinfo); result = SendCommand(timer.UpdateScheduleCommand()); if(result.find("True") == string::npos) { KODI->Log(LOG_DEBUG, "UpdateTimer for channel: %i [failed]", timerinfo.iClientChannelUid); return PVR_ERROR_FAILED; } KODI->Log(LOG_DEBUG, "UpdateTimer for channel: %i [done]", timerinfo.iClientChannelUid); // Although Kodi changes this timer, we still have to trigger Kodi to update its timer list to // see the timer changes at the Kodi side PVR->TriggerTimerUpdate(); return PVR_ERROR_NO_ERROR; } /************************************************************/ /** Live stream handling */ // The MediaPortal TV Server uses rtsp streams which Kodi can handle directly // via the dvdplayer (ffmpeg) so we don't need to open the streams in this // pvr addon. // However, we still need to request the stream URL for the channel we want // to watch as it is not known on beforehand. // Most of the times it is the same URL for each selected channel. Only the // stream itself changes. Example URL: rtsp://tvserverhost/stream2.0 // The number 2.0 may change when the tvserver is streaming multiple tv channels // at the same time. // // The rtsp code from ffmpeg does not function well enough for this addon. // Therefore the new TSReader version uses the Live555 library here to open rtsp // urls or it can read directly from the timeshift buffer file. bool cPVRClientMediaPortal::OpenLiveStream(const PVR_CHANNEL &channelinfo) { string result; char command[256] = ""; const char* sResolveRTSPHostname = booltostring(g_bResolveRTSPHostname); vector timeshiftfields; KODI->Log(LOG_NOTICE, "Open Live stream for channel uid=%i", channelinfo.iUniqueId); if (!IsUp()) { m_iCurrentChannel = -1; m_bTimeShiftStarted = false; m_bSkipCloseLiveStream = false; //initialization m_signalStateCounter = 0; KODI->Log(LOG_ERROR, "Open Live stream failed. No connection to backend."); return false; } if (((int)channelinfo.iUniqueId) == m_iCurrentChannel) { KODI->Log(LOG_NOTICE, "New channel uid equal to the already streaming channel. Skipping re-tune."); return true; } 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; m_bSkipCloseLiveStream = false; //initialization // Start the timeshift // Use the optimized TimeshiftChannel call (don't stop a running timeshift) snprintf(command, 256, "TimeshiftChannel:%i|%s|False\n", channelinfo.iUniqueId, sResolveRTSPHostname); result = SendCommand(command); if (result.find("ERROR") != std::string::npos || result.length() == 0) { KODI->Log(LOG_ERROR, "Could not start the timeshift for channel uid=%i. Reason: %s", channelinfo.iUniqueId, result.c_str()); if (g_iTVServerKodiBuild>=109) { Tokenize(result, timeshiftfields, "|"); //[0] = string error message //[1] = TvResult (optional field. SendCommand can also return a timeout) if(timeshiftfields.size()>1) { //For TVServer 1.2.1: //enum TvResult //{ // Succeeded = 0, (this is not an error) // AllCardsBusy = 1, // ChannelIsScrambled = 2, // NoVideoAudioDetected = 3, // NoSignalDetected = 4, // UnknownError = 5, // UnableToStartGraph = 6, // UnknownChannel = 7, // NoTuningDetails = 8, // ChannelNotMappedToAnyCard = 9, // CardIsDisabled = 10, // ConnectionToSlaveFailed = 11, // NotTheOwner = 12, // GraphBuildingFailed = 13, // SWEncoderMissing = 14, // NoFreeDiskSpace = 15, // NoPmtFound = 16, //}; int tvresult = atoi(timeshiftfields[1].c_str()); // Display one of the localized error messages 30060-30075 KODI->QueueNotification(QUEUE_ERROR, KODI->GetLocalizedString(30059 + tvresult)); } else { KODI->QueueNotification(QUEUE_ERROR, result.c_str()); } } else { if (result.find("[ERROR]: TVServer answer: ") != std::string::npos) { //Skip first part: "[ERROR]: TVServer answer: " KODI->QueueNotification(QUEUE_ERROR, "TVServer: %s", result.substr(26).c_str()); } else { //Skip first part: "[ERROR]: " KODI->QueueNotification(QUEUE_ERROR, result.substr(7).c_str()); } } m_iCurrentChannel = -1; if (m_tsreader != nullptr) { SAFE_DELETE(m_tsreader); } return false; } else { Tokenize(result, timeshiftfields, "|"); if(timeshiftfields.size()<4) { KODI->Log(LOG_ERROR, "OpenLiveStream: Field count mismatch (<4). Data: %s\n", result.c_str()); m_iCurrentChannel = -1; return false; } //[0] = rtsp url //[1] = original (unresolved) rtsp url //[2] = timeshift buffer filename //[3] = card id (TVServerKodi build >= 106) //[4] = tsbuffer pos (TVServerKodi build >= 110) //[5] = tsbuffer file nr (TVServerKodi build >= 110) m_PlaybackURL = timeshiftfields[0]; if (g_eStreamingMethod == TSReader) { KODI->Log(LOG_NOTICE, "Channel timeshift buffer: %s", timeshiftfields[2].c_str()); if (channelinfo.bIsRadio) { usleep(100000); // 100 ms sleep to allow the buffer to fill } } else { KODI->Log(LOG_NOTICE, "Channel stream URL: %s", m_PlaybackURL.c_str()); } if (g_iSleepOnRTSPurl > 0) { KODI->Log(LOG_NOTICE, "Sleeping %i ms before opening stream: %s", g_iSleepOnRTSPurl, timeshiftfields[0].c_str()); usleep(g_iSleepOnRTSPurl * 1000); } // Check the returned stream URL. When the URL is an rtsp stream, we need // to close it again after watching to stop the timeshift. // A radio web stream (added to the TV Server) will return the web stream // URL without starting a timeshift. if(timeshiftfields[0].compare(0,4, "rtsp") == 0) { m_bTimeShiftStarted = true; } if (g_eStreamingMethod == TSReader) { if (m_tsreader != NULL) { bool bReturn = false; // Continue with the existing TsReader. KODI->Log(LOG_NOTICE, "Re-using existing TsReader..."); //if(g_bDirectTSFileRead) if(g_bUseRTSP == false) { m_tsreader->SetCardId(atoi(timeshiftfields[3].c_str())); if ((g_iTVServerKodiBuild >=110) && (timeshiftfields.size()>=6)) bReturn = m_tsreader->OnZap(timeshiftfields[2].c_str(), atoll(timeshiftfields[4].c_str()), atol(timeshiftfields[5].c_str())); else bReturn = m_tsreader->OnZap(timeshiftfields[2].c_str(), -1, -1); } else { // RTSP url KODI->Log(LOG_NOTICE, "Skipping OnZap for TSReader RTSP"); bReturn = true; //Fast forward seek (OnZap) does not work for RTSP } if (bReturn) { m_iCurrentChannel = (int) channelinfo.iUniqueId; m_iCurrentCard = atoi(timeshiftfields[3].c_str()); m_bCurrentChannelIsRadio = channelinfo.bIsRadio; } else { KODI->Log(LOG_ERROR, "Re-using the existing TsReader failed."); CloseLiveStream(); } return bReturn; } else { KODI->Log(LOG_NOTICE, "Creating a new TsReader..."); m_tsreader = new CTsReader(); } if (!g_bUseRTSP) { // Reading directly from the Timeshift buffer m_tsreader->SetCardSettings(&m_cCards); m_tsreader->SetCardId(atoi(timeshiftfields[3].c_str())); //if (g_szTimeshiftDir.length() > 0) // m_tsreader->SetDirectory(g_szTimeshiftDir); if ( m_tsreader->Open(timeshiftfields[2].c_str()) != S_OK ) { KODI->Log(LOG_ERROR, "Cannot open timeshift buffer %s", timeshiftfields[2].c_str()); CloseLiveStream(); return false; } } else { // use the RTSP url and live555 if ( m_tsreader->Open(timeshiftfields[0].c_str()) != S_OK) { KODI->Log(LOG_ERROR, "Cannot open channel url %s", timeshiftfields[0].c_str()); CloseLiveStream(); return false; } usleep(400000); } } // at this point everything is ready for playback m_iCurrentChannel = (int) channelinfo.iUniqueId; m_iCurrentCard = atoi(timeshiftfields[3].c_str()); m_bCurrentChannelIsRadio = channelinfo.bIsRadio; } KODI->Log(LOG_NOTICE, "OpenLiveStream: success for channel id %i (%s) on card %i", m_iCurrentChannel, channelinfo.strChannelName, m_iCurrentCard); return true; } int cPVRClientMediaPortal::ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) { size_t read_wanted = iBufferSize; size_t read_done = 0; static int read_timeouts = 0; unsigned char* bufptr = pBuffer; //KODI->Log(LOG_DEBUG, "->ReadLiveStream(buf_size=%i)", buf_size); if (g_eStreamingMethod != TSReader) { KODI->Log(LOG_ERROR, "ReadLiveStream: this function should not be called in FFMPEG/RTSP mode. Use 'Reset the PVR database' to re-read the channel list"); return 0; } if (!m_tsreader) { KODI->Log(LOG_ERROR, "ReadLiveStream: failed. No open TSReader"); return -1; } if ((m_tsreader->State() == State_Paused) && (g_bUseRTSP)) { //KODI->Log(LOG_ERROR, "ReadLiveStream: failed. Stream is paused"); return 0; } while (read_done < static_cast(iBufferSize)) { read_wanted = iBufferSize - read_done; if (m_tsreader->Read(bufptr, read_wanted, &read_wanted) > 0) { usleep(20000); read_timeouts++; return static_cast(read_wanted); } read_done += read_wanted; if ( read_done < static_cast(iBufferSize) ) { if (read_timeouts > 200) { if (m_bCurrentChannelIsRadio == false || read_done == 0) { KODI->Log(LOG_NOTICE, "Kodi requested %u bytes, but the TSReader got only %lu bytes in 2 seconds", iBufferSize, read_done); } read_timeouts = 0; //TODO //if read_done == 0 then check if the backend is still timeshifting, //or retrieve the reason why the timeshifting was stopped/failed... return static_cast(read_done); } bufptr += read_wanted; read_timeouts++; usleep(10000); } } read_timeouts = 0; return static_cast(read_done); } void cPVRClientMediaPortal::CloseLiveStream(void) { string result; if (!IsUp()) return; if (m_bTimeShiftStarted && !m_bSkipCloseLiveStream) { if (g_eStreamingMethod == TSReader && m_tsreader) { m_tsreader->Close(); SAFE_DELETE(m_tsreader); } result = SendCommand("StopTimeshift:\n"); KODI->Log(LOG_NOTICE, "CloseLiveStream: %s", result.c_str()); m_bTimeShiftStarted = false; m_iCurrentChannel = -1; m_iCurrentCard = -1; m_PlaybackURL.clear(); m_signalStateCounter = 0; } } long long cPVRClientMediaPortal::SeekLiveStream(long long iPosition, int iWhence) { if (g_eStreamingMethod == ffmpeg || !m_tsreader) { KODI->Log(LOG_ERROR, "SeekLiveStream: is not supported in FFMPEG/RTSP mode."); return -1; } if (iPosition == 0 && iWhence == SEEK_CUR) { return m_tsreader->GetFilePointer(); } return m_tsreader->SetFilePointer(iPosition, iWhence); } long long cPVRClientMediaPortal::LengthLiveStream(void) { if (g_eStreamingMethod == ffmpeg || !m_tsreader) { return -1; } return m_tsreader->GetFileSize(); } bool cPVRClientMediaPortal::IsRealTimeStream(void) { return m_bTimeShiftStarted; } PVR_ERROR cPVRClientMediaPortal::SignalStatus(PVR_SIGNAL_STATUS &signalStatus) { if (g_iTVServerKodiBuild < 108 || (m_iCurrentChannel == -1)) { // Not yet supported or playing webstream return PVR_ERROR_NO_ERROR; } string result; // Limit the GetSignalQuality calls to once every 10 s if (m_signalStateCounter == 0) { // Request the signal quality for the current streaming card from the backend result = SendCommand("GetSignalQuality\n"); if (result.length() > 0) { int signallevel = 0; int signalquality = 0; // Fetch the signal level and SNR values from the result string if (sscanf(result.c_str(),"%5i|%5i", &signallevel, &signalquality) == 2) { m_iSignal = (int) (signallevel * 655.35); // 100% is 0xFFFF 65535 m_iSNR = (int) (signalquality * 655.35); // 100% is 0xFFFF 65535 } } } m_signalStateCounter++; if (m_signalStateCounter > 10) m_signalStateCounter = 0; signalStatus.iSignal = m_iSignal; signalStatus.iSNR = m_iSNR; signalStatus.iBER = m_signalStateCounter; PVR_STRCPY(signalStatus.strAdapterStatus, "timeshifting"); // hardcoded for now... if (m_iCurrentCard >= 0) { // Try to determine the name of the tv/radio card from the local card cache Card currentCard; if (m_cCards.GetCard(m_iCurrentCard, currentCard) == true) { PVR_STRCPY(signalStatus.strAdapterName, currentCard.Name.c_str()); return PVR_ERROR_NO_ERROR; } } PVR_STRCLR(signalStatus.strAdapterName); return PVR_ERROR_NO_ERROR; } /************************************************************/ /** Record stream handling */ // MediaPortal recordings are also rtsp streams. Main difference here with // respect to the live tv streams is that the URLs for the recordings // can be requested on beforehand (done in the TVServerKodi plugin). bool cPVRClientMediaPortal::OpenRecordedStream(const PVR_RECORDING &recording) { KODI->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) { KODI->Log(LOG_ERROR, "Addon is in 'ffmpeg' mode. Kodi should play the RTSP url directly. Please reset your Kodi PVR database!"); return false; } std::string recfile = ""; cRecording* myrecording = GetRecordingInfo(recording); if (!myrecording) { return false; } if (!g_bUseRTSP) { recfile = myrecording->FilePath(); if (recfile.empty()) { KODI->Log(LOG_ERROR, "Backend returned an empty recording filename for recording id %s.", recording.strRecordingId); recfile = myrecording->Stream(); if (!recfile.empty()) { KODI->Log(LOG_NOTICE, "Trying to use the recording RTSP stream URL name instead."); } } } else { recfile = myrecording->Stream(); if (recfile.empty()) { KODI->Log(LOG_ERROR, "Backend returned an empty RTSP stream URL for recording id %s.", recording.strRecordingId); recfile = myrecording->FilePath(); if (!recfile.empty()) { KODI->Log(LOG_NOTICE, "Trying to use the filename instead."); } } } if (recfile.empty()) { KODI->Log(LOG_ERROR, "Recording playback not possible. Backend returned an empty filename and no RTSP stream URL for recording id %s", recording.strRecordingId); KODI->QueueNotification(QUEUE_ERROR, KODI->GetLocalizedString(30052)); // Tell Kodi to re-read the list with recordings to remove deleted/non-existing recordings as a result of backend auto-deletion. PVR->TriggerRecordingUpdate(); 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) { if (!IsUp() || g_eStreamingMethod == ffmpeg) return; if (m_tsreader) { KODI->Log(LOG_NOTICE, "CloseRecordedStream: Stop TSReader..."); m_tsreader->Close(); SAFE_DELETE(m_tsreader); } else { KODI->Log(LOG_DEBUG, "CloseRecordedStream: Nothing to do."); } } int cPVRClientMediaPortal::ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize) { size_t read_wanted = static_cast(iBufferSize); size_t read_done = 0; unsigned char* bufptr = pBuffer; if (g_eStreamingMethod == ffmpeg) return -1; while (read_done < static_cast(iBufferSize)) { read_wanted = iBufferSize - read_done; if (!m_tsreader) return -1; if (m_tsreader->Read(bufptr, read_wanted, &read_wanted) > 0) { usleep(20000); return static_cast(read_wanted); } read_done += read_wanted; if ( read_done < static_cast(iBufferSize) ) { bufptr += read_wanted; usleep(20000); } } return static_cast(read_done); } long long cPVRClientMediaPortal::SeekRecordedStream(long long iPosition, int iWhence) { if (g_eStreamingMethod == ffmpeg || !m_tsreader) { return -1; } #ifdef _DEBUG KODI->Log(LOG_DEBUG, "SeekRec: Current pos %lli", m_tsreader->GetFilePointer()); #endif KODI->Log(LOG_DEBUG,"SeekRec: iWhence %i pos %lli", iWhence, iPosition); return m_tsreader->SetFilePointer(iPosition, iWhence); } long long cPVRClientMediaPortal::LengthRecordedStream(void) { if (g_eStreamingMethod == ffmpeg || !m_tsreader) { return -1; } return m_tsreader->GetFileSize(); } PVR_ERROR cPVRClientMediaPortal::GetRecordingStreamProperties(const PVR_RECORDING* recording, PVR_NAMED_VALUE* properties, unsigned int* iPropertiesCount) { // GetRecordingStreamProperties is called before OpenRecordedStream // If we return a stream URL here, Kodi will use its internal player to open the stream and bypass the PVR addon *iPropertiesCount = 0; cRecording* myrecording = GetRecordingInfo(*recording); if (!myrecording) return PVR_ERROR_NO_ERROR; AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_MIMETYPE, "video/mp2t"); if (g_eStreamingMethod == ffmpeg) { AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_STREAMURL, myrecording->Stream()); } else if (!g_bUseRTSP) { if (myrecording->IsRecording() == false) { #ifdef TARGET_WINDOWS_DESKTOP if (OS::CFile::Exists(myrecording->FilePath())) { std::string recordingUri(ToKodiPath(myrecording->FilePath())); // Direct file playback by Kodi (without involving the addon) AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_STREAMURL, recordingUri.c_str()); } #endif } else { // Indicate that this is a real-time stream AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_ISREALTIMESTREAM, "true"); } } return PVR_ERROR_NO_ERROR; } PVR_ERROR cPVRClientMediaPortal::GetChannelStreamProperties(const PVR_CHANNEL* channel, PVR_NAMED_VALUE* properties, unsigned int* iPropertiesCount) { if ((channel == nullptr) || (properties == nullptr)) { return PVR_ERROR_FAILED; } *iPropertiesCount = 0; // Is this a webstream? try { cChannel& selectedChannel = m_channels.at(channel->iUniqueId); if (selectedChannel.IsWebstream()) { KODI->Log(LOG_DEBUG, "GetChannelStreamProperties (webstream) for uid=%i is '%s'", channel->iUniqueId, selectedChannel.URL()); AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_STREAMURL, selectedChannel.URL()); AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_ISREALTIMESTREAM, "true"); return PVR_ERROR_NO_ERROR; } } catch (const std::out_of_range& oor) { // channel not found in our plugin channel cache } if (g_eStreamingMethod == ffmpeg) { // GetChannelStreamProperties is called before OpenLiveStream by Kodi, so we should already open the stream here... // The actual call to OpenLiveStream will return immediately since we've already tuned the correct channel here. if (m_bTimeShiftStarted == true) { //CloseLiveStream(); } if (OpenLiveStream(*channel) == true) { if (!m_PlaybackURL.empty()) { KODI->Log(LOG_DEBUG, "GetChannelStreamProperties (ffmpeg) for uid=%i is '%s'", channel->iUniqueId, m_PlaybackURL.c_str()); AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_STREAMURL, m_PlaybackURL); AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_ISREALTIMESTREAM, "true"); AddStreamProperty(properties, iPropertiesCount, PVR_STREAM_PROPERTY_MIMETYPE, "video/mp2t"); return PVR_ERROR_NO_ERROR; } } } else if (g_eStreamingMethod == TSReader) { if ((m_bTimeShiftStarted == true) && (g_bFastChannelSwitch = true)) { // This ignores the next CloseLiveStream call from Kodi to speedup channel switching m_bSkipCloseLiveStream = true; } } else { KODI->Log(LOG_ERROR, "GetChannelStreamProperties for uid=%i returned no URL", channel->iUniqueId); } *iPropertiesCount = 0; return PVR_ERROR_NO_ERROR; } void cPVRClientMediaPortal::PauseStream(bool bPaused) { if (m_tsreader) m_tsreader->Pause(bPaused); } bool cPVRClientMediaPortal::CanPauseAndSeek() { if (m_tsreader) return true; else return false; } void cPVRClientMediaPortal::LoadGenreTable() { // Read the genre string to type/subtype translation file: if(g_bReadGenre) { string sGenreFile = g_szUserPath + PATH_SEPARATOR_CHAR + "resources" + PATH_SEPARATOR_CHAR + "genre_translation.xml"; if (!KODI->FileExists(sGenreFile.c_str(), false)) { sGenreFile = g_szUserPath + PATH_SEPARATOR_CHAR + "genre_translation.xml"; if (!KODI->FileExists(sGenreFile.c_str(), false)) { sGenreFile = g_szClientPath + PATH_SEPARATOR_CHAR + "resources" + PATH_SEPARATOR_CHAR + "genre_translation.xml"; } } m_genretable = new CGenreTable(sGenreFile); } } void cPVRClientMediaPortal::LoadCardSettings() { KODI->Log(LOG_DEBUG, "Loading card settings"); /* Retrieve card settings (needed for Live TV and recordings folders) */ vector lines; if ( SendCommand2("GetCardSettings\n", lines) ) { m_cCards.ParseLines(lines); } } void cPVRClientMediaPortal::SetConnectionState(PVR_CONNECTION_STATE newState) { if (newState != m_state) { KODI->Log(LOG_DEBUG, "Connection state changed to '%s'", GetConnectionStateString(newState)); m_state = newState; /* Notify connection state change (callback!) */ PVR->ConnectionStateChange(GetConnectionString(), m_state, NULL); } } const char* cPVRClientMediaPortal::GetConnectionStateString(PVR_CONNECTION_STATE state) const { switch (state) { case PVR_CONNECTION_STATE_SERVER_UNREACHABLE: return "Backend server is not reachable"; case PVR_CONNECTION_STATE_SERVER_MISMATCH: return "Backend server is reachable, but the expected type of server is not running"; case PVR_CONNECTION_STATE_VERSION_MISMATCH: return "Backend server is reachable, but the server version does not match client requirements"; case PVR_CONNECTION_STATE_ACCESS_DENIED: return "Backend server is reachable, but denies client access (e.g. due to wrong credentials)"; case PVR_CONNECTION_STATE_CONNECTED: return "Connection to backend server is established"; case PVR_CONNECTION_STATE_DISCONNECTED: return "No connection to backend server (e.g. due to network errors or client initiated disconnect)"; case PVR_CONNECTION_STATE_CONNECTING: return "Connecting to backend"; case PVR_CONNECTION_STATE_UNKNOWN: default: return "Unknown state"; } } cRecording* cPVRClientMediaPortal::GetRecordingInfo(const PVR_RECORDING & recording) { // Is this the same recording as the previous one? if (m_lastSelectedRecording) { int recId = atoi(recording.strRecordingId); if (m_lastSelectedRecording->Index() == recId) { return m_lastSelectedRecording; } SAFE_DELETE(m_lastSelectedRecording); } if (!IsUp()) return nullptr; string result; string command; command = StringUtils::Format("GetRecordingInfo:%s|%s|True|%s\n", recording.strRecordingId, ((g_bUseRTSP || g_eStreamingMethod == ffmpeg) ? "True" : "False"), g_bResolveRTSPHostname ? "True" : "False" ); result = SendCommand(command); uri::decode(result); if (result.empty()) { KODI->Log(LOG_ERROR, "Backend command '%s' returned a zero-length answer.", command.c_str()); return nullptr; } m_lastSelectedRecording = new cRecording(); if (!m_lastSelectedRecording->ParseLine(result)) { KODI->Log(LOG_ERROR, "Parsing result from '%s' command failed. Result='%s'.", command.c_str(), result.c_str()); return nullptr; } KODI->Log(LOG_NOTICE, "RECORDING: %s", result.c_str()); return m_lastSelectedRecording; } PVR_ERROR cPVRClientMediaPortal::GetStreamTimes(PVR_STREAM_TIMES* stream_times) { if (!m_bTimeShiftStarted && m_lastSelectedRecording) { // Recording playback // Warning: documentation in xbmc_pvr_types.h is wrong. pts values are not in seconds. stream_times->startTime = 0; // seconds stream_times->ptsStart = 0; // Unit must match Kodi's internal m_clock.GetClock() which is in useconds stream_times->ptsBegin = 0; // useconds stream_times->ptsEnd = ((int64_t)m_lastSelectedRecording->Duration()) * DVD_TIME_BASE; //useconds return PVR_ERROR_NO_ERROR; } else if (m_bTimeShiftStarted) { stream_times->startTime = m_tsreader->GetStartTime(); stream_times->ptsStart = 0; // Unit must match Kodi's internal m_clock.GetClock() which is in useconds stream_times->ptsBegin = m_tsreader->GetPtsBegin(); // useconds stream_times->ptsEnd = m_tsreader->GetPtsEnd(); return PVR_ERROR_NO_ERROR; } *stream_times = { 0 }; return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR cPVRClientMediaPortal::GetStreamReadChunkSize(int* chunksize) { *chunksize = 32 * 1024; return PVR_ERROR_NO_ERROR; } void cPVRClientMediaPortal::AddStreamProperty(PVR_NAMED_VALUE* properties, unsigned int* propertiesCount, std::string name, std::string value) { PVR_STRCPY(properties[*propertiesCount].strName, name.c_str()); PVR_STRCPY(properties[*propertiesCount].strValue, value.c_str()); *propertiesCount = (*propertiesCount) + 1; } pvr.mediaportal.tvserver-3.5.18-Leia/src/pvrclient-mediaportal.h000066400000000000000000000136171346756700600247410ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include /* Master defines for client control */ #include "xbmc_pvr_types.h" /* Local includes */ #include "Socket.h" #include "Cards.h" #include "epg.h" #include "channels.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 { class CTsReader; } class cRecording; class cPVRClientMediaPortal: public P8PLATFORM::PreventCopy, public P8PLATFORM::CThread { public: /* Class interface */ cPVRClientMediaPortal(); ~cPVRClientMediaPortal(); /* Server handling */ ADDON_STATUS TryConnect(); void Disconnect(); bool IsUp(); /* General handling */ const char* GetBackendName(void); const char* GetBackendVersion(void); const char* GetConnectionString(void); PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed); PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset); /* EPG handling */ PVR_ERROR GetEpg(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart = 0, time_t iEnd = 0); /* Channel handling */ int GetNumChannels(void); PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio); /* Channel group handling */ int GetChannelGroupsAmount(void); PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio); PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group); /* Record handling **/ int GetNumRecordings(void); PVR_ERROR GetRecordings(ADDON_HANDLE handle); PVR_ERROR DeleteRecording(const PVR_RECORDING &recording); PVR_ERROR RenameRecording(const PVR_RECORDING &recording); PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING &recording, int count); PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition); int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording); /* Timer handling */ int GetNumTimers(void); PVR_ERROR GetTimers(ADDON_HANDLE handle); PVR_ERROR GetTimerInfo(unsigned int timernumber, PVR_TIMER &timer); PVR_ERROR AddTimer(const PVR_TIMER &timer); PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete = false); PVR_ERROR UpdateTimer(const PVR_TIMER &timer); PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int *size); /* Live stream handling */ bool OpenLiveStream(const PVR_CHANNEL &channel); void CloseLiveStream(); int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize); PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus); PVR_ERROR GetChannelStreamProperties(const PVR_CHANNEL* channel, PVR_NAMED_VALUE* properties, unsigned int* iPropertiesCount); long long SeekLiveStream(long long iPosition, int iWhence = SEEK_SET); long long LengthLiveStream(void); /* Record stream handling */ bool OpenRecordedStream(const PVR_RECORDING &recording); void CloseRecordedStream(void); int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize); long long SeekRecordedStream(long long iPosition, int iWhence = SEEK_SET); long long LengthRecordedStream(void); PVR_ERROR GetRecordingStreamProperties(const PVR_RECORDING* recording, PVR_NAMED_VALUE* properties, unsigned int* iPropertiesCount); /* Common stream handing functions */ bool CanPauseAndSeek(void); void PauseStream(bool bPaused); bool IsRealTimeStream(void); PVR_ERROR GetStreamReadChunkSize(int* chunksize); PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES* stream_times); protected: MPTV::Socket *m_tcpclient; private: /* TVServerKodi Listening Thread */ void* Process(void); PVR_CONNECTION_STATE Connect(bool updateConnectionState = true); void LoadGenreTable(void); void LoadCardSettings(void); void SetConnectionState(PVR_CONNECTION_STATE newState); cRecording* GetRecordingInfo(const PVR_RECORDING& recording); const char* GetConnectionStateString(PVR_CONNECTION_STATE state) const; void AddStreamProperty(PVR_NAMED_VALUE* properties, unsigned int* propertiesCount, std::string name, std::string value); int m_iCurrentChannel; int m_iCurrentCard; bool m_bCurrentChannelIsRadio; PVR_CONNECTION_STATE m_state; bool m_bStop; bool m_bTimeShiftStarted; bool m_bSkipCloseLiveStream; std::string m_ConnectionString; std::string m_PlaybackURL; std::string m_BackendName; std::string m_BackendVersion; int m_BackendUTCoffset; time_t m_BackendTime; CCards m_cCards; CGenreTable* m_genretable; P8PLATFORM::CMutex m_mutex; P8PLATFORM::CMutex m_connectionMutex; int64_t m_iLastRecordingUpdate; MPTV::CTsReader* m_tsreader; std::map m_channels; int m_signalStateCounter; int m_iSignal; int m_iSNR; cRecording* m_lastSelectedRecording; //Used for TV Server communication: std::string SendCommand(const char* command); std::string SendCommand(const std::string& command); bool SendCommand2(const std::string& command, std::vector& lines); }; pvr.mediaportal.tvserver-3.5.18-Leia/src/recordings.cpp000066400000000000000000000234651346756700600231300ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . * */ #include #include using namespace std; #include "recordings.h" #include "utils.h" #include "timers.h" #include "client.h" #include "DateTime.h" using namespace ADDON; cRecording::cRecording() : m_channelType(TvDatabase::ChannelType::Unknown) { m_duration = 0; m_Index = -1; m_cardSettings = NULL; m_channelID = PVR_CHANNEL_INVALID_UID; m_isRecording = false; m_genre_type = 0; m_genre_subtype = 0; m_genretable = NULL; m_scheduleID = 0; m_keepUntil = 0; m_timesWatched = 0; m_lastPlayedPosition = 0; } cRecording::~cRecording() { } void cRecording::SetCardSettings(CCards* cardSettings) { m_cardSettings = cardSettings; } bool cRecording::ParseLine(const std::string& data) { vector fields; Tokenize(data, fields, "|"); if( fields.size() >= 9 ) { //[0] index / mediaportal recording id //[1] start time //[2] end time //[3] channel name //[4] title //[5] description //[6] stream_url (resolved hostname if requested) //[7] filename (we can bypass rtsp streaming when XBMC and the TV server are on the same machine) //[8] keepUntilDate (DateTime) //[9] (optional) original stream_url when resolve hostnames is enabled //[10] keepUntil (int) //[11] episodeName (string) //[12] episodeNumber (string) //[13] episodePart (string) //[14] seriesNumber (string) //[15] scheduleID (int) //[16] genre (string) //[17] idchannel (int) //[18] isrecording (bool) //[19] timesWatched (int) //[20] stopTime (int) //[21] channelType (int) m_Index = atoi(fields[0].c_str()); if ( m_startTime.SetFromDateTime(fields[1]) == false ) { KODI->Log(LOG_ERROR, "%s: Unable to convert start time '%s' into date+time", __FUNCTION__, fields[1].c_str()); return false; } if ( m_endTime.SetFromDateTime(fields[2]) == false ) { KODI->Log(LOG_ERROR, "%s: Unable to convert end time '%s' into date+time", __FUNCTION__, fields[2].c_str()); return false; } m_duration = m_endTime - m_startTime; m_channelName = fields[3]; m_title = fields[4]; m_description = fields[5]; m_stream = fields[6]; m_filePath = fields[7]; // TODO: fill lifetime with data from MP TV Server // From the VDR documentation (VDR is used by Alwinus as basis for the XBMC // PVR framework: // "The lifetime (int) value corresponds to the the number of days (0..99) // a recording made through this timer is guaranteed to remain on disk // before it is automatically removed to free up space for a new recording. // Note that setting this parameter to very high values for all recordings // may soon fill up the entire disk and cause new recordings to fail due to // low disk space. The special value 99 means that this recording will live // forever, and a value of 0 means that this recording can be deleted any // time if a recording with a higher priority needs disk space." if ( m_keepUntilDate.SetFromDateTime(fields[8]) == false ) { // invalid date (or outside time_t boundaries) m_keepUntilDate = MPTV::cUndefinedDate; } if( m_filePath.length() > 0 ) { SplitFilePath(); } else { m_basePath = ""; m_fileName = ""; m_directory = ""; } if (fields.size() >= 10) // Since TVServerKodi 1.0.8.0 { m_originalurl = fields[9]; } else { m_originalurl = fields[6]; } if (fields.size() >= 16) // Since TVServerKodi 1.1.x.105 { m_keepUntil = atoi( fields[10].c_str() ); m_episodeName = fields[11]; m_episodeNumber = fields[12]; m_episodePart = fields[13]; m_seriesNumber = fields[14]; m_scheduleID = atoi( fields[15].c_str() ); } if (fields.size() >= 19) // Since TVServerKodi 1.2.x.111 { m_genre = fields[16]; m_channelID = atoi( fields[17].c_str() ); m_isRecording = stringtobool( fields[18] ); if (m_genretable) m_genretable->GenreToTypes(m_genre, m_genre_type, m_genre_subtype); if (fields.size() >= 20) // Since TVServerKodi 1.2.x.117 { m_timesWatched = atoi( fields[19].c_str() ); if (fields.size() >= 21) // Since TVServerKodi 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; } } } } return true; } else { KODI->Log(LOG_ERROR, "Recording information has not enough fields. At least 9 fields expected, got only %d fields.", fields.size()); return false; } } int cRecording::Lifetime(void) const { // margro: the meaning of the XBMC-PVR Lifetime field is undocumented. // Assuming that VDR is the source for this field: // The guaranteed lifetime (in days) of a recording created by this // timer. 0 means that this recording may be automatically deleted // at any time by a new recording with higher priority. 99 means // that this recording will never be automatically deleted. Any // number in the range 1...98 means that this recording may not be // automatically deleted in favour of a new recording, until the // given number of days since the start time of the recording has // passed by TvDatabase::KeepMethodType m_keepmethod = (TvDatabase::KeepMethodType) m_keepUntil; switch (m_keepmethod) { case TvDatabase::UntilSpaceNeeded: //until space needed case TvDatabase::UntilWatched: //until watched return 0; break; case TvDatabase::TillDate: //until keepdate { int diffseconds = m_keepUntilDate - m_startTime; int daysremaining = (int)(diffseconds / cSecsInDay); // Calculate value in the range 1...98, based on m_keepdate if ((daysremaining < MAXLIFETIME) && (daysremaining >= 0)) { return daysremaining; } else { // > 98 days => return forever return MAXLIFETIME; } } break; case TvDatabase::Always: //forever return MAXLIFETIME; default: return MAXLIFETIME; } } void cRecording::SplitFilePath(void) { size_t found = string::npos; // Try to find the base path used for this recording by searching for the // card recording folder name in the the recording file name. if ((m_cardSettings) && (m_cardSettings->size() > 0)) { for (CCards::iterator it = m_cardSettings->begin(); it < m_cardSettings->end(); ++it) { // Determine whether the first part of the recording file name is shared with this card // Minimal name length of the RecordingFolder should be 3 (drive letter + :\) if (it->RecordingFolder.length() >= 3) { found = m_filePath.find(it->RecordingFolder); if (found != string::npos) { m_basePath = it->RecordingFolder; if (m_basePath.at(m_basePath.length() - 1) != '\\') m_basePath += "\\"; // Remove the base path m_fileName = m_filePath.substr(it->RecordingFolder.length()+1); // Extract subdirectories below the base path size_t found2 = m_fileName.find_last_of("/\\"); if (found2 != string::npos) { m_directory = m_fileName.substr(0, found2); m_fileName = m_fileName.substr(found2+1); } else { m_directory = ""; } break; } } } } if (found == string::npos) { m_fileName = m_filePath; m_directory = ""; m_basePath = ""; } } void cRecording::SetGenreTable(CGenreTable* genretable) { m_genretable = genretable; } time_t cRecording::StartTime(void) const { time_t time = m_startTime.GetAsTime(); return time; } int cRecording::Duration(void) const { if (m_isRecording) { MPTV::CDateTime endTime = MPTV::CDateTime::Now(); int diff = endTime - m_startTime - 10; if (diff > 0) { return diff; } else { return 0; } } else { return m_duration; } } int cRecording::GetSeriesNumber(void) const { // From xbmc_pvr_types.h: // series number (usually called season). // Set to "0" for specials/pilot. // For 'invalid' see iEpisodeNumber or set to -1 if (m_seriesNumber.empty()) return -1; return atoi(m_seriesNumber.c_str()); } int cRecording::GetEpisodeNumber(void) const { // From xbmc_pvr_types.h: // episode number within the "iSeriesNumber" season. // For 'invalid' set to -1 or iSeriesNumber=iEpisodeNumber=0 // to show both are invalid */ if (m_episodeNumber.empty()) return -1; 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; } } pvr.mediaportal.tvserver-3.5.18-Leia/src/recordings.h000066400000000000000000000124011346756700600225610ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . */ #include #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 #define MAXLIFETIME 99 //Based on VDR addon and VDR documentation. 99=Keep forever, 0=can be deleted at any time, 1..98=days to keep class cRecording { private: int m_Index; int m_channelID; std::string m_channelName; std::string m_filePath; ///< The full recording path as returned by the backend std::string m_basePath; ///< The base path shared by all recordings (to be determined from the Card settings) std::string m_directory; ///< An optional subdirectory below the basePath std::string m_fileName; ///< The recording filename without path std::string m_stream; std::string m_originalurl; MPTV::CDateTime m_startTime; MPTV::CDateTime m_endTime; int m_duration; ///< Duration of the recording in seconds 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; int m_scheduleID; int m_keepUntil; MPTV::CDateTime m_keepUntilDate; ///< MediaPortal keepUntilDate CCards* m_cardSettings; ///< Pointer to the MediaPortal card settings. Will be used to determine the base path of the recordings std::string m_genre; int m_genre_type; int m_genre_subtype; bool m_isRecording; CGenreTable* m_genretable; int m_timesWatched; int m_lastPlayedPosition; int m_channelType; public: cRecording(); virtual ~cRecording(); bool ParseLine(const std::string& data); const char *ChannelName(void) const { return m_channelName.c_str(); } int Index(void) const { return m_Index; } time_t StartTime(void) const; int Duration(void) const; const char *Title(void) const { return m_title.c_str(); } const char *Description(void) const { return m_description.c_str(); } const char *EpisodeName(void) const { return m_episodeName.c_str(); } const char *SeriesNumber(void) const { return m_seriesNumber.c_str(); } const char *EpisodeNumber(void) const { return m_episodeNumber.c_str(); } const char *EpisodePart(void) const { return m_episodePart.c_str(); } int ScheduleID(void) const { return m_scheduleID; } int Lifetime(void) const; 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) */ const char *FilePath(void) const { return m_filePath.c_str(); } /** * \brief Filename of this recording without full path * \return Filename */ const char *FileName(void) const { return m_fileName.c_str(); } /** * \brief Directory where this recording is stored (at server side) * \return Filename */ const char *Directory(void) const { return m_directory.c_str(); } /** * \brief Override the directory where this recording is stored */ //void SetDirectory( std::string& directory ); /** * \brief The RTSP stream URL for this recording (hostname resolved to IP-address) * \return Stream URL */ const char *Stream(void) const { return m_stream.c_str(); } /** * \brief The RTSP stream URL for this recording (unresolved hostname) * \return Stream URL */ const char *OrignalURL(void) const { return m_originalurl.c_str(); } /** * \brief Pass a pointer to the MediaPortal card settings to this class * \param the cardSettings */ void SetCardSettings(CCards* cardSettings); /** * \brief Parse Recording file path and divide it in 3 parts: base path, subdirectory and filename; */ void SplitFilePath(void); int GenreType(void) const { return m_genre_type; } int GenreSubType(void) const { return m_genre_subtype; } void SetGenreTable(CGenreTable* genremap); const char* GetGenre(void) const { return m_genre.c_str(); } /** * \brief Returns the series number as an integer value. Returns -1 when this field is empty. */ int GetSeriesNumber(void) const; /** * \brief Returns the episode number as an integer value. Returns -1 when this field is empty. */ int GetEpisodeNumber(void) const; }; pvr.mediaportal.tvserver-3.5.18-Leia/src/timers.cpp000066400000000000000000000575371346756700600223030ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . * */ #include #include #include using namespace std; #include "p8-platform/os.h" //needed for snprintf #include "client.h" #include "timers.h" #include "utils.h" #include "DateTime.h" #include "epg.h" using namespace ADDON; using namespace MPTV; cTimer::cTimer() : m_keepDate(cUndefinedDate), m_canceled(cUndefinedDate) { m_index = PVR_TIMER_NO_CLIENT_INDEX; m_active = true; m_channel = PVR_CHANNEL_INVALID_UID; m_schedtype = TvDatabase::Once; m_priority = 0; m_keepmethod = TvDatabase::UntilSpaceNeeded; m_prerecordinterval = -1; // Use MediaPortal setting instead m_postrecordinterval = -1; // Use MediaPortal setting instead m_series = false; m_done = false; m_ismanual = false; m_isrecording = false; m_progid = (EPG_TAG_INVALID_UID - cKodiEpgIndexOffset); m_genretable = NULL; m_parentScheduleID = MPTV_NO_PARENT_SCHEDULE; } cTimer::cTimer(const PVR_TIMER& timerinfo): m_genretable(NULL) { m_index = timerinfo.iClientIndex - cKodiTimerIndexOffset; m_progid = timerinfo.iEpgUid - cKodiEpgIndexOffset; m_parentScheduleID = timerinfo.iParentClientIndex - cKodiTimerIndexOffset; if (m_index >= MPTV_REPEAT_NO_SERIES_OFFSET) { m_index = m_parentScheduleID; } m_done = (timerinfo.state == PVR_TIMER_STATE_COMPLETED); m_active = (timerinfo.state == PVR_TIMER_STATE_SCHEDULED || timerinfo.state == PVR_TIMER_STATE_RECORDING || timerinfo.state == PVR_TIMER_STATE_CONFLICT_OK || timerinfo.state == PVR_TIMER_STATE_CONFLICT_NOK); if (!m_active) { // Don't know when it was cancelled, so assume that it was canceled now... // backend (TVServerKodi) will only update the canceled date time when // this schedule was just canceled m_canceled = CDateTime::Now(); } else { m_canceled = cUndefinedDate; } m_title = timerinfo.strTitle; m_directory = timerinfo.strDirectory; m_channel = timerinfo.iClientChannelUid; if (timerinfo.startTime <= 0) { // Instant timer has starttime = 0, so set current time as starttime. m_startTime = CDateTime::Now(); m_ismanual = true; } else { m_startTime = timerinfo.startTime; m_ismanual = false; } m_endTime = timerinfo.endTime; //m_firstday = timerinfo.firstday; m_isrecording = (timerinfo.state == PVR_TIMER_STATE_RECORDING); m_priority = XBMC2MepoPriority(timerinfo.iPriority); SetKeepMethod(timerinfo.iLifetime); m_schedtype = static_cast(timerinfo.iTimerType - cKodiTimerTypeOffset); if (m_schedtype == TvDatabase::KodiManual) { m_schedtype = TvDatabase::Once; } if ((m_schedtype == TvDatabase::Once) && (timerinfo.iWeekdays != PVR_WEEKDAY_NONE)) // huh, still repeating? { // Select the correct schedule type m_schedtype = RepeatFlags2SchedRecType(timerinfo.iWeekdays); } m_series = (m_schedtype == TvDatabase::Once) ? false : true; m_prerecordinterval = timerinfo.iMarginStart; m_postrecordinterval = timerinfo.iMarginEnd; } cTimer::~cTimer() { } /** * @brief Fills the PVR_TIMER struct with information from this timer * @param tag A reference to the PVR_TIMER struct */ void cTimer::GetPVRtimerinfo(PVR_TIMER &tag) { memset(&tag, 0, sizeof(tag)); if (m_parentScheduleID != MPTV_NO_PARENT_SCHEDULE) { /* Hack: use a different client index for Kodi since it does not accept multiple times the same schedule id * This means that all programs scheduled via a series schedule in MediaPortal will get a different client * index in Kodi. The iParentClientIndex will still point to the original id_Schedule in MediaPortal */ tag.iClientIndex = cKodiTimerIndexOffset + MPTV_REPEAT_NO_SERIES_OFFSET + cKodiEpgIndexOffset + m_progid; } else { tag.iClientIndex = cKodiTimerIndexOffset + m_index; } tag.iEpgUid = cKodiEpgIndexOffset + m_progid; if (IsRecording()) tag.state = PVR_TIMER_STATE_RECORDING; else if (m_active) tag.state = PVR_TIMER_STATE_SCHEDULED; else tag.state = PVR_TIMER_STATE_DISABLED; if (m_schedtype == TvDatabase::EveryTimeOnEveryChannel) { tag.iClientChannelUid = PVR_TIMER_ANY_CHANNEL; } else { tag.iClientChannelUid = m_channel; } PVR_STRCPY(tag.strTitle, m_title.c_str()); tag.startTime = m_startTime.GetAsTime(); tag.endTime = m_endTime.GetAsTime(); // From the VDR manual // firstday: The date of the first day when this timer shall start recording // (only available for repeating timers). if(Repeat()) { if (m_parentScheduleID != MPTV_NO_PARENT_SCHEDULE) { tag.firstDay = 0; tag.iParentClientIndex = (unsigned int)(cKodiTimerIndexOffset + m_parentScheduleID); tag.iWeekdays = PVR_WEEKDAY_NONE; tag.iTimerType = cKodiTimerTypeOffset + (int) TvDatabase::Once; tag.iClientChannelUid = m_channel; } else { tag.firstDay = m_startTime.GetAsTime(); tag.iParentClientIndex = PVR_TIMER_NO_PARENT; tag.iWeekdays = RepeatFlags(); tag.iTimerType = cKodiTimerTypeOffset + (int) m_schedtype; } } else { tag.firstDay = 0; tag.iParentClientIndex = PVR_TIMER_NO_PARENT; tag.iWeekdays = RepeatFlags(); tag.iTimerType = cKodiTimerTypeOffset + (int) m_schedtype; } tag.iPriority = Priority(); tag.iLifetime = GetLifetime(); tag.iMarginStart = m_prerecordinterval; tag.iMarginEnd = m_postrecordinterval; if (m_genretable) { // genre string to genre type/subtype mapping int genreType; int genreSubType; m_genretable->GenreToTypes(m_genre, genreType, genreSubType); tag.iGenreType = genreType; tag.iGenreSubType = genreSubType; } else { tag.iGenreType = 0; tag.iGenreSubType = 0; } PVR_STRCPY(tag.strDirectory, m_directory.c_str()); PVR_STRCPY(tag.strSummary, m_description.c_str()); } time_t cTimer::StartTime(void) const { time_t retVal = m_startTime.GetAsTime(); return retVal; } time_t cTimer::EndTime(void) const { time_t retVal = m_endTime.GetAsTime(); return retVal; } bool cTimer::ParseLine(const char *s) { vector schedulefields; string data = s; uri::decode(data); Tokenize(data, schedulefields, "|"); if (schedulefields.size() >= 10) { // field 0 = index // field 1 = start date + time // field 2 = end date + time // field 3 = channel nr // field 4 = channel name // field 5 = program name // field 6 = schedule recording type // field 7 = priority // field 8 = isdone (finished) // field 9 = ismanual // field 10 = directory // field 11 = keepmethod (0=until space needed, 1=until watched, 2=until keepdate, 3=forever) (TVServerKodi build >= 100) // field 12 = keepdate (2000-01-01 00:00:00 = infinite) (TVServerKodi build >= 100) // field 13 = preRecordInterval (TVServerKodi build >= 100) // field 14 = postRecordInterval (TVServerKodi build >= 100) // field 15 = canceled (TVServerKodi build >= 100) // field 16 = series (True/False) (TVServerKodi build >= 100) // field 17 = isrecording (True/False) // field 18 = program id (EPG) // field 19 = parent schedule id (TVServerKodi build >= 130) // field 20 = genre of the program (TVServerKodi build >= 130) // field 21 = program description (EPG) (TVServerKodi build >= 130) m_index = atoi(schedulefields[0].c_str()); if ( m_startTime.SetFromDateTime(schedulefields[1]) == false ) return false; if ( m_endTime.SetFromDateTime(schedulefields[2]) == false ) return false; m_channel = atoi(schedulefields[3].c_str()); m_title = schedulefields[5]; m_schedtype = (TvDatabase::ScheduleRecordingType) atoi(schedulefields[6].c_str()); m_priority = atoi(schedulefields[7].c_str()); m_done = stringtobool(schedulefields[8]); m_ismanual = stringtobool(schedulefields[9]); m_directory = schedulefields[10]; if(schedulefields.size() >= 18) { //TVServerKodi build >= 100 m_keepmethod = (TvDatabase::KeepMethodType) atoi(schedulefields[11].c_str()); if ( m_keepDate.SetFromDateTime(schedulefields[12]) == false ) return false; m_prerecordinterval = atoi(schedulefields[13].c_str()); m_postrecordinterval = atoi(schedulefields[14].c_str()); // The DateTime value 2000-01-01 00:00:00 means: active in MediaPortal if(schedulefields[15].compare("2000-01-01 00:00:00Z")==0) { m_canceled.SetFromTime(MPTV::cUndefinedDate); m_active = true; } else { if (m_canceled.SetFromDateTime(schedulefields[15]) == false) { m_canceled.SetFromTime(MPTV::cUndefinedDate); } m_active = false; } m_series = stringtobool(schedulefields[16]); m_isrecording = stringtobool(schedulefields[17]); } else { m_keepmethod = TvDatabase::UntilSpaceNeeded; m_keepDate = cUndefinedDate; m_prerecordinterval = -1; m_postrecordinterval = -1; m_canceled = cUndefinedDate; m_active = true; m_series = false; m_isrecording = false; } if(schedulefields.size() >= 19) m_progid = atoi(schedulefields[18].c_str()); else m_progid = (EPG_TAG_INVALID_UID - cKodiEpgIndexOffset); if (schedulefields.size() >= 22) { m_parentScheduleID = atoi(schedulefields[19].c_str()); m_genre = schedulefields[20]; m_description = schedulefields[21]; } else { m_parentScheduleID = MPTV_NO_PARENT_SCHEDULE; m_genre.clear(); m_description.clear(); } return true; } return false; } int cTimer::SchedRecType2RepeatFlags(TvDatabase::ScheduleRecordingType schedtype) { // This field contains a bitmask that corresponds to the days of the week at which this timer runs // It is based on the VDR Day field format "MTWTF--" // The format is a 1 bit for every enabled day and a 0 bit for a disabled day // Thus: WeekDays = "0000 0001" = "M------" (monday only) // "0110 0000" = "-----SS" (saturday and sunday) // "0001 1111" = "MTWTF--" (all weekdays) int weekdays = 0; switch (schedtype) { case TvDatabase::Once: case TvDatabase::KodiManual: weekdays = PVR_WEEKDAY_NONE; break; case TvDatabase::Daily: weekdays = PVR_WEEKDAY_ALLDAYS; // 0111 1111 break; case TvDatabase::Weekly: case TvDatabase::WeeklyEveryTimeOnThisChannel: { // Not sure what to do with these MediaPortal options... // Assumption: record once a week, on the same day and time // => determine weekday and set the corresponding bit int weekday = m_startTime.GetDayOfWeek(); //days since Sunday [0-6] // bit 0 = monday, need to convert weekday value to bitnumber: if (weekday == 0) weekday = 6; // sunday 0100 0000 else weekday--; weekdays = 1 << weekday; break; } case TvDatabase::EveryTimeOnThisChannel: // Don't know what to do with this MediaPortal option? weekdays = PVR_WEEKDAY_ALLDAYS; // 0111 1111 (daily) break; case TvDatabase::EveryTimeOnEveryChannel: // Don't know what to do with this MediaPortal option? weekdays = PVR_WEEKDAY_ALLDAYS; // 0111 1111 (daily) break; case TvDatabase::Weekends: // 0110 0000 weekdays = PVR_WEEKDAY_SATURDAY | PVR_WEEKDAY_SUNDAY; break; case TvDatabase::WorkingDays: // 0001 1111 weekdays = PVR_WEEKDAY_MONDAY | PVR_WEEKDAY_TUESDAY | PVR_WEEKDAY_WEDNESDAY | PVR_WEEKDAY_THURSDAY | PVR_WEEKDAY_FRIDAY; break; default: weekdays = PVR_WEEKDAY_NONE; } return weekdays; } TvDatabase::ScheduleRecordingType cTimer::RepeatFlags2SchedRecType(int repeatflags) { // margro: the meaning of the XBMC-PVR Weekdays field is undocumented. // Assuming that VDR is the source for this field: // This field contains a bitmask that corresponds to the days of the week at which this timer runs // It is based on the VDR Day field format "MTWTF--" // The format is a 1 bit for every enabled day and a 0 bit for a disabled day // Thus: WeekDays = "0000 0001" = "M------" (monday only) // "0110 0000" = "-----SS" (saturday and sunday) // "0001 1111" = "MTWTF--" (all weekdays) switch (repeatflags) { case PVR_WEEKDAY_NONE: return TvDatabase::Once; break; case PVR_WEEKDAY_MONDAY: case PVR_WEEKDAY_TUESDAY: case PVR_WEEKDAY_WEDNESDAY: case PVR_WEEKDAY_THURSDAY: case PVR_WEEKDAY_FRIDAY: case PVR_WEEKDAY_SATURDAY: case PVR_WEEKDAY_SUNDAY: return TvDatabase::Weekly; break; case (PVR_WEEKDAY_MONDAY | PVR_WEEKDAY_TUESDAY | PVR_WEEKDAY_WEDNESDAY | PVR_WEEKDAY_THURSDAY | PVR_WEEKDAY_FRIDAY): // 0001 1111 return TvDatabase::WorkingDays; case (PVR_WEEKDAY_SATURDAY | PVR_WEEKDAY_SUNDAY): // 0110 0000 return TvDatabase::Weekends; break; case PVR_WEEKDAY_ALLDAYS: // 0111 1111 return TvDatabase::Daily; break; default: break; } return TvDatabase::Once; } std::string cTimer::AddScheduleCommand() { char command[1024]; string startTime; string endTime; m_startTime.GetAsLocalizedTime(startTime); m_endTime.GetAsLocalizedTime(endTime); KODI->Log(LOG_DEBUG, "Start time: %s, marginstart: %i min earlier", startTime.c_str(), m_prerecordinterval); KODI->Log(LOG_DEBUG, "End time: %s, marginstop: %i min later", endTime.c_str(), m_postrecordinterval); snprintf(command, 1023, "AddSchedule:%i|%s|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i\n", m_channel, //Channel number [0] uri::encode(uri::PATH_TRAITS, m_title).c_str(), //Program title [1] m_startTime.GetYear(), m_startTime.GetMonth(), m_startTime.GetDay(), //Start date [2..4] m_startTime.GetHour(), m_startTime.GetMinute(), m_startTime.GetSecond(), //Start time [5..7] m_endTime.GetYear(), m_endTime.GetMonth(), m_endTime.GetDay(), //End date [8..10] m_endTime.GetHour(), m_endTime.GetMinute(), m_endTime.GetSecond(), //End time [11..13] (int) m_schedtype, m_priority, (int) m_keepmethod, //SchedType, Priority, keepMethod [14..16] m_keepDate.GetYear(), m_keepDate.GetMonth(), m_keepDate.GetDay(), //Keepdate [17..19] m_keepDate.GetHour(), m_keepDate.GetMinute(), m_keepDate.GetSecond(), //Keeptime [20..22] m_prerecordinterval, m_postrecordinterval); //Prerecord,postrecord [23,24] return command; } std::string cTimer::UpdateScheduleCommand() { char command[1024]; string startTime; string endTime; m_startTime.GetAsLocalizedTime(startTime); m_endTime.GetAsLocalizedTime(endTime); KODI->Log(LOG_DEBUG, "Start time: %s, marginstart: %i min earlier", startTime.c_str(), m_prerecordinterval); KODI->Log(LOG_DEBUG, "End time: %s, marginstop: %i min later", endTime.c_str(), m_postrecordinterval); snprintf(command, 1024, "UpdateSchedule:%i|%i|%i|%s|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i\n", m_index, //Schedule index [0] m_active, //Active [1] m_channel, //Channel number [2] uri::encode(uri::PATH_TRAITS,m_title).c_str(), //Program title [3] m_startTime.GetYear(), m_startTime.GetMonth(), m_startTime.GetDay(), //Start date [4..6] m_startTime.GetHour(), m_startTime.GetMinute(), m_startTime.GetSecond(), //Start time [7..9] m_endTime.GetYear(), m_endTime.GetMonth(), m_endTime.GetDay(), //End date [10..12] m_endTime.GetHour(), m_endTime.GetMinute(), m_endTime.GetSecond(), //End time [13..15] (int) m_schedtype, m_priority, (int) m_keepmethod, //SchedType, Priority, keepMethod [16..18] m_keepDate.GetYear(), m_keepDate.GetMonth(), m_keepDate.GetDay(), //Keepdate [19..21] m_keepDate.GetHour(), m_keepDate.GetMinute(), m_keepDate.GetSecond(), //Keeptime [22..24] m_prerecordinterval, m_postrecordinterval, m_progid); //Prerecord,postrecord,program_id [25,26,27] return command; } int cTimer::XBMC2MepoPriority(int UNUSED(xbmcprio)) { // From XBMC side: 0.99 where 0=lowest and 99=highest priority (like VDR). Default value: 50 // Meaning of the MediaPortal field is unknown to me. Default seems to be 0. // TODO: figure out the mapping return 0; } int cTimer::Mepo2XBMCPriority(int UNUSED(mepoprio)) { return 50; //Default value } /* * @brief Convert a Kodi Lifetime value to MediaPortals keepMethod+keepDate settings * @param lifetime the Kodi lifetime value (in days) * Should be called after setting m_starttime !! */ void cTimer::SetKeepMethod(int lifetime) { // Kodi keep methods: // negative values: => special methods like Until Space Needed, Always, Until Watched // positive values: days to keep the recording if (lifetime == 0) { m_keepmethod = TvDatabase::UntilSpaceNeeded; m_keepDate = cUndefinedDate; } else if (lifetime < 0) { m_keepmethod = (TvDatabase::KeepMethodType) -lifetime; m_keepDate = cUndefinedDate; } else { m_keepmethod = TvDatabase::TillDate; m_keepDate = m_startTime; m_keepDate += (lifetime * cSecsInDay); } } int cTimer::GetLifetime(void) { // lifetime of recordings created by this timer. // value > 0 = days after which recordings will be deleted by the backend, // value < 0 addon defined integer list reference, // value == 0 disabled switch (m_keepmethod) { case TvDatabase::UntilSpaceNeeded: //until space needed return -MPTV_KEEP_UNTIL_SPACE_NEEDED; break; case TvDatabase::UntilWatched: //until watched return -MPTV_KEEP_UNTIL_WATCHED; break; case TvDatabase::TillDate: //until keepdate { int diffseconds = m_keepDate - m_startTime; int daysremaining = (int)(diffseconds / cSecsInDay); // Calculate value in the range 1...98, based on m_keepdate return daysremaining; } break; case TvDatabase::Always: //forever return -MPTV_KEEP_ALWAYS; default: return 0; } } void cTimer::SetScheduleRecordingType(TvDatabase::ScheduleRecordingType schedType) { m_schedtype = schedType; } void cTimer::SetKeepMethod(TvDatabase::KeepMethodType keepmethod) { m_keepmethod = keepmethod; } void cTimer::SetPreRecordInterval(int minutes) { m_prerecordinterval = minutes; } void cTimer::SetPostRecordInterval(int minutes) { m_postrecordinterval = minutes; } void cTimer::SetGenreTable(CGenreTable* genretable) { m_genretable = genretable; } cLifeTimeValues::cLifeTimeValues() { /* Prepare the list with Lifetime values and descriptions */ // MediaPortal keep methods: m_lifetimeValues.push_back(std::make_pair(-MPTV_KEEP_ALWAYS, KODI->GetLocalizedString(30133))); m_lifetimeValues.push_back(std::make_pair(-MPTV_KEEP_UNTIL_SPACE_NEEDED, KODI->GetLocalizedString(30130))); m_lifetimeValues.push_back(std::make_pair(-MPTV_KEEP_UNTIL_WATCHED, KODI->GetLocalizedString(30131))); //Not directly supported by Kodi. I can add this, but there is no way to select the date //m_lifetimeValues.push_back(std::make_pair(TvDatabase::TillDate, KODI->GetLocalizedString(30132))); // MediaPortal Until date replacements: const char* strWeeks = KODI->GetLocalizedString(30137); // %d weeks const char* strMonths = KODI->GetLocalizedString(30139); // %d months const size_t cKeepStringLength = 255; char strKeepString[cKeepStringLength]; m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_ONE_WEEK, KODI->GetLocalizedString(30134))); snprintf(strKeepString, cKeepStringLength, strWeeks, 2); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_TWO_WEEKS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strWeeks, 3); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_THREE_WEEKS, strKeepString)); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_ONE_MONTH, KODI->GetLocalizedString(30138))); snprintf(strKeepString, cKeepStringLength, strMonths, 2); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_TWO_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 3); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_THREE_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 4); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_FOUR_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 5); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_FIVE_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 6); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_SIX_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 7); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_SEVEN_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 8); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_EIGHT_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 9); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_NINE_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 10); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_TEN_MONTHS, strKeepString)); snprintf(strKeepString, cKeepStringLength, strMonths, 11); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_ELEVEN_MONTHS, strKeepString)); m_lifetimeValues.push_back(std::make_pair(MPTV_KEEP_ONE_YEAR, KODI->GetLocalizedString(30140))); } void cLifeTimeValues::SetLifeTimeValues(PVR_TIMER_TYPE& timertype) { timertype.iLifetimesSize = static_cast(m_lifetimeValues.size()); timertype.iLifetimesDefault = -MPTV_KEEP_ALWAYS; //Negative = special types, positive values is days //select default keep method switch (g_KeepMethodType) { case TvDatabase::UntilSpaceNeeded: //until space needed timertype.iLifetimesDefault = -MPTV_KEEP_UNTIL_SPACE_NEEDED; //Negative = special types, positive values is days break; case TvDatabase::UntilWatched: //until watched timertype.iLifetimesDefault = -MPTV_KEEP_UNTIL_WATCHED; //Negative = special types, positive values is days break; case TvDatabase::TillDate: //until keepdate //use defaultrecordinglifetime value from settings.xml timertype.iLifetimesDefault = g_DefaultRecordingLifeTime; break; case TvDatabase::Always: //forever default: break; } int i = 0; std::vector>::iterator it; for (it = m_lifetimeValues.begin(); ((it != m_lifetimeValues.end()) && (i < PVR_ADDON_TIMERTYPE_VALUES_ARRAY_SIZE)); ++it, ++i) { timertype.lifetimes[i].iValue = it->first; PVR_STRCPY(timertype.lifetimes[i].strDescription, it->second.c_str()); } } namespace Timer { // Life time values for the recordings cLifeTimeValues* lifetimeValues = NULL; }; pvr.mediaportal.tvserver-3.5.18-Leia/src/timers.h000066400000000000000000000255031346756700600217340ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2013 Team Kodi * https://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1335 USA * http://www.gnu.org/copyleft/gpl.html * */ #ifndef __TIMERS_H #define __TIMERS_H #include "libXBMC_pvr.h" #include #include #include "DateTime.h" #include "GenreTable.h" namespace TvDatabase { // From MediaPortal: TvDatabase.ScheduleRecordingType enum ScheduleRecordingType { // English MediaPortal GUI description: Once = 0, // Record once Daily = 1, // Record every day at this time Weekly = 2, // Record every week at this time EveryTimeOnThisChannel = 3, // Record every time on this channel EveryTimeOnEveryChannel = 4, // Record every time on every channel Weekends = 5, // Record Weekends WorkingDays = 6, // Record Weekdays WeeklyEveryTimeOnThisChannel = 7, // Weekly on this channel KodiManual = 99 // Special type for Kodi since it distinguishes between 'Once (EPG based)' and 'manual' }; // From MediaPortal: TvDatabase.KeepMethodType enum KeepMethodType { UntilSpaceNeeded = 0, UntilWatched = 1, TillDate = 2, Always = 3 }; }; const int cSecsInDay = 86400; ///> Amount of seconds in one day const int cKodiTimerTypeOffset = (PVR_TIMER_TYPE_NONE + 1); ///> Offset used to map the ScheduleRecordingType onto the iTimerType values const int cKodiTimerIndexOffset = (PVR_TIMER_NO_CLIENT_INDEX + 1); ///> Offset used to map the MediaPortal schedule id's to the iClientIndex values #define MPTV_REPEAT_NO_SERIES_OFFSET 0x7FFFFFF #define MPTV_NO_PARENT_SCHEDULE -1 // Kodi Keep methods : // The defines below are uses as replacement for the MediaPortal Until date version since // we cannot have both data selection and the three above keep methods at the same time #define MPTV_KEEP_ONE_WEEK 7 #define MPTV_KEEP_TWO_WEEKS 14 #define MPTV_KEEP_THREE_WEEKS 21 #define MPTV_KEEP_ONE_MONTH 31 #define MPTV_KEEP_TWO_MONTHS 61 #define MPTV_KEEP_THREE_MONTHS 92 #define MPTV_KEEP_FOUR_MONTHS 122 #define MPTV_KEEP_FIVE_MONTHS 153 #define MPTV_KEEP_SIX_MONTHS 183 #define MPTV_KEEP_SEVEN_MONTHS 214 #define MPTV_KEEP_EIGHT_MONTHS 244 #define MPTV_KEEP_NINE_MONTHS 275 #define MPTV_KEEP_TEN_MONTHS 305 #define MPTV_KEEP_ELEVEN_MONTHS 336 #define MPTV_KEEP_ONE_YEAR 365 #define MPTV_KEEP_UNTIL_SPACE_NEEDED TvDatabase::UntilSpaceNeeded #define MPTV_KEEP_UNTIL_WATCHED TvDatabase::UntilWatched #define MPTV_KEEP_ALWAYS TvDatabase::Always // Kodi timer types const int MPTV_RECORD_ONCE = PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_TITLE_EPG_MATCH; //PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS; const int MPTV_RECORD_EVERY_TIME_ON_THIS_CHANNEL = PVR_TIMER_TYPE_IS_REPEATING | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_TITLE_EPG_MATCH; //PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS; const int MPTV_RECORD_EVERY_TIME_ON_EVERY_CHANNEL = PVR_TIMER_TYPE_IS_REPEATING | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_TITLE_EPG_MATCH | PVR_TIMER_TYPE_SUPPORTS_ANY_CHANNEL; //PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS; const int MPTV_RECORD_WEEKLY = PVR_TIMER_TYPE_IS_REPEATING | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_TITLE_EPG_MATCH; //PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS; const int MPTV_RECORD_DAILY = PVR_TIMER_TYPE_IS_REPEATING | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_TITLE_EPG_MATCH; //PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS; const int MPTV_RECORD_WORKING_DAYS = PVR_TIMER_TYPE_IS_REPEATING | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME; //PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS; const int MPTV_RECORD_WEEEKENDS = PVR_TIMER_TYPE_IS_REPEATING | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME; //PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS; const int MPTV_RECORD_WEEKLY_EVERY_TIME_ON_THIS_CHANNEL = PVR_TIMER_TYPE_IS_REPEATING | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_TITLE_EPG_MATCH; //PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS; const int MPTV_RECORD_MANUAL = PVR_TIMER_TYPE_IS_MANUAL | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | //PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME; class cLifeTimeValues { public: cLifeTimeValues(); void SetLifeTimeValues(PVR_TIMER_TYPE& timertype); private: std::vector> m_lifetimeValues; }; class cTimer { public: cTimer(); cTimer(const PVR_TIMER &timerinfo); virtual ~cTimer(); void GetPVRtimerinfo(PVR_TIMER &tag); int Index(void) const { return m_index; } unsigned int Channel(void) const { return m_channel; } int Priority(void) { return Mepo2XBMCPriority(m_priority); } const char* Title(void) const { return m_title.c_str(); } const char* Dir(void) const { return m_directory.c_str(); } time_t StartTime(void) const; time_t EndTime(void) const; bool ParseLine(const char *s); int PreRecordInterval(void) const { return m_prerecordinterval; } int PostRecordInterval(void) const { return m_postrecordinterval; } int RepeatFlags() { return SchedRecType2RepeatFlags(m_schedtype); }; bool Repeat() const { return (m_schedtype == TvDatabase::Once ? false : true); }; bool Done() const { return m_done; }; bool IsManual() const { return m_ismanual; }; bool IsActive() const { return (m_canceled==MPTV::cUndefinedDate); }; bool IsRecording() const { return m_isrecording; }; TvDatabase::ScheduleRecordingType RepeatFlags2SchedRecType(int repeatflags); 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); /** * @brief Convert a XBMC Lifetime value to MediaPortals keepMethod+keepDate settings * @param lifetime the XBMC lifetime value (in days) (following the VDR syntax) * Should be called after setting m_starttime !! */ void SetKeepMethod(int lifetime); int GetLifetime(void); int XBMC2MepoPriority(int xbmcprio); int Mepo2XBMCPriority(int mepoprio); // MediaPortal database fields: int m_index; ///> MediaPortal id_Schedule int m_channel; ///> MediaPortal idChannel TvDatabase::ScheduleRecordingType m_schedtype; ///> MediaPortal scheduleType std::string m_title; ///> MediaPortal programName MPTV::CDateTime m_startTime; ///> MediaPortal startTime MPTV::CDateTime m_endTime; ///> MediaPortal endTime // skipped: maxAirings field = episodes to keep int m_priority; ///> MediaPortal priority (not the XBMC one!!!) std::string m_directory; ///> MediaPortal directory // skipped: quality field TvDatabase::KeepMethodType m_keepmethod; ///> MediaPortal keepMethod MPTV::CDateTime m_keepDate; ///> MediaPortal keepDate int m_prerecordinterval; ///> MediaPortal preRecordInterval int m_postrecordinterval; ///> MediaPortal postRecordInterval MPTV::CDateTime m_canceled; ///> MediaPortal canceled (date + time) // skipped: recommendedCard bool m_series; ///> MediaPortal series int m_parentScheduleID; ///> MediaPortal idParentSchedule // XBMC asks for these fields: bool m_active; bool m_done; bool m_ismanual; bool m_isrecording; int m_progid; ///> MediaPortal Program ID std::string m_genre; ///> The genre string for the program std::string m_description; ///> Program description CGenreTable* m_genretable; }; namespace Timer { // Life time values for the recordings extern cLifeTimeValues* lifetimeValues; }; #endif //__TIMERS_H pvr.mediaportal.tvserver-3.5.18-Leia/src/uri.cpp000066400000000000000000000245461346756700600215710ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . */ #include "uri.h" namespace uri { const char ENCODE_BEGIN_CHAR = '%'; const traits SCHEME_TRAITS = { 0, 0, ':', { CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CVA2,CINV,CVA2,CVA2,CINV, CVA2,CVA2,CVA2,CVA2,CVA2,CVA2,CVA2,CVA2, CVA2,CVA2,CEND,CINV,CINV,CINV,CINV,CINV, CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CINV, CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CINV, // 127 7F CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, } }; const traits AUTHORITY_TRAITS = { "//", 0, 0, { CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CEND,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, // 127 7F CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, } }; const traits PATH_TRAITS = { 0, 0, 0, { // '/' is invalid CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CVAL,CINV,CINV,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CINV, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CVAL,CINV,CINV, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL, CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, } }; const traits QUERY_TRAITS = { 0, '?', 0, { // '=' and '&' are invalid CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CVAL,CINV,CINV,CVAL,CVAL,CINV,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL, CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, } }; const traits FRAGMENT_TRAITS = { 0, '#', 0, { CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CVAL,CINV,CINV,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CVAL,CINV,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL, CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, } }; bool parse_hex(const std::string& s, size_t pos, char& chr) { if (s.size() < pos + 2) return false; unsigned int v; unsigned int c = (unsigned int) s[pos]; if ('0' <= c && c <= '9') v = (c - '0') << 4; else if ('A' <= c && c <= 'F') v = (10 + (c - 'A')) << 4; else if ('a' <= c && c <= 'f') v = (10 + (c - 'a')) << 4; else return false; c = (unsigned int) s[pos + 1]; if ('0' <= c && c <= '9') v += c - '0'; else if ('A' <= c && c <= 'F') v += 10 + (c - 'A'); else if ('a' <= c && c <= 'f') v += 10 + (c - 'a'); else return false; chr = (char) v; // Set output. return true; } void append_hex(char v, std::string& s) { unsigned int c = (unsigned char) v & 0xF0; c >>= 4; s.insert(s.end(), (char)((9 < c) ? (c - 10) + 'A' : c + '0')); c = v & 0x0F; s.insert(s.end(), (char)((9 < c) ? (c - 10) + 'A' : c + '0')); } std::string encode(const traits& ts, const std::string& comp) { std::string::const_iterator f = comp.begin(); std::string::const_iterator anchor = f; std::string s; for (; f != comp.end();) { char c = *f; if (ts.char_class[(unsigned char)c] < CVAL || c == ENCODE_BEGIN_CHAR) { // Must encode. s.append(anchor, f); // Catch up to this char. s.append(1, ENCODE_BEGIN_CHAR); append_hex(c, s); // Convert. anchor = ++f; } else { ++f; } } return (anchor == comp.begin()) ? comp : s.append(anchor, comp.end()); } bool decode(std::string& s) { size_t pos = s.find(ENCODE_BEGIN_CHAR); if (pos == std::string::npos) { // Handle the "99%" case fast. return true; } std::string v; for (size_t i = 0;;) { if (pos == std::string::npos) { v.append(s, i, s.size() - i); // Append up to end. break; } v.append(s, i, pos - i); // Append up to char. i = pos + 3; // Skip all 3 chars. char c; if (!parse_hex(s, pos + 1, c)) { // Convert hex. return false; } v.insert(v.end(), c); // Append converted hex. pos = s.find(ENCODE_BEGIN_CHAR, i); // Find next } s = v; return true; } } //namespace URI pvr.mediaportal.tvserver-3.5.18-Leia/src/uri.h000066400000000000000000000053551346756700600212330ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . */ #include namespace uri { /// Char class. typedef enum char_class_e : signed char { CINV = -2, ///< invalid CEND = -1, ///< end delimitor CVAL = 0, ///< valid any position CVA2 = 1, ///< valid anywhere but 1st position } char_class_e_type; /// Traits used for parsing and encoding components. struct traits { const char* begin_cstring; ///< begin cstring (or 0 if none) const char begin_char; ///< begin char (or 0 if none) const char end_char; ///< end char (or 0 if none) const char_class_e_type char_class[256]; ///< map of char to class }; /** * \brief Encode the URI (sub) component. * Note that this should be used on the subcomponents before appending to * subdelimiter chars, if any. * * From the RFC: URI producing applications should percent-encode data octets * are specifically allowed by the URI scheme to represent data in that * component. If a reserved character is found in a URI component and * no delimiting role is known for that character, then it must be * interpreted as representing the data octet corresponding to that * character's encoding in US-ASCII. * @see http://tools.ietf.org/html/rfc3986 * @see decode std::string encode(const traits& ts, const std::string& comp); */ std::string encode(const traits& ts, const std::string& comp); /** * Decode the pct-encoded (hex) sequences, if any, return success. * Does not change string on error. * @see http://tools.ietf.org/html/rfc3986#section-2.1 * @see encode * \param s A reference to the std::string to decode */ bool decode(std::string& s); extern const char ENCODE_BEGIN_CHAR; ///< encode begin char ('\%') extern const traits SCHEME_TRAITS; ///< scheme traits extern const traits AUTHORITY_TRAITS; ///< authority traits extern const traits PATH_TRAITS; ///< path traits extern const traits QUERY_TRAITS; ///< query traits extern const traits FRAGMENT_TRAITS; ///< fragment traits } pvr.mediaportal.tvserver-3.5.18-Leia/src/utils.cpp000066400000000000000000000076761346756700600221370ustar00rootroot00000000000000/* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . * */ #ifdef TARGET_WINDOWS #pragma warning(disable:4244) //wchar to char = loss of data #endif #include "client.h" #include "utils.h" #include #include #include "p8-platform/util/StringUtils.h" using namespace std; using namespace ADDON; void Tokenize(const string& str, vector& tokens, const string& delimiters = " ") { // Skip delimiters at beginning. //string::size_type lastPos = str.find_first_not_of(delimiters, 0); // Don't skip delimiters at beginning. string::size_type start_pos = 0; // Find first "non-delimiter". string::size_type delim_pos = 0; while (string::npos != delim_pos) { delim_pos = str.find_first_of(delimiters, start_pos); // Found a token, add it to the vector. tokens.push_back(str.substr(start_pos, delim_pos - start_pos)); start_pos = delim_pos + 1; // Find next "non-delimiter" } } std::string WStringToString(const std::wstring& s) { std::string temp(s.length(), ' '); std::copy(s.begin(), s.end(), temp.begin()); return temp; } std::wstring StringToWString(const std::string& s) { std::wstring temp(s.length(),L' '); std::copy(s.begin(), s.end(), temp.begin()); return temp; } std::string lowercase(const std::string& s) { std::string t; for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { t += tolower(*i); } return t; } bool stringtobool(const std::string& s) { std::string temp = lowercase(s); if(temp.compare("false") == 0) return false; else if(temp.compare("0") == 0) return false; else return true; } const char* booltostring(const bool b) { return (b==true) ? "True" : "False"; } std::string ToThumbFileName(const char* strChannelName) { std::string strThumbName = strChannelName; StringUtils::Replace(strThumbName, ":","_"); StringUtils::Replace(strThumbName, "/","_"); StringUtils::Replace(strThumbName, "\\","_"); StringUtils::Replace(strThumbName, ">","_"); StringUtils::Replace(strThumbName, "<","_"); StringUtils::Replace(strThumbName, "*","_"); StringUtils::Replace(strThumbName, "?","_"); StringUtils::Replace(strThumbName, "\"","_"); StringUtils::Replace(strThumbName, "|","_"); return strThumbName; } std::string ToKodiPath(const std::string& strFileName) { std::string strKodiFileName(strFileName); if (StringUtils::Left(strKodiFileName, 2) == "\\\\") { std::string SMBPrefix = "smb://"; if (g_szSMBusername.length() > 0) { SMBPrefix += g_szSMBusername; if (g_szSMBpassword.length() > 0) { SMBPrefix += ":" + g_szSMBpassword; } SMBPrefix += "@"; } StringUtils::Replace(strKodiFileName, "\\\\", SMBPrefix.c_str()); StringUtils::Replace(strKodiFileName, '\\', '/'); } return strKodiFileName; } std::string ToWindowsPath(const std::string& strFileName) { std::string strWinFileName; std::size_t found = strFileName.find_first_of('@'); if (found != std::string::npos) { strWinFileName = "\\\\" + strFileName.substr(found+1); } else { strWinFileName = strFileName; StringUtils::Replace(strWinFileName, "smb://","\\\\"); } StringUtils::Replace(strWinFileName, '/','\\'); return strWinFileName; } ////////////////////////////////////////////////////////////////////////////// pvr.mediaportal.tvserver-3.5.18-Leia/src/utils.h000066400000000000000000000034611346756700600215700ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2011 Team Kodi * https://kodi.tv * * 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 . */ #include #include #include #include "uri.h" #include "p8-platform/util/util.h" #ifdef TARGET_WINDOWS #include "windows/WindowsUtils.h" #endif /** * String tokenize * Split string using the given delimiter into a vector of substrings */ void Tokenize(const std::string& str, std::vector& tokens, const std::string& delimiters); std::wstring StringToWString(const std::string& s); std::string WStringToString(const std::wstring& s); std::string lowercase(const std::string& s); bool stringtobool(const std::string& s); const char* booltostring(const bool b); /** * @brief Filters forbidden filename characters from channel name and replaces them with _ ) */ std::string ToThumbFileName(const char* strChannelName); std::string ToKodiPath(const std::string& strFileName); std::string ToWindowsPath(const std::string& strFileName); /** * @brief Macro to silence unused parameter warnings */ #ifdef UNUSED # undef UNUSED #endif #ifdef __GNUC__ # define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) #else # define UNUSED(x) /* x */ #endif pvr.mediaportal.tvserver-3.5.18-Leia/src/windows/000077500000000000000000000000001346756700600217455ustar00rootroot00000000000000pvr.mediaportal.tvserver-3.5.18-Leia/src/windows/FileUtils.cpp000066400000000000000000000033371346756700600243570ustar00rootroot00000000000000/* * Copyright (C) 2005-2014 Team Kodi * https://kodi.tv * * 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 . * */ #include "../FileUtils.h" #include "p8-platform/os.h" #include "p8-platform/windows/CharsetConverter.h" #include #include "../utils.h" #ifdef TARGET_WINDOWS_DESKTOP #include #endif namespace OS { bool CFile::Exists(const std::string& strFileName, long* errCode) { std::string strWinFile = ToWindowsPath(strFileName); std::wstring strWFile = p8::windows::ToW(strWinFile.c_str()); DWORD dwAttr = GetFileAttributesW(strWFile.c_str()); if(dwAttr != 0xffffffff) { return true; } if (errCode) *errCode = GetLastError(); return false; } #ifdef TARGET_WINDOWS_DESKTOP /** * Return the location of the Program Data folder */ bool GetProgramData(std::string& programData) { LPWSTR wszPath = NULL; if (SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &wszPath) != S_OK) { return false; } std::wstring wPath = wszPath; CoTaskMemFree(wszPath); programData = WStringToString(wPath); return true; } #endif } pvr.mediaportal.tvserver-3.5.18-Leia/src/windows/WindowsUtils.cpp000066400000000000000000000024721346756700600251310ustar00rootroot00000000000000/* * Copyright (C) 2005-2014 Team Kodi * https://kodi.tv * * 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 . * */ #include "WindowsUtils.h" #include #include #include namespace OS { bool GetEnvironmentVariable(const char* strVarName, std::string& strResult) { #ifdef TARGET_WINDOWS_DESKTOP char strBuffer[4096]; DWORD dwRet; dwRet = ::GetEnvironmentVariableA(strVarName, strBuffer, 4096); if(0 == dwRet) { dwRet = GetLastError(); if( ERROR_ENVVAR_NOT_FOUND == dwRet ) { strResult.clear(); return false; } } strResult = strBuffer; return true; #else return false; #endif // TARGET_WINDOWS_DESKTOP } } pvr.mediaportal.tvserver-3.5.18-Leia/src/windows/WindowsUtils.h000066400000000000000000000015371346756700600245770ustar00rootroot00000000000000#pragma once /* * Copyright (C) 2005-2014 Team Kodi * https://kodi.tv * * 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 . * */ #include namespace OS { bool GetEnvironmentVariable(const char* strVarName, std::string& strResult); }