mirror of
				https://github.com/DigitalDevices/pvr.octonet.git
				synced 2025-03-01 10:53:09 +00:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			4.1.0-2-Ma
			...
			Krypton
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d4077a142c | ||
|  | 6ee28476fa | ||
|  | de0a0f9a81 | ||
|  | 1b0c31654a | ||
|  | f260cb07e9 | 
| @@ -1,88 +0,0 @@ | |||||||
| --- |  | ||||||
| # BasedOnStyle:  LLVM |  | ||||||
| AccessModifierOffset: -2 |  | ||||||
| AlignAfterOpenBracket: Align |  | ||||||
| AlignConsecutiveAssignments: false |  | ||||||
| AlignConsecutiveDeclarations: false |  | ||||||
| AlignEscapedNewlines: DontAlign |  | ||||||
| AlignOperands:   true |  | ||||||
| AlignTrailingComments: false |  | ||||||
| AllowAllParametersOfDeclarationOnNextLine: true |  | ||||||
| AllowShortBlocksOnASingleLine: false |  | ||||||
| AllowShortCaseLabelsOnASingleLine: false |  | ||||||
| AllowShortFunctionsOnASingleLine: InlineOnly |  | ||||||
| AllowShortIfStatementsOnASingleLine: false |  | ||||||
| AllowShortLoopsOnASingleLine: false |  | ||||||
| AlwaysBreakAfterDefinitionReturnType: None |  | ||||||
| AlwaysBreakAfterReturnType: None |  | ||||||
| AlwaysBreakBeforeMultilineStrings: false |  | ||||||
| AlwaysBreakTemplateDeclarations: true |  | ||||||
| BinPackArguments: true |  | ||||||
| BinPackParameters: false |  | ||||||
| BreakBeforeBinaryOperators: None |  | ||||||
| BreakBeforeBraces: Allman |  | ||||||
| BreakBeforeTernaryOperators: true |  | ||||||
| BreakConstructorInitializersBeforeComma: false |  | ||||||
| BreakConstructorInitializers: BeforeColon |  | ||||||
| BreakAfterJavaFieldAnnotations: false |  | ||||||
| BreakStringLiterals: true |  | ||||||
| ColumnLimit:     100 |  | ||||||
| CommentPragmas:  '^ IWYU pragma:' |  | ||||||
| ConstructorInitializerAllOnOneLineOrOnePerLine: true |  | ||||||
| ConstructorInitializerIndentWidth: 2 |  | ||||||
| ContinuationIndentWidth: 4 |  | ||||||
| Cpp11BracedListStyle: true |  | ||||||
| DerivePointerAlignment: false |  | ||||||
| DisableFormat:   false |  | ||||||
| ExperimentalAutoDetectBinPacking: false |  | ||||||
| ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ] |  | ||||||
| IncludeBlocks: Regroup |  | ||||||
| IncludeCategories: |  | ||||||
|   - Regex:           '^<[a-z0-9_]+>$' |  | ||||||
|     Priority:        3 |  | ||||||
|   - Regex:           '^<(assert|complex|ctype|errno|fenv|float|inttypes|iso646|limits|locale|math|setjmp|signal|stdalign|stdarg|stdatomic|stdbool|stddef|stdint|stdio|stdlib|stdnoreturn|string|tgmath|threads|time|uchar|wchar|wctype)\.h>$' |  | ||||||
|     Priority:        3 |  | ||||||
|   - Regex:           '^<' |  | ||||||
|     Priority:        3 |  | ||||||
|   - Regex:           '^["<](kodi|p8-platform)\/.*\.h[">]$' |  | ||||||
|     Priority:        2 |  | ||||||
|   - Regex:           '.*' |  | ||||||
|     Priority:        1 |  | ||||||
| IncludeIsMainRegex: '$' |  | ||||||
| IndentCaseLabels: true |  | ||||||
| IndentWidth:     2 |  | ||||||
| IndentWrappedFunctionNames: false |  | ||||||
| JavaScriptQuotes: Leave |  | ||||||
| JavaScriptWrapImports: true |  | ||||||
| KeepEmptyLinesAtTheStartOfBlocks: true |  | ||||||
| MacroBlockBegin: '' |  | ||||||
| MacroBlockEnd:   '' |  | ||||||
| MaxEmptyLinesToKeep: 2 |  | ||||||
| NamespaceIndentation: None |  | ||||||
| ObjCBlockIndentWidth: 2 |  | ||||||
| ObjCSpaceAfterProperty: false |  | ||||||
| ObjCSpaceBeforeProtocolList: true |  | ||||||
| PenaltyBreakBeforeFirstCallParameter: 19 |  | ||||||
| PenaltyBreakComment: 300 |  | ||||||
| PenaltyBreakFirstLessLess: 120 |  | ||||||
| PenaltyBreakString: 1000 |  | ||||||
| PenaltyExcessCharacter: 1000000 |  | ||||||
| PenaltyReturnTypeOnItsOwnLine: 60000 |  | ||||||
| PointerAlignment: Left |  | ||||||
| ReflowComments:  false |  | ||||||
| SortIncludes:    true |  | ||||||
| SpaceAfterCStyleCast: false |  | ||||||
| SpaceAfterTemplateKeyword: false |  | ||||||
| SpaceBeforeAssignmentOperators: true |  | ||||||
| SpaceBeforeParens: ControlStatements |  | ||||||
| SpaceInEmptyParentheses: false |  | ||||||
| SpacesBeforeTrailingComments: 1 |  | ||||||
| SpacesInAngles:  false |  | ||||||
| SpacesInContainerLiterals: true |  | ||||||
| SpacesInCStyleCastParentheses: false |  | ||||||
| SpacesInParentheses: false |  | ||||||
| SpacesInSquareBrackets: false |  | ||||||
| Standard:        Cpp11 |  | ||||||
| TabWidth:        8 |  | ||||||
| UseTab:          Never |  | ||||||
| ... |  | ||||||
							
								
								
									
										51
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,50 +1 @@ | |||||||
| # build artifacts | /build | ||||||
| build/ |  | ||||||
| pvr.*/addon.xml |  | ||||||
|  |  | ||||||
| # Debian build files |  | ||||||
| debian/changelog |  | ||||||
| debian/files |  | ||||||
| debian/*.log |  | ||||||
| debian/*.substvars |  | ||||||
| debian/.debhelper/ |  | ||||||
| debian/tmp/ |  | ||||||
| debian/kodi-pvr-*/ |  | ||||||
| obj-x86_64-linux-gnu/ |  | ||||||
|  |  | ||||||
| # commonly used editors |  | ||||||
| # vim |  | ||||||
| *.swp |  | ||||||
|  |  | ||||||
| # Eclipse |  | ||||||
| *.project |  | ||||||
| *.cproject |  | ||||||
| .classpath |  | ||||||
| *.sublime-* |  | ||||||
| .settings/ |  | ||||||
|  |  | ||||||
| # KDevelop 4 |  | ||||||
| *.kdev4 |  | ||||||
|  |  | ||||||
| # gedit |  | ||||||
| *~ |  | ||||||
|  |  | ||||||
| # CLion |  | ||||||
| /.idea |  | ||||||
|  |  | ||||||
| # clion |  | ||||||
| .idea/ |  | ||||||
|  |  | ||||||
| # to prevent add after a "git format-patch VALUE" and "git add ." call |  | ||||||
| /*.patch |  | ||||||
|  |  | ||||||
| # Visual Studio Code |  | ||||||
| .vscode |  | ||||||
|  |  | ||||||
| # to prevent add if project code opened by Visual Studio over CMake file |  | ||||||
| .vs/ |  | ||||||
|  |  | ||||||
| # General MacOS |  | ||||||
| .DS_Store |  | ||||||
| .AppleDouble |  | ||||||
| .LSOverride |  | ||||||
|   | |||||||
							
								
								
									
										55
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,54 +1,45 @@ | |||||||
| language: cpp | language: cpp | ||||||
|  |  | ||||||
| # | # | ||||||
| # Define the builds to get up to date versions of cmake and gcc | # Define the build matrix | ||||||
|  | # | ||||||
|  | # Travis defaults to building on Ubuntu Precise when building on | ||||||
|  | # Linux. We need Trusty in order to get up to date versions of | ||||||
|  | # cmake and g++. | ||||||
| # | # | ||||||
| env: |  | ||||||
|   global: |  | ||||||
|     - app_id=pvr.octonet |  | ||||||
|  |  | ||||||
| matrix: | matrix: | ||||||
|   include: |   include: | ||||||
|     - os: linux |     - os: linux | ||||||
|       dist: bionic |       dist: trusty | ||||||
|       sudo: required |       sudo: required | ||||||
|       compiler: gcc |       compiler: gcc | ||||||
|     - os: linux |     - os: linux | ||||||
|       dist: bionic |       dist: trusty | ||||||
|       sudo: required |       sudo: required | ||||||
|       compiler: clang |       compiler: clang | ||||||
|     - os: linux |  | ||||||
|       dist: bionic |  | ||||||
|       sudo: required |  | ||||||
|       compiler: gcc |  | ||||||
|       env: DEBIAN_BUILD=true |  | ||||||
|     - os: linux |  | ||||||
|       dist: focal |  | ||||||
|       sudo: required |  | ||||||
|       compiler: gcc |  | ||||||
|       env: DEBIAN_BUILD=true |  | ||||||
|     - os: osx |     - os: osx | ||||||
|       osx_image: xcode10.2 |       osx_image: xcode7.3 | ||||||
|  |     - os: osx | ||||||
|  |       osx_image: xcode6.1 | ||||||
|  |  | ||||||
|  | # | ||||||
|  | # Some of the OS X images don't have cmake, contrary to what people | ||||||
|  | # on the Internet say | ||||||
|  | # | ||||||
| before_install: | before_install: | ||||||
|   - if [[ $DEBIAN_BUILD == true ]]; then sudo add-apt-repository -y ppa:team-xbmc/xbmc-nightly; fi |   - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then which cmake || brew update        ; fi | ||||||
|   - if [[ $DEBIAN_BUILD == true ]]; then sudo apt-get update; fi |   - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then which cmake || brew install cmake ; fi | ||||||
|   - if [[ $DEBIAN_BUILD == true ]]; then sudo apt-get install fakeroot; fi |  | ||||||
|  |  | ||||||
| # | # | ||||||
| # The addon source is automatically checked out in $TRAVIS_BUILD_DIR, | # The addon source is automatically checked out in $TRAVIS_BUILD_DIR, | ||||||
| # we'll put the Kodi source on the same level | # we'll put the Kodi source on the same level | ||||||
| # | # | ||||||
| before_script: | before_script: | ||||||
|   - if [[ $DEBIAN_BUILD != true ]]; then cd $TRAVIS_BUILD_DIR/..; fi |   - cd $TRAVIS_BUILD_DIR/.. | ||||||
|   - if [[ $DEBIAN_BUILD != true ]]; then git clone --branch master --depth=1 https://github.com/xbmc/xbmc.git; fi |   - git clone --depth=1 https://github.com/xbmc/xbmc.git -b Krypton | ||||||
|   - if [[ $DEBIAN_BUILD != true ]]; then cd ${app_id} && mkdir build && cd build; fi |   - mkdir -p xbmc/project/cmake/addons/addons/pvr.octonet | ||||||
|   - if [[ $DEBIAN_BUILD != true ]]; then mkdir -p definition/${app_id}; fi |   - echo "pvr.octonet https://github.com/DigitalDevices/pvr.octonet master" > xbmc/project/cmake/addons/addons/pvr.octonet/pvr.octonet.txt | ||||||
|   - if [[ $DEBIAN_BUILD != true ]]; then echo ${app_id} $TRAVIS_BUILD_DIR $TRAVIS_COMMIT > definition/${app_id}/${app_id}.txt; fi |   - cd $TRAVIS_BUILD_DIR && mkdir build && cd build | ||||||
|   - if [[ $DEBIAN_BUILD != true ]]; then 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; fi |   - cmake -DADDONS_TO_BUILD=pvr.octonet -DADDON_SRC_PREFIX=$TRAVIS_BUILD_DIR/.. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$TRAVIS_BUILD_DIR/../xbmc/addons -DPACKAGE_ZIP=1 $TRAVIS_BUILD_DIR/../xbmc/project/cmake/addons | ||||||
|   - if [[ $DEBIAN_BUILD == true ]]; then wget https://raw.githubusercontent.com/xbmc/xbmc/master/xbmc/addons/kodi-dev-kit/tools/debian-addon-package-test.sh && chmod +x ./debian-addon-package-test.sh; fi |  | ||||||
|   - if [[ $DEBIAN_BUILD == true ]]; then sudo apt-get build-dep $TRAVIS_BUILD_DIR; fi |  | ||||||
|  |  | ||||||
| script:  | script: make | ||||||
|   - if [[ $DEBIAN_BUILD != true ]]; then make; fi |  | ||||||
|   - if [[ $DEBIAN_BUILD == true ]]; then ./debian-addon-package-test.sh $TRAVIS_BUILD_DIR; fi |  | ||||||
|   | |||||||
| @@ -1,37 +1,35 @@ | |||||||
| cmake_minimum_required(VERSION 3.5) |  | ||||||
| project(pvr.octonet) | project(pvr.octonet) | ||||||
|  |  | ||||||
|  | cmake_minimum_required(VERSION 2.6) | ||||||
|  |  | ||||||
| list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) | list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) | ||||||
|  | enable_language(CXX) | ||||||
|  |  | ||||||
| find_package(Kodi REQUIRED) | find_package(Kodi REQUIRED) | ||||||
|  | find_package(kodiplatform REQUIRED) | ||||||
|  | find_package(p8-platform REQUIRED) | ||||||
| find_package(JsonCpp REQUIRED) | find_package(JsonCpp REQUIRED) | ||||||
|  |  | ||||||
| include_directories(${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways | include_directories( | ||||||
|                     ${JSONCPP_INCLUDE_DIRS}) | 	${kodiplatform_INCLUDE_DIRS} | ||||||
|  | 	${p8-platform_INCLUDE_DIRS} | ||||||
|  | 	${KODI_INCLUDE_DIR} | ||||||
|  | 	${JSONCPP_INCLUDE_DIRS}) | ||||||
|  |  | ||||||
| set(DEPLIBS ${JSONCPP_LIBRARIES}) | set(DEPLIBS | ||||||
|  | 	${p8-platform_LIBRARIES} | ||||||
|  | 	${JSONCPP_LIBRARIES}) | ||||||
|  |  | ||||||
| set(OCTONET_SOURCES src/addon.cpp | set(OCTONET_SOURCES | ||||||
|                     src/OctonetData.cpp | 	src/OctonetData.cpp | ||||||
|                     src/Socket.cpp | 	src/client.cpp | ||||||
|                     src/rtsp_client.cpp) | 	src/Socket.cpp | ||||||
|  | 	src/rtsp_client.cpp) | ||||||
| set(OCTONET_HEADERS src/addon.h |  | ||||||
|                     src/OctonetData.h |  | ||||||
|                     src/Socket.h |  | ||||||
|                     src/rtsp_client.hpp) |  | ||||||
|  |  | ||||||
| addon_version(pvr.octonet OCTONET) |  | ||||||
| add_definitions(-DOCTONET_VERSION=${OCTONET_VERSION}) |  | ||||||
|  |  | ||||||
| build_addon(pvr.octonet OCTONET DEPLIBS) | build_addon(pvr.octonet OCTONET DEPLIBS) | ||||||
|  |  | ||||||
| if(WIN32) | if(WIN32) | ||||||
|   if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore) | 	target_link_libraries(pvr.octonet wsock32 ws2_32) | ||||||
|     target_link_libraries(pvr.octonet wsock32 ws2_32) |  | ||||||
|   else() |  | ||||||
|     target_link_libraries(pvr.octonet ws2_32) |  | ||||||
|   endif() |  | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| include(CPack) | include(CPack) | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | |||||||
| buildPlugin(version: "Matrix") |  | ||||||
							
								
								
									
										264
									
								
								LICENSE.md
									
									
									
									
									
								
							
							
						
						
									
										264
									
								
								LICENSE.md
									
									
									
									
									
								
							| @@ -1,264 +0,0 @@ | |||||||
| The GNU General Public License, Version 2, June 1991 (GPLv2) |  | ||||||
| ============================================================ |  | ||||||
|  |  | ||||||
| > Copyright (C) 1989, 1991 Free Software Foundation, Inc. |  | ||||||
| > 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 Lesser 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. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 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. |  | ||||||
| @@ -20,14 +20,17 @@ $ git clone https://github.com/DigitalDevices/pvr.octonet.git | |||||||
| Clone the Kodi repository: | Clone the Kodi repository: | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| $ git clone --branch master https://github.com/xbmc/xbmc.git | $ git clone https://github.com/xbmc/xbmc.git | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | If you already have a local Kodi checkout, you can use that one. Just make sure it is recent enough | ||||||
|  | (Kodi 17 Beta 5 or later should work). | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| $ cd pvr.octonet | $ cd pvr.octonet | ||||||
| $ mkdir build | $ mkdir build | ||||||
| $ cd build | $ cd build | ||||||
| $ cmake -DCMAKE_BUILD_TYPE=Release -DADDONS_TO_BUILD="pvr.octonet" -DADDON_SRC_PREFIX="path to parent of pvr.octonet" -DCMAKE_INSTALL_PREFIX="install" -DPACKAGE_ZIP=ON "path to kodi/cmake/addons" | $ cmake -DCMAKE_BUILD_TYPE=Release -DADDONS_TO_BUILD="pvr.octonet" -DADDON_SRC_PREFIX="path to parent of pvr.octonet" -DCMAKE_INSTALL_PREFIX="install" -DPACKAGE_ZIP=ON "path to kodi/project/cmake/addons" | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| On Windows, you should add `-G "NMake Makefiles"` to the CMake invocation. Make sure that | On Windows, you should add `-G "NMake Makefiles"` to the CMake invocation. Make sure that | ||||||
|   | |||||||
| @@ -29,12 +29,12 @@ artifacts: | |||||||
| build_script: | build_script: | ||||||
|   - cd .. |   - cd .. | ||||||
|   - set ROOT=%cd% |   - set ROOT=%cd% | ||||||
|   - git clone --branch master --depth=1 https://github.com/xbmc/xbmc.git |   - git clone --depth=1 https://github.com/xbmc/xbmc.git -b Krypton | ||||||
|   - mkdir xbmc\cmake\addons\addons\pvr.octonet |   - mkdir xbmc\project\cmake\addons\addons\pvr.octonet | ||||||
|   - echo pvr.octonet https://github.com/DigitalDevices/pvr.octonet master > xbmc\cmake\addons\addons\pvr.octonet\pvr.octonet.txt |   - echo pvr.octonet https://github.com/DigitalDevices/pvr.octonet master > xbmc\project\cmake\addons\addons\pvr.octonet\pvr.octonet.txt | ||||||
|   - cd %ADDON% |   - cd %ADDON% | ||||||
|   - mkdir build |   - mkdir build | ||||||
|   - cd build |   - cd build | ||||||
|   # Must use absolute path for cmake to build depends correctly |   # Must use absolute path for cmake to build depends correctly | ||||||
|   - cmake -G "%GENERATOR%" -DADDONS_TO_BUILD=%ADDON% -DCMAKE_BUILD_TYPE=%CONFIG% -DADDON_SRC_PREFIX=%ROOT% -DCMAKE_INSTALL_PREFIX=install -DPACKAGE_ZIP=1 %ROOT%\xbmc\cmake\addons |   - cmake -G "%GENERATOR%" -DADDONS_TO_BUILD=%ADDON% -DCMAKE_BUILD_TYPE=%CONFIG% -DADDON_SRC_PREFIX=%ROOT% -DCMAKE_INSTALL_PREFIX=install -DPACKAGE_ZIP=1 %ROOT%\xbmc\project\cmake\addons | ||||||
|   - cmake --build . --config %CONFIG% |   - cmake --build . --config %CONFIG% | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -1,9 +1,9 @@ | |||||||
| Source: kodi-pvr-octonet | Source: kodi-pvr-octonet | ||||||
| Priority: extra | Priority: extra | ||||||
| Maintainer: Julian Scheel <julian@jusst.de> | Maintainer: Julian Scheel <julian@jusst.de> | ||||||
| Build-Depends: debhelper (>= 9.0.0), cmake, libjsoncpp-dev, | Build-Depends: debhelper (>= 9.0.0), cmake, libjsoncpp-dev, kodi-pvr-dev, | ||||||
|                libp8-platform-dev, kodi-addon-dev |                libkodiplatform-dev (>= 16.0.0), kodi-addon-dev | ||||||
| Standards-Version: 4.1.2 | Standards-Version: 3.9.4 | ||||||
| Section: libs | Section: libs | ||||||
| Homepage: https://github.com/DigitalDevices/pvr.octonet | Homepage: https://github.com/DigitalDevices/pvr.octonet | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -5,7 +5,7 @@ Source: https://github.com/DigitalDevices/pvr.octonet | |||||||
| Files: * | Files: * | ||||||
| Copyright: 2015-2016 Julian Scheel | Copyright: 2015-2016 Julian Scheel | ||||||
|            2015-2016 jusst technologies GmbH |            2015-2016 jusst technologies GmbH | ||||||
|            2005-2020 Team Kodi |            2005-2013 Team XBMC | ||||||
| License: GPL-2+ | License: GPL-2+ | ||||||
|  This package is free software; you can redistribute it and/or modify |  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 |  it under the terms of the GNU General Public License as published by | ||||||
| @@ -24,8 +24,7 @@ License: GPL-2+ | |||||||
|  Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". |  Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". | ||||||
|  |  | ||||||
| Files: debian/* | Files: debian/* | ||||||
| Copyright: 2020 Team Kodi | Copyright: 2016 Julian Scheel <julian@jusst.de> | ||||||
|            2016 Julian Scheel <julian@jusst.de> |  | ||||||
|            2015 Jean-Luc Barriere |            2015 Jean-Luc Barriere | ||||||
|            2015 wsnipex <wsnipex@a1.net> |            2015 wsnipex <wsnipex@a1.net> | ||||||
| License: GPL-2+ | License: GPL-2+ | ||||||
|   | |||||||
| @@ -1,9 +1,7 @@ | |||||||
| cmake_minimum_required(VERSION 3.5) |  | ||||||
| project(jsoncpp) | project(jsoncpp) | ||||||
|  |  | ||||||
| SET(CMAKE_CXX_STANDARD 11) | cmake_minimum_required(VERSION 2.6) | ||||||
| SET(CMAKE_CXX_STANDARD_REQUIRED ON) | enable_language(CXX) | ||||||
| SET(CMAKE_CXX_EXTENSIONS OFF) |  | ||||||
|  |  | ||||||
| set(SOURCES src/lib_json/json_reader.cpp | set(SOURCES src/lib_json/json_reader.cpp | ||||||
|             src/lib_json/json_value.cpp |             src/lib_json/json_value.cpp | ||||||
|   | |||||||
| @@ -1 +0,0 @@ | |||||||
| 3671ba6051e0f30849942cc66d1798fdf0362d089343a83f704c09ee7156604f |  | ||||||
| @@ -1 +1 @@ | |||||||
| jsoncpp http://mirrors.kodi.tv/build-deps/sources/jsoncpp-1.8.3.tar.gz | jsoncpp http://mirrors.kodi.tv/build-deps/sources/jsoncpp-src-0.5.0.tar.gz | ||||||
|   | |||||||
| @@ -1,18 +1,23 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <addon | <addon | ||||||
| 	id="pvr.octonet" | 	id="pvr.octonet" | ||||||
| 	version="4.1.0" | 	version="0.3.7" | ||||||
| 	name="Digital Devices Octopus NET Client" | 	name="Digital Devices Octopus NET Client" | ||||||
| 	provider-name="digitaldevices"> | 	provider-name="digitaldevices"> | ||||||
| 	<requires>@ADDON_DEPENDS@</requires> | 	<requires> | ||||||
|  | 		<c-pluff version="0.1" /> | ||||||
|  | 		<import addon="xbmc.pvr" version="5.2.1" /> | ||||||
|  | 	</requires> | ||||||
| 	<extension | 	<extension | ||||||
| 		point="kodi.pvrclient" | 		point="xbmc.pvrclient" | ||||||
| 		library_@PLATFORM@="@LIBRARY_FILENAME@"/> | 		library_linux="pvr.octonet.so" | ||||||
|  | 		library_osx="pvr.octonet.dylib" | ||||||
|  | 		library_freebsd="pvr.octonet.so" | ||||||
|  | 		library_windx="pvr.octonet.dll" | ||||||
|  | 		library_android="libpvr.octonet.so" /> | ||||||
| 	<extension point="xbmc.addon.metadata"> | 	<extension point="xbmc.addon.metadata"> | ||||||
| 		<summary lang="de_DE">Kodi PVR Addon für Digital Devices Octopus NET Streams</summary> | 		<summary lang="de_DE">Kodi PVR Addon für Digital Devices Octopus NET Streams</summary> | ||||||
| 		<summary lang="en_US">Kodi PVR Addon for Digital Devices Octopus NET Streams</summary> | 		<summary lang="en_US">Kodi PVR Addon for Digital Devices Octopus NET Streams</summary> | ||||||
| 		<platform>@PLATFORM@</platform> | 		<platform>all</platform> | ||||||
| 		<license>GPL-2.0-or-later</license> |  | ||||||
| 		<source>https://github.com/DigitalDevices/pvr.octonet</source> |  | ||||||
| 	</extension> | 	</extension> | ||||||
| </addon> | </addon> | ||||||
| @@ -1,18 +1,5 @@ | |||||||
| <?xml version="1.0" encoding="utf-8" standalone="yes"?> | <?xml version="1.0" encoding="utf-8" standalone="yes"?> | ||||||
| <settings version="1"> | <settings> | ||||||
|   <section id="pvr.octonet"> | 	<!-- Octonet Server Address --> | ||||||
|     <category id="main" label="128" help="-1"> | 	<setting id="octonetAddress" type="text" label="30000" default="" /> | ||||||
|       <group id="1" label="-1"> |  | ||||||
|         <!-- Octonet Server Address --> |  | ||||||
|         <setting id="octonetAddress" type="string" label="30000" help="-1"> |  | ||||||
|           <level>0</level> |  | ||||||
|           <default></default> |  | ||||||
|           <constraints> |  | ||||||
|             <allowempty>true</allowempty> |  | ||||||
|           </constraints> |  | ||||||
|           <control type="edit" format="string" /> |  | ||||||
|         </setting> |  | ||||||
|       </group> |  | ||||||
|     </category> |  | ||||||
|   </section> |  | ||||||
| </settings> | </settings> | ||||||
|   | |||||||
| @@ -1,458 +1,370 @@ | |||||||
| /* | /* | ||||||
|  *  Copyright (C) 2015 Julian Scheel <julian@jusst.de> |  * Copyright (C) 2015 Julian Scheel <julian@jusst.de> | ||||||
|  *  Copyright (C) 2015 jusst technologies GmbH |  * Copyright (C) 2015 jusst technologies GmbH | ||||||
|  *  Copyright (C) 2015 Digital Devices GmbH |  * Copyright (C) 2015 Digital Devices GmbH | ||||||
|  * |  * | ||||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later |  * This program is free software; you can redistribute it and/or | ||||||
|  *  See LICENSE.md for more information. |  * 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, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, | ||||||
|  |  * USA. | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "OctonetData.h" |  | ||||||
|  |  | ||||||
| #include "rtsp_client.hpp" |  | ||||||
|  |  | ||||||
| #include <json/json.h> |  | ||||||
| #include <kodi/Filesystem.h> |  | ||||||
| #include <kodi/General.h> |  | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) | #include <json/json.h> | ||||||
|  |  | ||||||
|  | #include "OctonetData.h" | ||||||
|  | #include "p8-platform/util/StringUtils.h" | ||||||
|  |  | ||||||
|  | #ifdef __WINDOWS__ | ||||||
| #define timegm _mkgmtime | #define timegm _mkgmtime | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| OctonetData::OctonetData(const std::string& octonetAddress, | using namespace ADDON; | ||||||
|                          KODI_HANDLE instance, |  | ||||||
|                          const std::string& kodiVersion) | OctonetData::OctonetData() | ||||||
|   : kodi::addon::CInstancePVRClient(instance, kodiVersion) |  | ||||||
| { | { | ||||||
|   m_serverAddress = octonetAddress; | 	serverAddress = octonetAddress; | ||||||
|   m_channels.clear(); | 	channels.clear(); | ||||||
|   m_groups.clear(); | 	groups.clear(); | ||||||
|   m_lastEpgLoad = 0; | 	lastEpgLoad = 0; | ||||||
|  |  | ||||||
|   if (!LoadChannelList()) | 	if (!loadChannelList()) | ||||||
|     kodi::QueueFormattedNotification(QUEUE_ERROR, kodi::GetLocalizedString(30001).c_str(), | 		kodi->QueueNotification(QUEUE_ERROR, kodi->GetLocalizedString(30001), channels.size()); | ||||||
|                                      m_channels.size()); |  | ||||||
|  |  | ||||||
|   /* |  | ||||||
|   // Currently unused, as thread was already present before with |  | ||||||
|   // p8platform, by remove of them was it added as C++11 thread way. |  | ||||||
|   kodi::Log(ADDON_LOG_INFO, "%s Starting separate client update thread...", __func__); |  | ||||||
|   m_running = true; |  | ||||||
|   m_thread = std::thread([&] { Process(); }); |  | ||||||
|   */ |  | ||||||
| } | } | ||||||
|  |  | ||||||
| OctonetData::~OctonetData(void) | OctonetData::~OctonetData(void) | ||||||
| { | { | ||||||
|   /* | 	channels.clear(); | ||||||
|   m_running = false; | 	groups.clear(); | ||||||
|   if (m_thread.joinable()) |  | ||||||
|     m_thread.join(); |  | ||||||
|   */ |  | ||||||
| } | } | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetCapabilities(kodi::addon::PVRCapabilities& capabilities) | int64_t OctonetData::parseID(std::string id) | ||||||
| { | { | ||||||
|   capabilities.SetSupportsTV(true); | 	std::hash<std::string> hash_fn; | ||||||
|   capabilities.SetSupportsRadio(true); | 	int64_t nativeId = hash_fn(id); | ||||||
|   capabilities.SetSupportsChannelGroups(true); |  | ||||||
|   capabilities.SetSupportsEPG(true); |  | ||||||
|   capabilities.SetSupportsRecordings(false); |  | ||||||
|   capabilities.SetSupportsRecordingsRename(false); |  | ||||||
|   capabilities.SetSupportsRecordingsLifetimeChange(false); |  | ||||||
|   capabilities.SetSupportsDescrambleInfo(false); |  | ||||||
|  |  | ||||||
|   return PVR_ERROR_NO_ERROR; | 	return nativeId; | ||||||
| } | } | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetBackendName(std::string& name) | bool OctonetData::loadChannelList() | ||||||
| { | { | ||||||
|   name = "Digital Devices Octopus NET Client"; | 	std::string jsonContent; | ||||||
|   return PVR_ERROR_NO_ERROR; | 	void *f = kodi->OpenFile(("http://" + serverAddress + "/channellist.lua?select=json").c_str(), 0); | ||||||
|  | 	if (!f) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	char buf[1024]; | ||||||
|  | 	while (int read = kodi->ReadFile(f, buf, 1024)) | ||||||
|  | 		jsonContent.append(buf, read); | ||||||
|  |  | ||||||
|  | 	kodi->CloseFile(f); | ||||||
|  |  | ||||||
|  | 	Json::Value root; | ||||||
|  | 	Json::Reader reader; | ||||||
|  |  | ||||||
|  | 	if (!reader.parse(jsonContent, root, false)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	const Json::Value groupList = root["GroupList"]; | ||||||
|  | 	for (unsigned int i = 0; i < groupList.size(); i++) { | ||||||
|  | 		const Json::Value channelList = groupList[i]["ChannelList"]; | ||||||
|  | 		OctonetGroup group; | ||||||
|  |  | ||||||
|  | 		group.name = groupList[i]["Title"].asString(); | ||||||
|  | 		group.radio = group.name.compare(0, 5, "Radio") ? false : true; | ||||||
|  |  | ||||||
|  | 		for (unsigned int j = 0; j < channelList.size(); j++) { | ||||||
|  | 			const Json::Value channel = channelList[j]; | ||||||
|  | 			OctonetChannel chan; | ||||||
|  |  | ||||||
|  | 			chan.name = channel["Title"].asString(); | ||||||
|  | 			chan.url = "rtsp://" + serverAddress + "/" + channel["Request"].asString(); | ||||||
|  | 			chan.radio = group.radio; | ||||||
|  | 			chan.nativeId = parseID(channel["ID"].asString()); | ||||||
|  |  | ||||||
|  | 			chan.id = 1000 + channels.size(); | ||||||
|  | 			group.members.push_back(channels.size()); | ||||||
|  | 			channels.push_back(chan); | ||||||
|  | 		} | ||||||
|  | 		groups.push_back(group); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetBackendVersion(std::string& version) | OctonetChannel* OctonetData::findChannel(int64_t nativeId) | ||||||
| { | { | ||||||
|   version = STR(OCTONET_VERSION); | 	std::vector<OctonetChannel>::iterator it; | ||||||
|   return PVR_ERROR_NO_ERROR; | 	for (it = channels.begin(); it < channels.end(); ++it) { | ||||||
|  | 		if (it->nativeId == nativeId) | ||||||
|  | 			return &*it; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetConnectionString(std::string& connection) | time_t OctonetData::parseDateTime(std::string date) | ||||||
| { | { | ||||||
|   connection = "connected"; // FIXME: translate? | 	struct tm timeinfo; | ||||||
|   return PVR_ERROR_NO_ERROR; |  | ||||||
|  | 	memset(&timeinfo, 0, sizeof(timeinfo)); | ||||||
|  |  | ||||||
|  | 	if (date.length() > 8) { | ||||||
|  | 		sscanf(date.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", | ||||||
|  | 				&timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, | ||||||
|  | 				&timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); | ||||||
|  | 		timeinfo.tm_mon -= 1; | ||||||
|  | 		timeinfo.tm_year -= 1900; | ||||||
|  | 	} else { | ||||||
|  | 		sscanf(date.c_str(), "%02d:%02d:%02d", | ||||||
|  | 				&timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); | ||||||
|  | 		timeinfo.tm_year = 70; // unix timestamps start 1970 | ||||||
|  | 		timeinfo.tm_mday = 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	timeinfo.tm_isdst = -1; | ||||||
|  |  | ||||||
|  | 	return timegm(&timeinfo); | ||||||
| } | } | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetBackendHostname(std::string& hostname) | bool OctonetData::loadEPG(void) | ||||||
| { | { | ||||||
|   hostname = m_serverAddress; | 	/* Reload at most every 30 seconds */ | ||||||
|   return PVR_ERROR_NO_ERROR; | 	if (lastEpgLoad + 30 > time(NULL)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	std::string jsonContent; | ||||||
|  | 	void *f = kodi->OpenFile(("http://" + serverAddress + "/epg.lua?;#|encoding=gzip").c_str(), 0); | ||||||
|  | 	if (!f) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	char buf[1024]; | ||||||
|  | 	while (int read = kodi->ReadFile(f, buf, 1024)) | ||||||
|  | 		jsonContent.append(buf, read); | ||||||
|  |  | ||||||
|  | 	kodi->CloseFile(f); | ||||||
|  |  | ||||||
|  | 	Json::Value root; | ||||||
|  | 	Json::Reader reader; | ||||||
|  |  | ||||||
|  | 	if (!reader.parse(jsonContent, root, false)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	const Json::Value eventList = root["EventList"]; | ||||||
|  | 	OctonetChannel *channel = NULL; | ||||||
|  | 	for (unsigned int i = 0; i < eventList.size(); i++) { | ||||||
|  | 		const Json::Value event = eventList[i]; | ||||||
|  | 		OctonetEpgEntry entry; | ||||||
|  |  | ||||||
|  | 		entry.start = parseDateTime(event["Time"].asString()); | ||||||
|  | 		entry.end = entry.start + parseDateTime(event["Duration"].asString()); | ||||||
|  | 		entry.title = event["Name"].asString(); | ||||||
|  | 		entry.subtitle = event["Text"].asString(); | ||||||
|  | 		std::string channelId = event["ID"].asString(); | ||||||
|  | 		std::string epgId = channelId.substr(channelId.rfind(":") + 1); | ||||||
|  | 		channelId = channelId.substr(0, channelId.rfind(":")); | ||||||
|  |  | ||||||
|  | 		entry.channelId = parseID(channelId); | ||||||
|  | 		entry.id = atoi(epgId.c_str()); | ||||||
|  |  | ||||||
|  | 		if (channel == NULL || channel->nativeId != entry.channelId) | ||||||
|  | 			channel = findChannel(entry.channelId); | ||||||
|  |  | ||||||
|  | 		if (channel == NULL) { | ||||||
|  | 			kodi->Log(LOG_ERROR, "EPG for unknown channel."); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		channel->epg.push_back(entry); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	lastEpgLoad = time(NULL); | ||||||
|  | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::OnSystemSleep() | void *OctonetData::Process(void) | ||||||
| { | { | ||||||
|   kodi::Log(ADDON_LOG_INFO, "Received event: %s", __func__); | 	return NULL; | ||||||
|   // FIXME: Disconnect? |  | ||||||
|   return PVR_ERROR_NO_ERROR; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::OnSystemWake() | int OctonetData::getChannelCount(void) | ||||||
| { | { | ||||||
|   kodi::Log(ADDON_LOG_INFO, "Received event: %s", __func__); | 	return channels.size(); | ||||||
|   // FIXME:Reconnect? |  | ||||||
|   return PVR_ERROR_NO_ERROR; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int64_t OctonetData::ParseID(std::string id) | PVR_ERROR OctonetData::getChannels(ADDON_HANDLE handle, bool bRadio) | ||||||
| { | { | ||||||
|   std::hash<std::string> hash_fn; | 	for (unsigned int i = 0; i < channels.size(); i++) | ||||||
|   int64_t nativeId = hash_fn(id); | 	{ | ||||||
|  | 		OctonetChannel &channel = channels.at(i); | ||||||
|  | 		if (channel.radio == bRadio) | ||||||
|  | 		{ | ||||||
|  | 			PVR_CHANNEL chan; | ||||||
|  | 			memset(&chan, 0, sizeof(PVR_CHANNEL)); | ||||||
|  |  | ||||||
|   return nativeId; | 			chan.iUniqueId = channel.id; | ||||||
|  | 			chan.bIsRadio = channel.radio; | ||||||
|  | 			chan.iChannelNumber = i; | ||||||
|  | 			strncpy(chan.strChannelName, channel.name.c_str(), strlen(channel.name.c_str())); | ||||||
|  | 			strcpy(chan.strInputFormat, "video/x-mpegts"); | ||||||
|  | 			chan.bIsHidden = false; | ||||||
|  |  | ||||||
|  | 			pvr->TransferChannelEntry(handle, &chan); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return PVR_ERROR_NO_ERROR; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool OctonetData::LoadChannelList() | PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end) | ||||||
| { | { | ||||||
|   std::string jsonContent; | 	for (unsigned int i = 0; i < channels.size(); i++) | ||||||
|   kodi::vfs::CFile f; | 	{ | ||||||
|   if (!f.OpenFile("http://" + m_serverAddress + "/channellist.lua?select=json", 0)) | 		OctonetChannel &chan = channels.at(i); | ||||||
|     return false; | 		if (channel.iUniqueId != chan.id) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|   char buf[1024]; | 		if(chan.epg.empty()) { | ||||||
|   while (int read = f.Read(buf, 1024)) | 			loadEPG(); | ||||||
|     jsonContent.append(buf, read); | 		} | ||||||
|  |  | ||||||
|   f.Close(); | 		// FIXME: Check if reload is needed!? | ||||||
|  |  | ||||||
|   Json::Value root; | 		std::vector<OctonetEpgEntry>::iterator it; | ||||||
|   Json::Reader reader; | 		time_t last_end = 0; | ||||||
|  | 		for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { | ||||||
|  | 			if (it->end > last_end) | ||||||
|  | 				last_end = it->end; | ||||||
|  |  | ||||||
|   if (!reader.parse(jsonContent, root, false)) | 			if (it->end < start || it->start > end) { | ||||||
|     return false; | 				continue; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|   const Json::Value groupList = root["GroupList"]; | 			EPG_TAG entry; | ||||||
|   for (unsigned int i = 0; i < groupList.size(); i++) | 			memset(&entry, 0, sizeof(EPG_TAG)); | ||||||
|   { |  | ||||||
|     const Json::Value channelList = groupList[i]["ChannelList"]; |  | ||||||
|     OctonetGroup group; |  | ||||||
|  |  | ||||||
|     group.name = groupList[i]["Title"].asString(); | 			entry.iChannelNumber = i; | ||||||
|     group.radio = group.name.compare(0, 5, "Radio") ? false : true; | 			entry.iUniqueBroadcastId = it->id; | ||||||
|  | 			entry.strTitle = it->title.c_str(); | ||||||
|  | 			entry.strPlotOutline = it->subtitle.c_str(); | ||||||
|  | 			entry.startTime = it->start; | ||||||
|  | 			entry.endTime = it->end; | ||||||
|  |  | ||||||
|     for (unsigned int j = 0; j < channelList.size(); j++) | 			pvr->TransferEpgEntry(handle, &entry); | ||||||
|     { | 		} | ||||||
|       const Json::Value channel = channelList[j]; |  | ||||||
|       OctonetChannel chan; |  | ||||||
|  |  | ||||||
|       chan.name = channel["Title"].asString(); | 		if (last_end < end) | ||||||
|       chan.url = "rtsp://" + m_serverAddress + "/" + channel["Request"].asString(); | 			loadEPG(); | ||||||
|       chan.radio = group.radio; |  | ||||||
|       chan.nativeId = ParseID(channel["ID"].asString()); |  | ||||||
|  |  | ||||||
|       chan.id = 1000 + m_channels.size(); | 		for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { | ||||||
|       group.members.push_back(m_channels.size()); | 			if (it->end < start || it->start > end) { | ||||||
|       m_channels.push_back(chan); | 				continue; | ||||||
|     } | 			} | ||||||
|     m_groups.push_back(group); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return true; | 			EPG_TAG entry; | ||||||
|  | 			memset(&entry, 0, sizeof(EPG_TAG)); | ||||||
|  |  | ||||||
|  | 			entry.iChannelNumber = i; | ||||||
|  | 			entry.iUniqueBroadcastId = it->id; | ||||||
|  | 			entry.strTitle = it->title.c_str(); | ||||||
|  | 			entry.strPlotOutline = it->subtitle.c_str(); | ||||||
|  | 			entry.startTime = it->start; | ||||||
|  | 			entry.endTime = it->end; | ||||||
|  |  | ||||||
|  | 			pvr->TransferEpgEntry(handle, &entry); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return PVR_ERROR_NO_ERROR; | ||||||
| } | } | ||||||
|  |  | ||||||
| OctonetChannel* OctonetData::FindChannel(int64_t nativeId) | const std::string& OctonetData::getUrl(int id) const { | ||||||
|  | 	for(std::vector<OctonetChannel>::const_iterator iter = channels.begin(); iter != channels.end(); ++iter) { | ||||||
|  | 		if(iter->id == id) { | ||||||
|  | 			return iter->url; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return channels[0].url; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const std::string& OctonetData::getName(int id) const { | ||||||
|  | 	for(std::vector<OctonetChannel>::const_iterator iter = channels.begin(); iter != channels.end(); ++iter) { | ||||||
|  | 		if(iter->id == id) { | ||||||
|  | 			return iter->name; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return channels[0].name; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int OctonetData::getGroupCount(void) | ||||||
| { | { | ||||||
|   for (auto& channel : m_channels) | 	return groups.size(); | ||||||
|   { |  | ||||||
|     if (channel.nativeId == nativeId) |  | ||||||
|       return &channel; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return nullptr; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| time_t OctonetData::ParseDateTime(std::string date) | PVR_ERROR OctonetData::getGroups(ADDON_HANDLE handle, bool bRadio) | ||||||
| { | { | ||||||
|   struct tm timeinfo; | 	for (unsigned int i = 0; i < groups.size(); i++) | ||||||
|  | 	{ | ||||||
|  | 		OctonetGroup &group = groups.at(i); | ||||||
|  | 		if (group.radio == bRadio) | ||||||
|  | 		{ | ||||||
|  | 			PVR_CHANNEL_GROUP g; | ||||||
|  | 			memset(&g, 0, sizeof(PVR_CHANNEL_GROUP)); | ||||||
|  |  | ||||||
|   memset(&timeinfo, 0, sizeof(timeinfo)); | 			g.iPosition = 0; | ||||||
|  | 			g.bIsRadio = group.radio; | ||||||
|  | 			strncpy(g.strGroupName, group.name.c_str(), strlen(group.name.c_str())); | ||||||
|  |  | ||||||
|   if (date.length() > 8) | 			pvr->TransferChannelGroup(handle, &g); | ||||||
|   { | 		} | ||||||
|     sscanf(date.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", &timeinfo.tm_year, &timeinfo.tm_mon, | 	} | ||||||
|            &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); |  | ||||||
|     timeinfo.tm_mon -= 1; |  | ||||||
|     timeinfo.tm_year -= 1900; |  | ||||||
|   } |  | ||||||
|   else |  | ||||||
|   { |  | ||||||
|     sscanf(date.c_str(), "%02d:%02d:%02d", &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); |  | ||||||
|     timeinfo.tm_year = 70; // unix timestamps start 1970 |  | ||||||
|     timeinfo.tm_mday = 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   timeinfo.tm_isdst = -1; | 	return PVR_ERROR_NO_ERROR; | ||||||
|  |  | ||||||
|   return timegm(&timeinfo); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool OctonetData::LoadEPG(void) | PVR_ERROR OctonetData::getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) | ||||||
| { | { | ||||||
|   /* Reload at most every 30 seconds */ | 	OctonetGroup *g = findGroup(group.strGroupName); | ||||||
|   if (m_lastEpgLoad + 30 > time(nullptr)) | 	if (g == NULL) | ||||||
|     return false; | 		return PVR_ERROR_UNKNOWN; | ||||||
|  |  | ||||||
|   std::string jsonContent; | 	for (unsigned int i = 0; i < g->members.size(); i++) | ||||||
|   kodi::vfs::CFile f; | 	{ | ||||||
|   if (!f.OpenFile("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip", 0)) | 		OctonetChannel &channel = channels.at(g->members[i]); | ||||||
|     return false; | 		PVR_CHANNEL_GROUP_MEMBER m; | ||||||
|  | 		memset(&m, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); | ||||||
|  |  | ||||||
|   char buf[1024]; | 		strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName)); | ||||||
|   while (int read = f.Read(buf, 1024)) | 		m.iChannelUniqueId = channel.id; | ||||||
|     jsonContent.append(buf, read); | 		m.iChannelNumber = channel.id; | ||||||
|  |  | ||||||
|   f.Close(); | 		pvr->TransferChannelGroupMember(handle, &m); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|   Json::Value root; | 	return PVR_ERROR_NO_ERROR; | ||||||
|   Json::Reader reader; |  | ||||||
|  |  | ||||||
|   if (!reader.parse(jsonContent, root, false)) |  | ||||||
|     return false; |  | ||||||
|  |  | ||||||
|   const Json::Value eventList = root["EventList"]; |  | ||||||
|   OctonetChannel* channel = nullptr; |  | ||||||
|   for (unsigned int i = 0; i < eventList.size(); i++) |  | ||||||
|   { |  | ||||||
|     const Json::Value event = eventList[i]; |  | ||||||
|     OctonetEpgEntry entry; |  | ||||||
|  |  | ||||||
|     entry.start = ParseDateTime(event["Time"].asString()); |  | ||||||
|     entry.end = entry.start + ParseDateTime(event["Duration"].asString()); |  | ||||||
|     entry.title = event["Name"].asString(); |  | ||||||
|     entry.subtitle = event["Text"].asString(); |  | ||||||
|     std::string channelId = event["ID"].asString(); |  | ||||||
|     std::string epgId = channelId.substr(channelId.rfind(":") + 1); |  | ||||||
|     channelId = channelId.substr(0, channelId.rfind(":")); |  | ||||||
|  |  | ||||||
|     entry.channelId = ParseID(channelId); |  | ||||||
|     entry.id = std::stoi(epgId); |  | ||||||
|  |  | ||||||
|     if (channel == nullptr || channel->nativeId != entry.channelId) |  | ||||||
|       channel = FindChannel(entry.channelId); |  | ||||||
|  |  | ||||||
|     if (channel == nullptr) |  | ||||||
|     { |  | ||||||
|       kodi::Log(ADDON_LOG_ERROR, "EPG for unknown channel."); |  | ||||||
|       continue; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     channel->epg.push_back(entry); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   m_lastEpgLoad = time(nullptr); |  | ||||||
|   return true; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void OctonetData::Process() | OctonetGroup* OctonetData::findGroup(const std::string &name) | ||||||
| { | { | ||||||
|   return; | 	for (unsigned int i = 0; i < groups.size(); i++) | ||||||
| } | 	{ | ||||||
|  | 		if (groups.at(i).name == name) | ||||||
| PVR_ERROR OctonetData::GetChannelsAmount(int& amount) | 			return &groups.at(i); | ||||||
| { | 	} | ||||||
|   amount = m_channels.size(); |  | ||||||
|   return PVR_ERROR_NO_ERROR; | 	return NULL; | ||||||
| } |  | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results) |  | ||||||
| { |  | ||||||
|   for (unsigned int i = 0; i < m_channels.size(); i++) |  | ||||||
|   { |  | ||||||
|     OctonetChannel& channel = m_channels.at(i); |  | ||||||
|     if (channel.radio == radio) |  | ||||||
|     { |  | ||||||
|       kodi::addon::PVRChannel chan; |  | ||||||
|  |  | ||||||
|       chan.SetUniqueId(channel.id); |  | ||||||
|       chan.SetIsRadio(channel.radio); |  | ||||||
|       chan.SetChannelNumber(i); |  | ||||||
|       chan.SetChannelName(channel.name); |  | ||||||
|       chan.SetMimeType("video/x-mpegts"); |  | ||||||
|       chan.SetIsHidden(false); |  | ||||||
|  |  | ||||||
|       results.Add(chan); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return PVR_ERROR_NO_ERROR; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetEPGForChannel(int channelUid, |  | ||||||
|                                         time_t start, |  | ||||||
|                                         time_t end, |  | ||||||
|                                         kodi::addon::PVREPGTagsResultSet& results) |  | ||||||
| { |  | ||||||
|   for (unsigned int i = 0; i < m_channels.size(); i++) |  | ||||||
|   { |  | ||||||
|     OctonetChannel& chan = m_channels.at(i); |  | ||||||
|     if (channelUid != chan.id) |  | ||||||
|       continue; |  | ||||||
|  |  | ||||||
|     if (chan.epg.empty()) |  | ||||||
|     { |  | ||||||
|       LoadEPG(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // FIXME: Check if reload is needed!? |  | ||||||
|  |  | ||||||
|     time_t last_end = 0; |  | ||||||
|     for (const auto& epg : chan.epg) |  | ||||||
|     { |  | ||||||
|       if (epg.end > last_end) |  | ||||||
|         last_end = epg.end; |  | ||||||
|  |  | ||||||
|       if (epg.end < start || epg.start > end) |  | ||||||
|       { |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       kodi::addon::PVREPGTag entry; |  | ||||||
|  |  | ||||||
|       entry.SetUniqueChannelId(chan.id); |  | ||||||
|       entry.SetUniqueBroadcastId(epg.id); |  | ||||||
|       entry.SetTitle(epg.title); |  | ||||||
|       entry.SetPlotOutline(epg.subtitle); |  | ||||||
|       entry.SetStartTime(epg.start); |  | ||||||
|       entry.SetEndTime(epg.end); |  | ||||||
|  |  | ||||||
|       results.Add(entry); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (last_end < end) |  | ||||||
|       LoadEPG(); |  | ||||||
|  |  | ||||||
|     for (const auto& epg : chan.epg) |  | ||||||
|     { |  | ||||||
|       if (epg.end < start || epg.start > end) |  | ||||||
|       { |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       kodi::addon::PVREPGTag entry; |  | ||||||
|  |  | ||||||
|       entry.SetUniqueChannelId(chan.id); |  | ||||||
|       entry.SetUniqueBroadcastId(epg.id); |  | ||||||
|       entry.SetTitle(epg.title); |  | ||||||
|       entry.SetPlotOutline(epg.subtitle); |  | ||||||
|       entry.SetStartTime(epg.start); |  | ||||||
|       entry.SetEndTime(epg.end); |  | ||||||
|  |  | ||||||
|       results.Add(entry); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return PVR_ERROR_NO_ERROR; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const std::string& OctonetData::GetUrl(int id) const |  | ||||||
| { |  | ||||||
|   for (const auto& channel : m_channels) |  | ||||||
|   { |  | ||||||
|     if (channel.id == id) |  | ||||||
|     { |  | ||||||
|       return channel.url; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return m_channels[0].url; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const std::string& OctonetData::GetName(int id) const |  | ||||||
| { |  | ||||||
|   for (const auto& channel : m_channels) |  | ||||||
|   { |  | ||||||
|     if (channel.id == id) |  | ||||||
|     { |  | ||||||
|       return channel.name; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return m_channels[0].name; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetChannelGroupsAmount(int& amount) |  | ||||||
| { |  | ||||||
|   amount = m_groups.size(); |  | ||||||
|   return PVR_ERROR_NO_ERROR; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results) |  | ||||||
| { |  | ||||||
|   for (const auto& group : m_groups) |  | ||||||
|   { |  | ||||||
|     if (group.radio == radio) |  | ||||||
|     { |  | ||||||
|       kodi::addon::PVRChannelGroup g; |  | ||||||
|  |  | ||||||
|       g.SetPosition(0); |  | ||||||
|       g.SetIsRadio(group.radio); |  | ||||||
|       g.SetGroupName(group.name); |  | ||||||
|  |  | ||||||
|       results.Add(g); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return PVR_ERROR_NO_ERROR; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group, |  | ||||||
|                                               kodi::addon::PVRChannelGroupMembersResultSet& results) |  | ||||||
| { |  | ||||||
|   const OctonetGroup* g = FindGroup(group.GetGroupName()); |  | ||||||
|   if (g == nullptr) |  | ||||||
|     return PVR_ERROR_UNKNOWN; |  | ||||||
|  |  | ||||||
|   for (unsigned int i = 0; i < g->members.size(); i++) |  | ||||||
|   { |  | ||||||
|     OctonetChannel& channel = m_channels.at(g->members[i]); |  | ||||||
|     kodi::addon::PVRChannelGroupMember m; |  | ||||||
|  |  | ||||||
|     m.SetGroupName(group.GetGroupName()); |  | ||||||
|     m.SetChannelUniqueId(channel.id); |  | ||||||
|     m.SetChannelNumber(channel.id); |  | ||||||
|  |  | ||||||
|     results.Add(m); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return PVR_ERROR_NO_ERROR; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| OctonetGroup* OctonetData::FindGroup(const std::string& name) |  | ||||||
| { |  | ||||||
|   for (auto& group : m_groups) |  | ||||||
|   { |  | ||||||
|     if (group.name == name) |  | ||||||
|       return &group; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return nullptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* PVR stream handling */ |  | ||||||
| /* entirely unused, as we use standard RTSP+TS mux, which can be handlded by |  | ||||||
|  * Kodi core */ |  | ||||||
| bool OctonetData::OpenLiveStream(const kodi::addon::PVRChannel& channelinfo) |  | ||||||
| { |  | ||||||
|   return rtsp_open(GetName(channelinfo.GetUniqueId()), GetUrl(channelinfo.GetUniqueId())); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int OctonetData::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) |  | ||||||
| { |  | ||||||
|   return rtsp_read(pBuffer, iBufferSize); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void OctonetData::CloseLiveStream() |  | ||||||
| { |  | ||||||
|   rtsp_close(); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,102 +1,93 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  *  Copyright (C) 2015 Julian Scheel <julian@jusst.de> |  * Copyright (C) 2015 Julian Scheel <julian@jusst.de> | ||||||
|  *  Copyright (C) 2015 jusst technologies GmbH |  * Copyright (C) 2015 jusst technologies GmbH | ||||||
|  *  Copyright (C) 2015 Digital Devices GmbH |  * Copyright (C) 2015 Digital Devices GmbH | ||||||
|  * |  * | ||||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later |  * This program is free software; you can redistribute it and/or | ||||||
|  *  See LICENSE.md for more information. |  * 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, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, | ||||||
|  |  * USA. | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <atomic> |  | ||||||
| #include <kodi/addon-instance/PVR.h> |  | ||||||
| #include <thread> |  | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
|  | #include "p8-platform/threads/threads.h" | ||||||
|  | #include "p8-platform/util/StdString.h" | ||||||
|  | #include "client.h" | ||||||
|  |  | ||||||
| struct OctonetEpgEntry | struct OctonetEpgEntry | ||||||
| { | { | ||||||
|   int64_t channelId; | 	int64_t channelId; | ||||||
|   time_t start; | 	time_t start; | ||||||
|   time_t end; | 	time_t end; | ||||||
|   int id; | 	int id; | ||||||
|   std::string title; | 	std::string title; | ||||||
|   std::string subtitle; | 	std::string subtitle; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct OctonetChannel | struct OctonetChannel | ||||||
| { | { | ||||||
|   int64_t nativeId; | 	int64_t nativeId; | ||||||
|   std::string name; | 	std::string name; | ||||||
|   std::string url; | 	std::string url; | ||||||
|   bool radio; | 	bool radio; | ||||||
|   int id; | 	int id; | ||||||
|  |  | ||||||
|   std::vector<OctonetEpgEntry> epg; | 	std::vector<OctonetEpgEntry> epg; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct OctonetGroup | struct OctonetGroup | ||||||
| { | { | ||||||
|   std::string name; | 	std::string name; | ||||||
|   bool radio; | 	bool radio; | ||||||
|   std::vector<int> members; | 	std::vector<int> members; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ATTRIBUTE_HIDDEN OctonetData : public kodi::addon::CInstancePVRClient | class OctonetData : public P8PLATFORM::CThread | ||||||
| { | { | ||||||
| public: | 	public: | ||||||
|   OctonetData(const std::string& octonetAddress, | 		OctonetData(void); | ||||||
|               KODI_HANDLE instance, | 		virtual ~OctonetData(void); | ||||||
|               const std::string& kodiVersion); |  | ||||||
|   ~OctonetData() override; |  | ||||||
|  |  | ||||||
|   PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override; | 		virtual int getChannelCount(void); | ||||||
|   PVR_ERROR GetBackendName(std::string& name) override; | 		virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio); | ||||||
|   PVR_ERROR GetBackendVersion(std::string& version) override; |  | ||||||
|   PVR_ERROR GetConnectionString(std::string& connection) override; |  | ||||||
|   PVR_ERROR GetBackendHostname(std::string& hostname) override; |  | ||||||
|  |  | ||||||
|   PVR_ERROR OnSystemSleep() override; | 		virtual int getGroupCount(void); | ||||||
|   PVR_ERROR OnSystemWake() override; | 		virtual PVR_ERROR getGroups(ADDON_HANDLE handle, bool bRadio); | ||||||
|  | 		virtual PVR_ERROR getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group); | ||||||
|  |  | ||||||
|   PVR_ERROR GetChannelsAmount(int& amount) override; | 		virtual PVR_ERROR getEPG(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end); | ||||||
|   PVR_ERROR GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results) override; | 		const std::string& getUrl(int id) const; | ||||||
|  | 		const std::string& getName(int id) const; | ||||||
|  |  | ||||||
|   PVR_ERROR GetChannelGroupsAmount(int& amount) override; | 	protected: | ||||||
|   PVR_ERROR GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results) override; | 		virtual bool loadChannelList(void); | ||||||
|   PVR_ERROR GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group, | 		virtual bool loadEPG(void); | ||||||
|                                    kodi::addon::PVRChannelGroupMembersResultSet& results) override; | 		virtual OctonetGroup* findGroup(const std::string &name); | ||||||
|  |  | ||||||
|   PVR_ERROR GetEPGForChannel(int channelUid, | 		virtual void *Process(void); | ||||||
|                              time_t start, |  | ||||||
|                              time_t end, |  | ||||||
|                              kodi::addon::PVREPGTagsResultSet& results) override; |  | ||||||
|  |  | ||||||
|   bool OpenLiveStream(const kodi::addon::PVRChannel& channelinfo) override; | 		OctonetChannel* findChannel(int64_t nativeId); | ||||||
|   int ReadLiveStream(unsigned char* buffer, unsigned int size) override; | 		time_t parseDateTime(std::string date); | ||||||
|   void CloseLiveStream() override; | 		int64_t parseID(std::string id); | ||||||
|  |  | ||||||
| protected: | 	private: | ||||||
|   void Process(); | 		std::string serverAddress; | ||||||
|  | 		std::vector<OctonetChannel> channels; | ||||||
|  | 		std::vector<OctonetGroup> groups; | ||||||
|  |  | ||||||
|   const std::string& GetUrl(int id) const; | 		time_t lastEpgLoad; | ||||||
|   const std::string& GetName(int id) const; |  | ||||||
|  |  | ||||||
|   bool LoadChannelList(void); |  | ||||||
|   bool LoadEPG(void); |  | ||||||
|   OctonetGroup* FindGroup(const std::string& name); |  | ||||||
|   OctonetChannel* FindChannel(int64_t nativeId); |  | ||||||
|   time_t ParseDateTime(std::string date); |  | ||||||
|   int64_t ParseID(std::string id); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|   std::string m_serverAddress; |  | ||||||
|   std::vector<OctonetChannel> m_channels; |  | ||||||
|   std::vector<OctonetGroup> m_groups; |  | ||||||
|  |  | ||||||
|   time_t m_lastEpgLoad; |  | ||||||
|  |  | ||||||
|   std::atomic<bool> m_running = {false}; |  | ||||||
|   std::thread m_thread; |  | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										483
									
								
								src/Socket.cpp
									
									
									
									
									
								
							
							
						
						
									
										483
									
								
								src/Socket.cpp
									
									
									
									
									
								
							| @@ -1,18 +1,30 @@ | |||||||
| /* | /* | ||||||
|  *  Copyright (C) 2005-2020 Team Kodi |  *      Copyright (C) 2005-2011 Team XBMC | ||||||
|  *  https://kodi.tv |  *      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 <http://www.gnu.org/licenses/>. | ||||||
|  * |  * | ||||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later |  | ||||||
|  *  See LICENSE.md for more information. |  | ||||||
|  */ |  */ | ||||||
|  | #include "libXBMC_addon.h" | ||||||
| #include "Socket.h" |  | ||||||
|  |  | ||||||
| #include <cstdio> |  | ||||||
| #include <kodi/General.h> |  | ||||||
| #include <string> | #include <string> | ||||||
|  | #include "p8-platform/os.h" | ||||||
|  | #include "client.h" | ||||||
|  | #include "Socket.h" | ||||||
|  | #include <cstdio> | ||||||
|  |  | ||||||
| using namespace std; | using namespace std; | ||||||
|  | using namespace ADDON; | ||||||
|  |  | ||||||
| namespace OCTO | namespace OCTO | ||||||
| { | { | ||||||
| @@ -20,31 +32,28 @@ namespace OCTO | |||||||
| /* Master defines for client control */ | /* Master defines for client control */ | ||||||
| #define RECEIVE_TIMEOUT 6 //sec | #define RECEIVE_TIMEOUT 6 //sec | ||||||
|  |  | ||||||
| Socket::Socket(const enum SocketFamily family, | Socket::Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol) | ||||||
|                const enum SocketDomain domain, |  | ||||||
|                const enum SocketType type, |  | ||||||
|                const enum SocketProtocol protocol) |  | ||||||
| { | { | ||||||
|   m_sd = INVALID_SOCKET; |   _sd = INVALID_SOCKET; | ||||||
|   m_family = family; |   _family = family; | ||||||
|   m_domain = domain; |   _domain = domain; | ||||||
|   m_type = type; |   _type = type; | ||||||
|   m_protocol = protocol; |   _protocol = protocol; | ||||||
|   m_port = 0; |   _port = 0; | ||||||
|   memset(&m_sockaddr, 0, sizeof(m_sockaddr)); |   memset (&_sockaddr, 0, sizeof( _sockaddr ) ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| Socket::Socket() | Socket::Socket() | ||||||
| { | { | ||||||
|   // Default constructor, default settings |   // Default constructor, default settings | ||||||
|   m_sd = INVALID_SOCKET; |   _sd = INVALID_SOCKET; | ||||||
|   m_family = af_inet; |   _family = af_inet; | ||||||
|   m_domain = pf_inet; |   _domain = pf_inet; | ||||||
|   m_type = sock_stream; |   _type = sock_stream; | ||||||
|   m_protocol = tcp; |   _protocol = tcp; | ||||||
|   m_port = 0; |   _port = 0; | ||||||
|   memset(&m_sockaddr, 0, sizeof(m_sockaddr)); |   memset (&_sockaddr, 0, sizeof( _sockaddr ) ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -56,7 +65,7 @@ Socket::~Socket() | |||||||
|  |  | ||||||
| bool Socket::setHostname(const std::string& host) | bool Socket::setHostname(const std::string& host) | ||||||
| { | { | ||||||
|   m_hostname = host; |   _hostname = host; | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -64,9 +73,9 @@ bool Socket::close() | |||||||
| { | { | ||||||
|   if (is_valid()) |   if (is_valid()) | ||||||
|   { |   { | ||||||
|     if (m_sd != SOCKET_ERROR) |     if (_sd != SOCKET_ERROR) | ||||||
|       closesocket(m_sd); |       closesocket(_sd); | ||||||
|     m_sd = INVALID_SOCKET; |     _sd = INVALID_SOCKET; | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   return false; |   return false; | ||||||
| @@ -76,7 +85,7 @@ bool Socket::create() | |||||||
| { | { | ||||||
|   close(); |   close(); | ||||||
|  |  | ||||||
|   if (!osInit()) |   if(!osInit()) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| @@ -85,25 +94,25 @@ bool Socket::create() | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| bool Socket::bind(const unsigned short port) | bool Socket::bind ( const unsigned short port ) | ||||||
| { | { | ||||||
|  |  | ||||||
|   if (is_valid()) |   if (is_valid()) | ||||||
|   { |   { | ||||||
|     close(); |       close(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   m_sd = socket(m_family, m_type, m_protocol); |   _sd = socket(_family, _type, _protocol); | ||||||
|   m_port = port; |   _port = port; | ||||||
|   m_sockaddr.sin_family = (sa_family_t)m_family; |   _sockaddr.sin_family = (sa_family_t) _family; | ||||||
|   m_sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all |   _sockaddr.sin_addr.s_addr = INADDR_ANY;  //listen to all | ||||||
|   m_sockaddr.sin_port = htons(m_port); |   _sockaddr.sin_port = htons( _port ); | ||||||
|  |  | ||||||
|   int bind_return = ::bind(m_sd, (sockaddr*)(&m_sockaddr), sizeof(m_sockaddr)); |   int bind_return = ::bind(_sd, (sockaddr*)(&_sockaddr), sizeof(_sockaddr)); | ||||||
|  |  | ||||||
|   if (bind_return == -1) |   if ( bind_return == -1 ) | ||||||
|   { |   { | ||||||
|     errormessage(getLastError(), "Socket::bind"); |     errormessage( getLastError(), "Socket::bind" ); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -119,13 +128,13 @@ bool Socket::listen() const | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   int listen_return = ::listen(m_sd, SOMAXCONN); |   int listen_return = ::listen (_sd, SOMAXCONN); | ||||||
|   //This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h. |   //This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h. | ||||||
|   //linux 128//MAXCONNECTIONS =1 |   //linux 128//MAXCONNECTIONS =1 | ||||||
|  |  | ||||||
|   if (listen_return == -1) |   if (listen_return == -1) | ||||||
|   { |   { | ||||||
|     errormessage(getLastError(), "Socket::listen"); |     errormessage( getLastError(), "Socket::listen" ); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -133,24 +142,23 @@ bool Socket::listen() const | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| bool Socket::accept(Socket& new_socket) const | bool Socket::accept ( Socket& new_socket ) const | ||||||
| { | { | ||||||
|   if (!is_valid()) |   if (!is_valid()) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   socklen_t addr_length = sizeof(m_sockaddr); |   socklen_t addr_length = sizeof( _sockaddr ); | ||||||
|   new_socket.m_sd = |   new_socket._sd = ::accept(_sd, const_cast<sockaddr*>( (const sockaddr*) &_sockaddr), &addr_length ); | ||||||
|       ::accept(m_sd, const_cast<sockaddr*>((const sockaddr*)&m_sockaddr), &addr_length); |  | ||||||
|  |  | ||||||
| #ifdef TARGET_WINDOWS | #ifdef TARGET_WINDOWS | ||||||
|   if (new_socket.m_sd == INVALID_SOCKET) |   if (new_socket._sd == INVALID_SOCKET) | ||||||
| #else | #else | ||||||
|   if (new_socket.m_sd <= 0) |   if (new_socket._sd <= 0) | ||||||
| #endif | #endif | ||||||
|   { |   { | ||||||
|     errormessage(getLastError(), "Socket::accept"); |     errormessage( getLastError(), "Socket::accept" ); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -158,17 +166,17 @@ bool Socket::accept(Socket& new_socket) const | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| int Socket::send(const std::string& data) | int Socket::send ( const std::string& data ) | ||||||
| { | { | ||||||
|   return Socket::send((const char*)data.c_str(), (const unsigned int)data.size()); |   return Socket::send( (const char*) data.c_str(), (const unsigned int) data.size()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| int Socket::send(const char* data, const unsigned int len) | int Socket::send ( const char* data, const unsigned int len ) | ||||||
| { | { | ||||||
|   fd_set set_w, set_e; |   fd_set set_w, set_e; | ||||||
|   struct timeval tv; |   struct timeval tv; | ||||||
|   int result; |   int  result; | ||||||
|  |  | ||||||
|   if (!is_valid()) |   if (!is_valid()) | ||||||
|   { |   { | ||||||
| @@ -176,35 +184,35 @@ int Socket::send(const char* data, const unsigned int len) | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // fill with new data |   // fill with new data | ||||||
|   tv.tv_sec = 0; |   tv.tv_sec  = 0; | ||||||
|   tv.tv_usec = 0; |   tv.tv_usec = 0; | ||||||
|  |  | ||||||
|   FD_ZERO(&set_w); |   FD_ZERO(&set_w); | ||||||
|   FD_ZERO(&set_e); |   FD_ZERO(&set_e); | ||||||
|   FD_SET(m_sd, &set_w); |   FD_SET(_sd, &set_w); | ||||||
|   FD_SET(m_sd, &set_e); |   FD_SET(_sd, &set_e); | ||||||
|  |  | ||||||
|   result = select(FD_SETSIZE, &set_w, nullptr, &set_e, &tv); |   result = select(FD_SETSIZE, &set_w, NULL, &set_e, &tv); | ||||||
|  |  | ||||||
|   if (result < 0) |   if (result < 0) | ||||||
|   { |   { | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::send  - select failed"); |     kodi->Log(LOG_ERROR, "Socket::send  - select failed"); | ||||||
|     close(); |     close(); | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|   if (FD_ISSET(m_sd, &set_w)) |   if (FD_ISSET(_sd, &set_w)) | ||||||
|   { |   { | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::send  - failed to send data"); |     kodi->Log(LOG_ERROR, "Socket::send  - failed to send data"); | ||||||
|     close(); |     close(); | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   int status = ::send(m_sd, data, len, 0); |   int status = ::send(_sd, data, len, 0 ); | ||||||
|  |  | ||||||
|   if (status == -1) |   if (status == -1) | ||||||
|   { |   { | ||||||
|     errormessage(getLastError(), "Socket::send"); |     errormessage( getLastError(), "Socket::send"); | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::send  - failed to send data"); |     kodi->Log(LOG_ERROR, "Socket::send  - failed to send data"); | ||||||
|     close(); |     close(); | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
| @@ -212,31 +220,31 @@ int Socket::send(const char* data, const unsigned int len) | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| int Socket::sendto(const char* data, unsigned int size, bool sendcompletebuffer) | int Socket::sendto ( const char* data, unsigned int size, bool sendcompletebuffer) | ||||||
| { | { | ||||||
|   int sentbytes = 0; |   int sentbytes = 0; | ||||||
|   int i; |   int i; | ||||||
|  |  | ||||||
|   do |   do | ||||||
|   { |   { | ||||||
|     i = ::sendto(m_sd, data, size, 0, (const struct sockaddr*)&m_sockaddr, sizeof(m_sockaddr)); |     i = ::sendto(_sd, data, size, 0, (const struct sockaddr*) &_sockaddr, sizeof( _sockaddr ) ); | ||||||
|  |  | ||||||
|     if (i <= 0) |     if (i <= 0) | ||||||
|     { |     { | ||||||
|       errormessage(getLastError(), "Socket::sendto"); |       errormessage( getLastError(), "Socket::sendto"); | ||||||
|       osCleanup(); |       osCleanup(); | ||||||
|       return i; |       return i; | ||||||
|     } |     } | ||||||
|     sentbytes += i; |     sentbytes += i; | ||||||
|   } while ((sentbytes < (int)size) && (sendcompletebuffer == true)); |   } while ( (sentbytes < (int) size) && (sendcompletebuffer == true)); | ||||||
|  |  | ||||||
|   return i; |   return i; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| int Socket::receive(std::string& data, unsigned int minpacketsize) const | int Socket::receive ( std::string& data, unsigned int minpacketsize ) const | ||||||
| { | { | ||||||
|   char* buf = nullptr; |   char * buf = NULL; | ||||||
|   int status = 0; |   int status = 0; | ||||||
|  |  | ||||||
|   if (!is_valid()) |   if (!is_valid()) | ||||||
| @@ -244,10 +252,10 @@ int Socket::receive(std::string& data, unsigned int minpacketsize) const | |||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   buf = new char[minpacketsize + 1]; |   buf = new char [ minpacketsize + 1 ]; | ||||||
|   memset(buf, 0, minpacketsize + 1); |   memset ( buf, 0, minpacketsize + 1 ); | ||||||
|  |  | ||||||
|   status = receive(buf, minpacketsize, minpacketsize); |   status = receive( buf, minpacketsize, minpacketsize ); | ||||||
|  |  | ||||||
|   data = buf; |   data = buf; | ||||||
|  |  | ||||||
| @@ -257,12 +265,12 @@ int Socket::receive(std::string& data, unsigned int minpacketsize) const | |||||||
|  |  | ||||||
|  |  | ||||||
| //Receive until error or \n | //Receive until error or \n | ||||||
| bool Socket::ReadLine(string& line) | bool Socket::ReadLine (string& line) | ||||||
| { | { | ||||||
|   fd_set set_r, set_e; |   fd_set         set_r, set_e; | ||||||
|   timeval timeout; |   timeval        timeout; | ||||||
|   int retries = 6; |   int            retries = 6; | ||||||
|   char buffer[2048]; |   char           buffer[2048]; | ||||||
|  |  | ||||||
|   if (!is_valid()) |   if (!is_valid()) | ||||||
|     return false; |     return false; | ||||||
| @@ -276,20 +284,20 @@ bool Socket::ReadLine(string& line) | |||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     timeout.tv_sec = RECEIVE_TIMEOUT; |     timeout.tv_sec  = RECEIVE_TIMEOUT; | ||||||
|     timeout.tv_usec = 0; |     timeout.tv_usec = 0; | ||||||
|  |  | ||||||
|     // fill with new data |     // fill with new data | ||||||
|     FD_ZERO(&set_r); |     FD_ZERO(&set_r); | ||||||
|     FD_ZERO(&set_e); |     FD_ZERO(&set_e); | ||||||
|     FD_SET(m_sd, &set_r); |     FD_SET(_sd, &set_r); | ||||||
|     FD_SET(m_sd, &set_e); |     FD_SET(_sd, &set_e); | ||||||
|     int result = select(FD_SETSIZE, &set_r, nullptr, &set_e, &timeout); |     int result = select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout); | ||||||
|  |  | ||||||
|     if (result < 0) |     if (result < 0) | ||||||
|     { |     { | ||||||
|       kodi::Log(ADDON_LOG_DEBUG, "%s: select failed", __func__); |       kodi->Log(LOG_DEBUG, "%s: select failed", __FUNCTION__); | ||||||
|       errormessage(getLastError(), __func__); |       errormessage(getLastError(), __FUNCTION__); | ||||||
|       close(); |       close(); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| @@ -298,24 +306,20 @@ bool Socket::ReadLine(string& line) | |||||||
|     { |     { | ||||||
|       if (retries != 0) |       if (retries != 0) | ||||||
|       { |       { | ||||||
|         kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __func__, |          kodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__, retries); | ||||||
|                   retries); |          retries--; | ||||||
|         retries--; |  | ||||||
|         continue; |         continue; | ||||||
|       } |       } else { | ||||||
|       else |          kodi->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", __FUNCTION__); | ||||||
|       { |          return false; | ||||||
|         kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", |  | ||||||
|                   __func__); |  | ||||||
|         return false; |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     result = recv(m_sd, buffer, sizeof(buffer) - 1, 0); |     result = recv(_sd, buffer, sizeof(buffer) - 1, 0); | ||||||
|     if (result < 0) |     if (result < 0) | ||||||
|     { |     { | ||||||
|       kodi::Log(ADDON_LOG_DEBUG, "%s: recv failed", __func__); |       kodi->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__); | ||||||
|       errormessage(getLastError(), __func__); |       errormessage(getLastError(), __FUNCTION__); | ||||||
|       close(); |       close(); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| @@ -328,41 +332,39 @@ bool Socket::ReadLine(string& line) | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| int Socket::receive(std::string& data) const | int Socket::receive ( std::string& data) const | ||||||
| { | { | ||||||
|   char buf[MAXRECV + 1]; |   char buf[MAXRECV + 1]; | ||||||
|   int status = 0; |   int status = 0; | ||||||
|  |  | ||||||
|   if (!is_valid()) |   if ( !is_valid() ) | ||||||
|   { |   { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   memset(buf, 0, MAXRECV + 1); |   memset ( buf, 0, MAXRECV + 1 ); | ||||||
|   status = receive(buf, MAXRECV, 0); |   status = receive( buf, MAXRECV, 0 ); | ||||||
|   data = buf; |   data = buf; | ||||||
|  |  | ||||||
|   return status; |   return status; | ||||||
| } | } | ||||||
|  |  | ||||||
| int Socket::receive(char* data, | int Socket::receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const | ||||||
|                     const unsigned int buffersize, |  | ||||||
|                     const unsigned int minpacketsize) const |  | ||||||
| { | { | ||||||
|   unsigned int receivedsize = 0; |   unsigned int receivedsize = 0; | ||||||
|  |  | ||||||
|   if (!is_valid()) |   if ( !is_valid() ) | ||||||
|   { |   { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   while ((receivedsize <= minpacketsize) && (receivedsize < buffersize)) |   while ( (receivedsize <= minpacketsize) && (receivedsize < buffersize) ) | ||||||
|   { |   { | ||||||
|     int status = ::recv(m_sd, data + receivedsize, (buffersize - receivedsize), 0); |     int status = ::recv(_sd, data+receivedsize, (buffersize - receivedsize), 0 ); | ||||||
|  |  | ||||||
|     if (status == SOCKET_ERROR) |     if ( status == SOCKET_ERROR ) | ||||||
|     { |     { | ||||||
|       errormessage(getLastError(), "Socket::receive"); |       errormessage( getLastError(), "Socket::receive" ); | ||||||
|       return status; |       return status; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -373,38 +375,35 @@ int Socket::receive(char* data, | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| int Socket::recvfrom(char* data, | int Socket::recvfrom ( char* data, const int buffersize, struct sockaddr* from, socklen_t* fromlen) const | ||||||
|                      const int buffersize, |  | ||||||
|                      struct sockaddr* from, |  | ||||||
|                      socklen_t* fromlen) const |  | ||||||
| { | { | ||||||
|   int status = ::recvfrom(m_sd, data, buffersize, 0, from, fromlen); |   int status = ::recvfrom(_sd, data, buffersize, 0, from, fromlen); | ||||||
|  |  | ||||||
|   return status; |   return status; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| bool Socket::connect(const std::string& host, const unsigned short port) | bool Socket::connect ( const std::string& host, const unsigned short port ) | ||||||
| { | { | ||||||
|   close(); |   close(); | ||||||
|  |  | ||||||
|   if (!setHostname(host)) |   if ( !setHostname( host ) ) | ||||||
|   { |   { | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); |     kodi->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|   m_port = port; |   _port = port; | ||||||
|  |  | ||||||
|   char strPort[15]; |   char strPort[15]; | ||||||
|   snprintf(strPort, 15, "%hu", port); |   snprintf(strPort, 15, "%hu", port); | ||||||
|  |  | ||||||
|   struct addrinfo hints; |   struct addrinfo hints; | ||||||
|   struct addrinfo* result = nullptr; |   struct addrinfo* result = NULL; | ||||||
|   struct addrinfo* address = nullptr; |   struct addrinfo *address = NULL; | ||||||
|   memset(&hints, 0, sizeof(hints)); |   memset(&hints, 0, sizeof(hints)); | ||||||
|   hints.ai_family = m_family; |   hints.ai_family = _family; | ||||||
|   hints.ai_socktype = m_type; |   hints.ai_socktype = _type; | ||||||
|   hints.ai_protocol = m_protocol; |   hints.ai_protocol = _protocol; | ||||||
|  |  | ||||||
|   int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); |   int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); | ||||||
|   if (retval != 0) |   if (retval != 0) | ||||||
| @@ -413,18 +412,18 @@ bool Socket::connect(const std::string& host, const unsigned short port) | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   for (address = result; address != nullptr; address = address->ai_next) |   for (address = result; address != NULL; address = address->ai_next) | ||||||
|   { |   { | ||||||
|     // Create the socket |     // Create the socket | ||||||
|     m_sd = socket(address->ai_family, address->ai_socktype, address->ai_protocol); |     _sd = socket(address->ai_family, address->ai_socktype, address->ai_protocol); | ||||||
|  |  | ||||||
|     if (m_sd == INVALID_SOCKET) |     if (_sd == INVALID_SOCKET) | ||||||
|     { |     { | ||||||
|       errormessage(getLastError(), "Socket::create"); |       errormessage(getLastError(), "Socket::create"); | ||||||
|       continue; |       continue; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int status = ::connect(m_sd, address->ai_addr, address->ai_addrlen); |     int status = ::connect(_sd, address->ai_addr, address->ai_addrlen); | ||||||
|     if (status == SOCKET_ERROR) |     if (status == SOCKET_ERROR) | ||||||
|     { |     { | ||||||
|       close(); |       close(); | ||||||
| @@ -437,9 +436,9 @@ bool Socket::connect(const std::string& host, const unsigned short port) | |||||||
|  |  | ||||||
|   freeaddrinfo(result); |   freeaddrinfo(result); | ||||||
|  |  | ||||||
|   if (address == nullptr) |   if (address == NULL) | ||||||
|   { |   { | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); |     kodi->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); | ||||||
|     errormessage(getLastError(), "Socket::connect"); |     errormessage(getLastError(), "Socket::connect"); | ||||||
|     close(); |     close(); | ||||||
|     return false; |     return false; | ||||||
| @@ -450,124 +449,123 @@ bool Socket::connect(const std::string& host, const unsigned short port) | |||||||
|  |  | ||||||
| bool Socket::reconnect() | bool Socket::reconnect() | ||||||
| { | { | ||||||
|   if (is_valid()) |   if ( is_valid() ) | ||||||
|   { |   { | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return connect(m_hostname, m_port); |   return connect(_hostname, _port); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool Socket::is_valid() const | bool Socket::is_valid() const | ||||||
| { | { | ||||||
|   return (m_sd != INVALID_SOCKET); |   return (_sd != INVALID_SOCKET); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(TARGET_WINDOWS) | #if defined(TARGET_WINDOWS) | ||||||
| bool Socket::set_non_blocking(const bool b) | bool Socket::set_non_blocking ( const bool b ) | ||||||
| { | { | ||||||
|   u_long iMode; |   u_long iMode; | ||||||
|  |  | ||||||
|   if (b) |   if ( b ) | ||||||
|     iMode = 1; // enable non_blocking |     iMode = 1;  // enable non_blocking | ||||||
|   else |   else | ||||||
|     iMode = 0; // disable non_blocking |     iMode = 0;  // disable non_blocking | ||||||
|  |  | ||||||
|   if (ioctlsocket(m_sd, FIONBIO, &iMode) == -1) |   if (ioctlsocket(_sd, FIONBIO, &iMode) == -1) | ||||||
|   { |   { | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", |     kodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode); | ||||||
|               iMode); |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Socket::errormessage(int errnum, const char* functionname) const | void Socket::errormessage( int errnum, const char* functionname) const | ||||||
| { | { | ||||||
|   const char* errmsg = nullptr; |   const char* errmsg = NULL; | ||||||
|  |  | ||||||
|   switch (errnum) |   switch (errnum) | ||||||
|   { |   { | ||||||
|     case WSANOTINITIALISED: |   case WSANOTINITIALISED: | ||||||
|       errmsg = "A successful WSAStartup call must occur before using this function."; |     errmsg = "A successful WSAStartup call must occur before using this function."; | ||||||
|       break; |     break; | ||||||
|     case WSAENETDOWN: |   case WSAENETDOWN: | ||||||
|       errmsg = "The network subsystem or the associated service provider has failed"; |     errmsg = "The network subsystem or the associated service provider has failed"; | ||||||
|       break; |     break; | ||||||
|     case WSA_NOT_ENOUGH_MEMORY: |   case WSA_NOT_ENOUGH_MEMORY: | ||||||
|       errmsg = "Insufficient memory available"; |     errmsg = "Insufficient memory available"; | ||||||
|       break; |     break; | ||||||
|     case WSA_INVALID_PARAMETER: |   case WSA_INVALID_PARAMETER: | ||||||
|       errmsg = "One or more parameters are invalid"; |     errmsg = "One or more parameters are invalid"; | ||||||
|       break; |     break; | ||||||
|     case WSA_OPERATION_ABORTED: |   case WSA_OPERATION_ABORTED: | ||||||
|       errmsg = "Overlapped operation aborted"; |     errmsg = "Overlapped operation aborted"; | ||||||
|       break; |     break; | ||||||
|     case WSAEINTR: |   case WSAEINTR: | ||||||
|       errmsg = "Interrupted function call"; |     errmsg = "Interrupted function call"; | ||||||
|       break; |     break; | ||||||
|     case WSAEBADF: |   case WSAEBADF: | ||||||
|       errmsg = "File handle is not valid"; |     errmsg = "File handle is not valid"; | ||||||
|       break; |     break; | ||||||
|     case WSAEACCES: |   case WSAEACCES: | ||||||
|       errmsg = "Permission denied"; |     errmsg = "Permission denied"; | ||||||
|       break; |     break; | ||||||
|     case WSAEFAULT: |   case WSAEFAULT: | ||||||
|       errmsg = "Bad address"; |     errmsg = "Bad address"; | ||||||
|       break; |     break; | ||||||
|     case WSAEINVAL: |   case WSAEINVAL: | ||||||
|       errmsg = "Invalid argument"; |     errmsg = "Invalid argument"; | ||||||
|       break; |     break; | ||||||
|     case WSAENOTSOCK: |   case WSAENOTSOCK: | ||||||
|       errmsg = "Socket operation on nonsocket"; |     errmsg = "Socket operation on nonsocket"; | ||||||
|       break; |     break; | ||||||
|     case WSAEDESTADDRREQ: |   case WSAEDESTADDRREQ: | ||||||
|       errmsg = "Destination address required"; |     errmsg = "Destination address required"; | ||||||
|       break; |     break; | ||||||
|     case WSAEMSGSIZE: |   case WSAEMSGSIZE: | ||||||
|       errmsg = "Message too long"; |     errmsg = "Message too long"; | ||||||
|       break; |     break; | ||||||
|     case WSAEPROTOTYPE: |   case WSAEPROTOTYPE: | ||||||
|       errmsg = "Protocol wrong type for socket"; |     errmsg = "Protocol wrong type for socket"; | ||||||
|       break; |     break; | ||||||
|     case WSAENOPROTOOPT: |   case WSAENOPROTOOPT: | ||||||
|       errmsg = "Bad protocol option"; |     errmsg = "Bad protocol option"; | ||||||
|       break; |     break; | ||||||
|     case WSAEPFNOSUPPORT: |   case WSAEPFNOSUPPORT: | ||||||
|       errmsg = "Protocol family not supported"; |     errmsg = "Protocol family not supported"; | ||||||
|       break; |     break; | ||||||
|     case WSAEAFNOSUPPORT: |   case WSAEAFNOSUPPORT: | ||||||
|       errmsg = "Address family not supported by protocol family"; |     errmsg = "Address family not supported by protocol family"; | ||||||
|       break; |     break; | ||||||
|     case WSAEADDRINUSE: |   case WSAEADDRINUSE: | ||||||
|       errmsg = "Address already in use"; |     errmsg = "Address already in use"; | ||||||
|       break; |     break; | ||||||
|     case WSAECONNRESET: |   case WSAECONNRESET: | ||||||
|       errmsg = "Connection reset by peer"; |     errmsg = "Connection reset by peer"; | ||||||
|       break; |     break; | ||||||
|     case WSAHOST_NOT_FOUND: |   case WSAHOST_NOT_FOUND: | ||||||
|       errmsg = "Authoritative answer host not found"; |     errmsg = "Authoritative answer host not found"; | ||||||
|       break; |     break; | ||||||
|     case WSATRY_AGAIN: |   case WSATRY_AGAIN: | ||||||
|       errmsg = "Nonauthoritative host not found, or server failure"; |     errmsg = "Nonauthoritative host not found, or server failure"; | ||||||
|       break; |     break; | ||||||
|     case WSAEISCONN: |   case WSAEISCONN: | ||||||
|       errmsg = "Socket is already connected"; |     errmsg = "Socket is already connected"; | ||||||
|       break; |     break; | ||||||
|     case WSAETIMEDOUT: |   case WSAETIMEDOUT: | ||||||
|       errmsg = "Connection timed out"; |     errmsg = "Connection timed out"; | ||||||
|       break; |     break; | ||||||
|     case WSAECONNREFUSED: |   case WSAECONNREFUSED: | ||||||
|       errmsg = "Connection refused"; |     errmsg = "Connection refused"; | ||||||
|       break; |     break; | ||||||
|     case WSANO_DATA: |   case WSANO_DATA: | ||||||
|       errmsg = "Valid name, no data record of requested type"; |     errmsg = "Valid name, no data record of requested type"; | ||||||
|       break; |     break; | ||||||
|     default: |   default: | ||||||
|       errmsg = "WSA Error"; |     errmsg = "WSA Error"; | ||||||
|   } |   } | ||||||
|   kodi::Log(ADDON_LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); |   kodi->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); | ||||||
| } | } | ||||||
|  |  | ||||||
| int Socket::getLastError() const | int Socket::getLastError() const | ||||||
| @@ -581,15 +579,15 @@ bool Socket::osInit() | |||||||
| { | { | ||||||
|   win_usage_count++; |   win_usage_count++; | ||||||
|   // initialize winsock: |   // initialize winsock: | ||||||
|   if (WSAStartup(MAKEWORD(2, 2), &m_wsaData) != 0) |   if (WSAStartup(MAKEWORD(2,2),&_wsaData) != 0) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   WORD wVersionRequested = MAKEWORD(2, 2); |   WORD wVersionRequested = MAKEWORD(2,2); | ||||||
|  |  | ||||||
|   // check version |   // check version | ||||||
|   if (m_wsaData.wVersion != wVersionRequested) |   if (_wsaData.wVersion != wVersionRequested) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| @@ -600,42 +598,42 @@ bool Socket::osInit() | |||||||
| void Socket::osCleanup() | void Socket::osCleanup() | ||||||
| { | { | ||||||
|   win_usage_count--; |   win_usage_count--; | ||||||
|   if (win_usage_count == 0) |   if(win_usage_count == 0) | ||||||
|   { |   { | ||||||
|     WSACleanup(); |     WSACleanup(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||||
| bool Socket::set_non_blocking(const bool b) | bool Socket::set_non_blocking ( const bool b ) | ||||||
| { | { | ||||||
|   int opts; |   int opts; | ||||||
|  |  | ||||||
|   opts = fcntl(m_sd, F_GETFL); |   opts = fcntl(_sd, F_GETFL); | ||||||
|  |  | ||||||
|   if (opts < 0) |   if ( opts < 0 ) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (b) |   if ( b ) | ||||||
|     opts = (opts | O_NONBLOCK); |     opts = ( opts | O_NONBLOCK ); | ||||||
|   else |   else | ||||||
|     opts = (opts & ~O_NONBLOCK); |     opts = ( opts & ~O_NONBLOCK ); | ||||||
|  |  | ||||||
|   if (fcntl(m_sd, F_SETFL, opts) == -1) |   if(fcntl (_sd , F_SETFL, opts) == -1) | ||||||
|   { |   { | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); |     kodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Socket::errormessage(int errnum, const char* functionname) const | void Socket::errormessage( int errnum, const char* functionname) const | ||||||
| { | { | ||||||
|   const char* errmsg = nullptr; |   const char* errmsg = NULL; | ||||||
|  |  | ||||||
|   switch (errnum) |   switch ( errnum ) | ||||||
|   { |   { | ||||||
|     case EAGAIN: //same as EWOULDBLOCK |     case EAGAIN: //same as EWOULDBLOCK | ||||||
|       errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block"; |       errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block"; | ||||||
| @@ -662,8 +660,7 @@ void Socket::errormessage(int errnum, const char* functionname) const | |||||||
|       errmsg = "ENOTSOCK: The argument is not a valid socket"; |       errmsg = "ENOTSOCK: The argument is not a valid socket"; | ||||||
|       break; |       break; | ||||||
|     case EMSGSIZE: |     case EMSGSIZE: | ||||||
|       errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the " |       errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the message to be sent made this impossible"; | ||||||
|                "message to be sent made this impossible"; |  | ||||||
|       break; |       break; | ||||||
|     case ENOBUFS: |     case ENOBUFS: | ||||||
|       errmsg = "ENOBUFS: The output queue for a network interface was full"; |       errmsg = "ENOBUFS: The output queue for a network interface was full"; | ||||||
| @@ -675,8 +672,7 @@ void Socket::errormessage(int errnum, const char* functionname) const | |||||||
|       errmsg = "EPIPE: The local end has been shut down on a connection oriented socket"; |       errmsg = "EPIPE: The local end has been shut down on a connection oriented socket"; | ||||||
|       break; |       break; | ||||||
|     case EPROTONOSUPPORT: |     case EPROTONOSUPPORT: | ||||||
|       errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported " |       errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported within this domain"; | ||||||
|                "within this domain"; |  | ||||||
|       break; |       break; | ||||||
|     case EAFNOSUPPORT: |     case EAFNOSUPPORT: | ||||||
|       errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family"; |       errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family"; | ||||||
| @@ -688,16 +684,13 @@ void Socket::errormessage(int errnum, const char* functionname) const | |||||||
|       errmsg = "EMFILE: Process file table overflow"; |       errmsg = "EMFILE: Process file table overflow"; | ||||||
|       break; |       break; | ||||||
|     case EACCES: |     case EACCES: | ||||||
|       errmsg = |       errmsg = "EACCES: Permission to create a socket of the specified type and/or protocol is denied"; | ||||||
|           "EACCES: Permission to create a socket of the specified type and/or protocol is denied"; |  | ||||||
|       break; |       break; | ||||||
|     case ECONNREFUSED: |     case ECONNREFUSED: | ||||||
|       errmsg = "ECONNREFUSED: A remote host refused to allow the network connection (typically " |       errmsg = "ECONNREFUSED: A remote host refused to allow the network connection (typically because it is not running the requested service)"; | ||||||
|                "because it is not running the requested service)"; |  | ||||||
|       break; |       break; | ||||||
|     case ENOTCONN: |     case ENOTCONN: | ||||||
|       errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not " |       errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not been connected"; | ||||||
|                "been connected"; |  | ||||||
|       break; |       break; | ||||||
|     //case E: |     //case E: | ||||||
|     //	errmsg = ""; |     //	errmsg = ""; | ||||||
| @@ -706,7 +699,7 @@ void Socket::errormessage(int errnum, const char* functionname) const | |||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   kodi::Log(ADDON_LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); |   kodi->Log(LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); | ||||||
| } | } | ||||||
|  |  | ||||||
| int Socket::getLastError() const | int Socket::getLastError() const | ||||||
|   | |||||||
							
								
								
									
										428
									
								
								src/Socket.h
									
									
									
									
									
								
							
							
						
						
									
										428
									
								
								src/Socket.h
									
									
									
									
									
								
							| @@ -1,73 +1,83 @@ | |||||||
| /* | /* | ||||||
|  *  Copyright (C) 2005-2020 Team Kodi |  *      Copyright (C) 2005-2011 Team XBMC | ||||||
|  *  https://kodi.tv |  *      http://www.xbmc.org | ||||||
|  |  * | ||||||
|  |  *  This Program is free software; you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation; either version 2, or (at your option) | ||||||
|  |  *  any later version. | ||||||
|  |  * | ||||||
|  |  *  This Program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  * |  * | ||||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later |  | ||||||
|  *  See LICENSE.md for more information. |  | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| //Include platform specific datatypes, header files, defines and constants: | //Include platform specific datatypes, header files, defines and constants: | ||||||
| #if defined TARGET_WINDOWS | #if defined TARGET_WINDOWS | ||||||
| #define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support |   #define WIN32_LEAN_AND_MEAN           // Enable LEAN_AND_MEAN support | ||||||
| #pragma warning(disable : 4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" |   #pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" | ||||||
| #include <WS2tcpip.h> |   #include <winsock2.h> | ||||||
| #include <winsock2.h> |   #include <WS2tcpip.h> | ||||||
| #pragma warning(default : 4005) |   #pragma warning(default:4005) | ||||||
| #include <windows.h> |   #include <windows.h> | ||||||
|  |  | ||||||
| #ifndef NI_MAXHOST |   #ifndef NI_MAXHOST | ||||||
| #define NI_MAXHOST 1025 |     #define NI_MAXHOST 1025 | ||||||
| #endif |   #endif | ||||||
|  |  | ||||||
| #ifndef socklen_t |   #ifndef socklen_t | ||||||
| typedef int socklen_t; |     typedef int socklen_t; | ||||||
| #endif |   #endif | ||||||
| #ifndef ipaddr_t |   #ifndef ipaddr_t | ||||||
| typedef unsigned long ipaddr_t; |     typedef unsigned long ipaddr_t; | ||||||
| #endif |   #endif | ||||||
| #ifndef port_t |   #ifndef port_t | ||||||
| typedef unsigned short port_t; |     typedef unsigned short port_t; | ||||||
| #endif |   #endif | ||||||
| #ifndef sa_family_t |   #ifndef sa_family_t | ||||||
| #define sa_family_t ADDRESS_FAMILY |     #define sa_family_t ADDRESS_FAMILY | ||||||
| #endif |   #endif | ||||||
| #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||||
| #ifdef SOCKADDR_IN | #ifdef SOCKADDR_IN | ||||||
| #undef SOCKADDR_IN | #undef SOCKADDR_IN | ||||||
| #endif | #endif | ||||||
| #include <arpa/inet.h> /* for inet_pton */ |   #include <sys/types.h>     /* for socket,connect */ | ||||||
| #include <errno.h> |   #include <sys/socket.h>    /* for socket,connect */ | ||||||
| #include <fcntl.h> |   #include <sys/un.h>        /* for Unix socket */ | ||||||
| #include <netdb.h> /* for gethostbyname */ |   #include <arpa/inet.h>     /* for inet_pton */ | ||||||
| #include <netinet/in.h> /* for htons */ |   #include <netdb.h>         /* for gethostbyname */ | ||||||
| #include <sys/socket.h> /* for socket,connect */ |   #include <netinet/in.h>    /* for htons */ | ||||||
| #include <sys/types.h> /* for socket,connect */ |   #include <unistd.h>        /* for read, write, close */ | ||||||
| #include <sys/un.h> /* for Unix socket */ |   #include <errno.h> | ||||||
| #include <unistd.h> /* for read, write, close */ |   #include <fcntl.h> | ||||||
|  |  | ||||||
| typedef int SOCKET; |   typedef int SOCKET; | ||||||
| typedef sockaddr SOCKADDR; |   typedef sockaddr SOCKADDR; | ||||||
| typedef sockaddr_in SOCKADDR_IN; |   typedef sockaddr_in SOCKADDR_IN; | ||||||
| #ifndef INVALID_SOCKET |   #ifndef INVALID_SOCKET | ||||||
| #define INVALID_SOCKET (-1) |   #define INVALID_SOCKET (-1) | ||||||
| #endif |   #endif | ||||||
| #define SOCKET_ERROR (-1) |   #define SOCKET_ERROR (-1) | ||||||
|  |  | ||||||
| #define closesocket(sd) ::close(sd) |   #define closesocket(sd) ::close(sd) | ||||||
| #else | #else | ||||||
| #error Platform specific socket support is not yet available on this platform! |   #error Platform specific socket support is not yet available on this platform! | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #include <string> |  | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| namespace OCTO | namespace OCTO | ||||||
| { | { | ||||||
|  |  | ||||||
| #define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused" | #define MAXCONNECTIONS 1  ///< Maximum number of pending connections before "Connection refused" | ||||||
| #define MAXRECV 1500 ///< Maximum packet size | #define MAXRECV 1500      ///< Maximum packet size | ||||||
|  |  | ||||||
| enum SocketFamily | enum SocketFamily | ||||||
| { | { | ||||||
| @@ -78,10 +88,10 @@ enum SocketFamily | |||||||
|  |  | ||||||
| enum SocketDomain | enum SocketDomain | ||||||
| { | { | ||||||
| #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD |   #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||||
|   pf_unix = PF_UNIX, |     pf_unix  = PF_UNIX, | ||||||
|   pf_local = PF_LOCAL, |     pf_local = PF_LOCAL, | ||||||
| #endif |   #endif | ||||||
|   pf_inet = PF_INET |   pf_inet = PF_INET | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -99,187 +109,197 @@ enum SocketProtocol | |||||||
|  |  | ||||||
| class Socket | class Socket | ||||||
| { | { | ||||||
| public: |   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 |     /*! | ||||||
|  |      * 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) { m_family = family; }; |  | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket setDomain |      * Socket setFamily | ||||||
|    * \param domain    Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet |      * \param family    Can be af_inet or af_inet6. Default: af_inet | ||||||
|    */ |      */ | ||||||
|   void setDomain(const enum SocketDomain domain) { m_domain = domain; }; |     void setFamily(const enum SocketFamily family) | ||||||
|  |     { | ||||||
|  |       _family = family; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket setType |      * Socket setDomain | ||||||
|    * \param type    Can be sock_stream or sock_dgram. Default: sock_stream. |      * \param domain    Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet | ||||||
|    */ |      */ | ||||||
|   void setType(const enum SocketType type) { m_type = type; }; |     void setDomain(const enum SocketDomain domain) | ||||||
|  |     { | ||||||
|  |       _domain = domain; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket setProtocol |      * Socket setType | ||||||
|    * \param protocol    Can be tcp or udp. Default: tcp. |      * \param type    Can be sock_stream or sock_dgram. Default: sock_stream. | ||||||
|    */ |      */ | ||||||
|   void setProtocol(const enum SocketProtocol protocol) { m_protocol = protocol; }; |     void setType(const enum SocketType type) | ||||||
|  |     { | ||||||
|  |       _type = type; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket setPort |      * Socket setProtocol | ||||||
|    * \param port    port number for socket communication |      * \param protocol    Can be tcp or udp. Default: tcp. | ||||||
|    */ |      */ | ||||||
|   void setPort(const unsigned short port) { m_sockaddr.sin_port = htons(port); }; |     void setProtocol(const enum SocketProtocol protocol) | ||||||
|  |     { | ||||||
|  |       _protocol = protocol; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|   bool setHostname(const std::string& host); |     /*! | ||||||
|  |      * Socket setPort | ||||||
|  |      * \param port    port number for socket communication | ||||||
|  |      */ | ||||||
|  |     void setPort (const unsigned short port) | ||||||
|  |     { | ||||||
|  |       _sockaddr.sin_port = htons ( port ); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|   // Server initialization |     bool setHostname ( const std::string& host ); | ||||||
|  |  | ||||||
|   /*! |     // Server initialization | ||||||
|    * Socket create |  | ||||||
|    * Create a new socket |  | ||||||
|    * \return     True if succesful |  | ||||||
|    */ |  | ||||||
|   bool create(); |  | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket close |      * Socket create | ||||||
|    * Close the socket |      * Create a new socket | ||||||
|    * \return     True if succesful |      * \return     True if succesful | ||||||
|    */ |      */ | ||||||
|   bool close(); |     bool create(); | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket bind |      * Socket close | ||||||
|    */ |      * Close the socket | ||||||
|   bool bind(const unsigned short port); |      * \return     True if succesful | ||||||
|   bool listen() const; |      */ | ||||||
|   bool accept(Socket& socket) const; |     bool close(); | ||||||
|  |  | ||||||
|   // Client initialization |     /*! | ||||||
|   bool connect(const std::string& host, const unsigned short port); |      * Socket bind | ||||||
|  |      */ | ||||||
|  |     bool bind ( const unsigned short port ); | ||||||
|  |     bool listen() const; | ||||||
|  |     bool accept ( Socket& socket ) const; | ||||||
|  |  | ||||||
|   bool reconnect(); |     // Client initialization | ||||||
|  |     bool connect ( const std::string& host, const unsigned short port ); | ||||||
|  |  | ||||||
|   // Data Transmission |     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 |      * Socket send function | ||||||
|    * |      * | ||||||
|    * \param data    Pointer to a character array of size 'size' with the data to transmit |      * \param data    Reference to a std::string 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 | ||||||
|    * \return    Number of bytes send or -1 in case of an error |      */ | ||||||
|    */ |     int send ( const std::string& data ); | ||||||
|   int send(const char* data, const unsigned int size); |  | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket sendto function |      * Socket send function | ||||||
|    * |      * | ||||||
|    * \param data    Reference to a std::string with the data to transmit |      * \param data    Pointer to a character array of size 'size' with the data to transmit | ||||||
|    * \param size    Length of 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 | ||||||
|    * \return    Number of bytes send or -1 in case of an error |      */ | ||||||
|    */ |     int send ( const char* data, const unsigned int size ); | ||||||
|   int sendto(const char* data, unsigned int size, bool sendcompletebuffer = false); |  | ||||||
|   // Data Receive |  | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket receive function |      * Socket sendto function | ||||||
|    * |      * | ||||||
|    * \param data    Reference to a std::string for storage of the received data. |      * \param data    Reference to a std::string with the data to transmit | ||||||
|    * \param minpacketsize    The minimum number of bytes that should be received before returning from this function |      * \param size    Length of the data to transmit | ||||||
|    * \return    Number of bytes received or SOCKET_ERROR |      * \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 receive(std::string& data, unsigned int minpacketsize) const; |      */ | ||||||
|  |     int sendto ( const char* data, unsigned int size, bool sendcompletebuffer = false); | ||||||
|  |     // Data Receive | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket receive function |      * Socket receive function | ||||||
|    * |      * | ||||||
|    * \param data    Reference to a std::string for storage of the received data. |      * \param data    Reference to a std::string for storage of the received data. | ||||||
|    * \return    Number of bytes received or SOCKET_ERROR |      * \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) const; |      */ | ||||||
|  |     int receive ( std::string& data, unsigned int minpacketsize ) const; | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket receive function |      * Socket receive function | ||||||
|    * |      * | ||||||
|    * \param data    Pointer to a character array of size buffersize. Used to store the received data. |      * \param data    Reference to a std::string for storage of the received data. | ||||||
|    * \param buffersize    Size of the 'data' buffer |      * \return    Number of bytes received or SOCKET_ERROR | ||||||
|    * \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 ( std::string& data ) const; | ||||||
|    */ |  | ||||||
|   int receive(char* data, const unsigned int buffersize, const unsigned int minpacketsize) const; |  | ||||||
|  |  | ||||||
|   /*! |     /*! | ||||||
|    * Socket recvfrom function |      * Socket receive function | ||||||
|    * |      * | ||||||
|    * \param data    Pointer to a character array of size buffersize. Used to store the received data. |      * \param data    Pointer to a character array of size buffersize. Used to store the received data. | ||||||
|    * \param buffersize    Size of the 'data' buffer |      * \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 minpacketsize    Specifies the minimum number of bytes that need to be received before returning | ||||||
|    * \param fromlen    Optional, only required if 'from' is given: length of from struct |      * \return    Number of bytes received or SOCKET_ERROR | ||||||
|    * \return    Number of bytes received or SOCKET_ERROR |      */ | ||||||
|    */ |     int receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const; | ||||||
|   int recvfrom(char* data, |  | ||||||
|                const int buffersize, |  | ||||||
|                struct sockaddr* from = nullptr, |  | ||||||
|                socklen_t* fromlen = nullptr) const; |  | ||||||
|  |  | ||||||
|   bool set_non_blocking(const bool); |     /*! | ||||||
|  |      * 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 ReadLine(std::string& line); |     bool set_non_blocking ( const bool ); | ||||||
|  |  | ||||||
|   bool is_valid() const; |     bool ReadLine (std::string& line); | ||||||
|  |  | ||||||
| private: |     bool is_valid() const; | ||||||
|   SOCKET m_sd; ///< Socket Descriptor |  | ||||||
|   SOCKADDR_IN m_sockaddr; ///< Socket Address |  | ||||||
|   //struct addrinfo* m_addrinfo;         ///< Socket address info |  | ||||||
|   std::string m_hostname; ///< Hostname |  | ||||||
|   unsigned short m_port; ///< Port number |  | ||||||
|  |  | ||||||
|   enum SocketFamily m_family; ///< Socket Address Family |   private: | ||||||
|   enum SocketProtocol m_protocol; ///< Socket Protocol |  | ||||||
|   enum SocketType m_type; ///< Socket Type |  | ||||||
|   enum SocketDomain m_domain; ///< Socket domain |  | ||||||
|  |  | ||||||
| #ifdef TARGET_WINDOWS |     SOCKET _sd;                         ///< Socket Descriptor | ||||||
|   WSADATA m_wsaData; ///< Windows Socket data |     SOCKADDR_IN _sockaddr;              ///< Socket Address | ||||||
|   static int |     //struct addrinfo* _addrinfo;         ///< Socket address info | ||||||
|       win_usage_count; ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used |     std::string _hostname;              ///< Hostname | ||||||
| #endif |     unsigned short _port;               ///< Port number | ||||||
|  |  | ||||||
|   void errormessage(int errornum, const char* functionname = nullptr) const; |     enum SocketFamily _family;          ///< Socket Address Family | ||||||
|   int getLastError(void) const; |     enum SocketProtocol _protocol;      ///< Socket Protocol | ||||||
|   bool osInit(); |     enum SocketType _type;              ///< Socket Type | ||||||
|   void osCleanup(); |     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 OCTO | } //namespace OCTO | ||||||
|   | |||||||
| @@ -1,62 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  Copyright (C) 2015 Julian Scheel <julian@jusst.de> |  | ||||||
|  *  Copyright (C) 2015 jusst technologies GmbH |  | ||||||
|  *  Copyright (C) 2015 Digital Devices GmbH |  | ||||||
|  * |  | ||||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later |  | ||||||
|  *  See LICENSE.md for more information. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "addon.h" |  | ||||||
|  |  | ||||||
| #include "OctonetData.h" |  | ||||||
|  |  | ||||||
| ADDON_STATUS COctonetAddon::SetSetting(const std::string& settingName, |  | ||||||
|                                        const kodi::CSettingValue& settingValue) |  | ||||||
| { |  | ||||||
|   /* For simplicity do a full addon restart whenever settings are |  | ||||||
|    * changed */ |  | ||||||
|   return ADDON_STATUS_NEED_RESTART; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ADDON_STATUS COctonetAddon::CreateInstance(int instanceType, |  | ||||||
|                                            const std::string& instanceID, |  | ||||||
|                                            KODI_HANDLE instance, |  | ||||||
|                                            const std::string& version, |  | ||||||
|                                            KODI_HANDLE& addonInstance) |  | ||||||
| { |  | ||||||
|   if (instanceType == ADDON_INSTANCE_PVR) |  | ||||||
|   { |  | ||||||
|     kodi::Log(ADDON_LOG_DEBUG, "%s: Creating octonet pvr instance", __func__); |  | ||||||
|  |  | ||||||
|     /* IP or hostname of the octonet to be connected to */ |  | ||||||
|     std::string octonetAddress = kodi::GetSettingString("octonetAddress"); |  | ||||||
|  |  | ||||||
|     OctonetData* usedInstance = new OctonetData(octonetAddress, instance, version); |  | ||||||
|     addonInstance = usedInstance; |  | ||||||
|  |  | ||||||
|     m_usedInstances.emplace(instanceID, usedInstance); |  | ||||||
|     return ADDON_STATUS_OK; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return ADDON_STATUS_UNKNOWN; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void COctonetAddon::DestroyInstance(int instanceType, |  | ||||||
|                                     const std::string& instanceID, |  | ||||||
|                                     KODI_HANDLE addonInstance) |  | ||||||
| { |  | ||||||
|   if (instanceType == ADDON_INSTANCE_PVR) |  | ||||||
|   { |  | ||||||
|     kodi::Log(ADDON_LOG_DEBUG, "%s: Destoying octonet pvr instance", __func__); |  | ||||||
|  |  | ||||||
|     const auto& it = m_usedInstances.find(instanceID); |  | ||||||
|     if (it != m_usedInstances.end()) |  | ||||||
|     { |  | ||||||
|       m_usedInstances.erase(it); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ADDONCREATOR(COctonetAddon) |  | ||||||
							
								
								
									
										36
									
								
								src/addon.h
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/addon.h
									
									
									
									
									
								
							| @@ -1,36 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  Copyright (C) 2015 Julian Scheel <julian@jusst.de> |  | ||||||
|  *  Copyright (C) 2015 jusst technologies GmbH |  | ||||||
|  *  Copyright (C) 2015 Digital Devices GmbH |  | ||||||
|  * |  | ||||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later |  | ||||||
|  *  See LICENSE.md for more information. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <kodi/AddonBase.h> |  | ||||||
| #include <unordered_map> |  | ||||||
|  |  | ||||||
| class OctonetData; |  | ||||||
|  |  | ||||||
| class ATTRIBUTE_HIDDEN COctonetAddon : public kodi::addon::CAddonBase |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|   COctonetAddon() = default; |  | ||||||
|  |  | ||||||
|   ADDON_STATUS SetSetting(const std::string& settingName, |  | ||||||
|                           const kodi::CSettingValue& settingValue) override; |  | ||||||
|   ADDON_STATUS CreateInstance(int instanceType, |  | ||||||
|                               const std::string& instanceID, |  | ||||||
|                               KODI_HANDLE instance, |  | ||||||
|                               const std::string& version, |  | ||||||
|                               KODI_HANDLE& addonInstance) override; |  | ||||||
|   void DestroyInstance(int instanceType, |  | ||||||
|                        const std::string& instanceID, |  | ||||||
|                        KODI_HANDLE addonInstance) override; |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|   std::unordered_map<std::string, OctonetData*> m_usedInstances; |  | ||||||
| }; |  | ||||||
							
								
								
									
										318
									
								
								src/client.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								src/client.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,318 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Julian Scheel <julian@jusst.de> | ||||||
|  |  * Copyright (C) 2015 jusst technologies GmbH | ||||||
|  |  * Copyright (C) 2015 Digital Devices GmbH | ||||||
|  |  * | ||||||
|  |  * 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, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, | ||||||
|  |  * USA. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "client.h" | ||||||
|  | #include <xbmc_pvr_dll.h> | ||||||
|  | #include <libXBMC_addon.h> | ||||||
|  | #include <p8-platform/util/util.h> | ||||||
|  | #include <libKODI_guilib.h> | ||||||
|  |  | ||||||
|  | #include "OctonetData.h" | ||||||
|  | #include "rtsp_client.hpp" | ||||||
|  |  | ||||||
|  | using namespace ADDON; | ||||||
|  |  | ||||||
|  | /* setting variables with defaults */ | ||||||
|  | std::string octonetAddress = ""; | ||||||
|  |  | ||||||
|  | /* internal state variables */ | ||||||
|  | ADDON_STATUS addonStatus = ADDON_STATUS_UNKNOWN; | ||||||
|  | CHelper_libXBMC_addon *kodi = NULL; | ||||||
|  | CHelper_libXBMC_pvr *pvr = NULL; | ||||||
|  |  | ||||||
|  | OctonetData *data = NULL; | ||||||
|  |  | ||||||
|  | /* KODI Core Addon functions | ||||||
|  |  * see xbmc_addon_dll.h */ | ||||||
|  |  | ||||||
|  | extern "C" { | ||||||
|  |  | ||||||
|  | void ADDON_ReadSettings(void) | ||||||
|  | { | ||||||
|  | 	char buffer[2048]; | ||||||
|  | 	if (kodi->GetSetting("octonetAddress", &buffer)) | ||||||
|  | 		octonetAddress = buffer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ADDON_STATUS ADDON_Create(void *callbacks, void* props) | ||||||
|  | { | ||||||
|  | 	if (callbacks == NULL || props == NULL) | ||||||
|  | 		return ADDON_STATUS_UNKNOWN; | ||||||
|  |  | ||||||
|  | 	PVR_PROPERTIES *pvrprops = (PVR_PROPERTIES*)props; | ||||||
|  | 	kodi = new CHelper_libXBMC_addon; | ||||||
|  | 	if (!kodi->RegisterMe(callbacks)) { | ||||||
|  | 		kodi->Log(LOG_ERROR, "%s: Failed to register octonet addon", __func__); | ||||||
|  | 		SAFE_DELETE(kodi); | ||||||
|  | 		return ADDON_STATUS_PERMANENT_FAILURE; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pvr = new CHelper_libXBMC_pvr; | ||||||
|  | 	if (!pvr->RegisterMe(callbacks)) { | ||||||
|  | 		kodi->Log(LOG_ERROR, "%s: Failed to register octonet pvr addon", __func__); | ||||||
|  | 		SAFE_DELETE(pvr); | ||||||
|  | 		SAFE_DELETE(kodi); | ||||||
|  | 		return ADDON_STATUS_PERMANENT_FAILURE; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	kodi->Log(LOG_DEBUG, "%s: Creating octonet pvr addon", __func__); | ||||||
|  | 	ADDON_ReadSettings(); | ||||||
|  |  | ||||||
|  | 	data = new OctonetData; | ||||||
|  |  | ||||||
|  | 	addonStatus = ADDON_STATUS_OK; | ||||||
|  | 	return addonStatus; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ADDON_Stop() {} /* no-op */ | ||||||
|  |  | ||||||
|  | void ADDON_Destroy() | ||||||
|  | { | ||||||
|  | 	delete pvr; | ||||||
|  | 	delete kodi; | ||||||
|  | 	addonStatus = ADDON_STATUS_UNKNOWN; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ADDON_STATUS ADDON_GetStatus() | ||||||
|  | { | ||||||
|  | 	return addonStatus; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ADDON_HasSettings() | ||||||
|  | { | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue) | ||||||
|  | { | ||||||
|  | 	/* For simplicity do a full addon restart whenever settings are | ||||||
|  | 	 * changed */ | ||||||
|  | 	return ADDON_STATUS_NEED_RESTART; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ADDON_FreeSettings() {} /* no-op */ | ||||||
|  | void ADDON_Announce(const char *flag, const char *sender, const char *message, const void *data) {} /* no-op */ | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* KODI PVR Addon functions | ||||||
|  |  * see xbmc_pvr_dll.h */ | ||||||
|  | extern "C" | ||||||
|  | { | ||||||
|  |  | ||||||
|  | const char* GetPVRAPIVersion(void) | ||||||
|  | { | ||||||
|  | 	return XBMC_PVR_API_VERSION; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char* GetMininumPVRAPIVersion(void) | ||||||
|  | { | ||||||
|  | 	return XBMC_PVR_MIN_API_VERSION; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char* GetGUIAPIVersion(void) | ||||||
|  | { | ||||||
|  | 	return KODI_GUILIB_API_VERSION; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char* GetMininumGUIAPIVersion(void) | ||||||
|  | { | ||||||
|  | 	return KODI_GUILIB_MIN_API_VERSION; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities) | ||||||
|  | { | ||||||
|  | 	pCapabilities->bSupportsTV = true; | ||||||
|  | 	pCapabilities->bSupportsRadio = true; | ||||||
|  | 	pCapabilities->bSupportsChannelGroups = true; | ||||||
|  | 	pCapabilities->bSupportsEPG = true; | ||||||
|  |  | ||||||
|  | 	return PVR_ERROR_NO_ERROR; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char* GetBackendName(void) | ||||||
|  | { | ||||||
|  | 	return "Digital Devices Octopus NET Client"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char* GetBackendVersion(void) | ||||||
|  | { | ||||||
|  | 	return XBMC_PVR_API_VERSION; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char* GetConnectionString(void) | ||||||
|  | { | ||||||
|  | 	return "connected"; // FIXME: translate? | ||||||
|  | } | ||||||
|  |  | ||||||
|  | PVR_ERROR GetDriveSpace(long long* iTotal, long long* iUsed) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook, const PVR_MENUHOOK_DATA &item) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  |  | ||||||
|  | void OnSystemSleep() { | ||||||
|  | 	kodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); | ||||||
|  | 	// FIXME: Disconnect? | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OnSystemWake() { | ||||||
|  | 	kodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); | ||||||
|  | 	// FIXME:Reconnect? | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OnPowerSavingActivated() {} | ||||||
|  | void OnPowerSavingDeactivated() {} | ||||||
|  |  | ||||||
|  | /* EPG */ | ||||||
|  | PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL& channel, time_t iStart, time_t iEnd) | ||||||
|  | { | ||||||
|  | 	return data->getEPG(handle, channel, iStart, iEnd); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Channel groups */ | ||||||
|  | int GetChannelGroupsAmount(void) | ||||||
|  | { | ||||||
|  | 	return data->getGroupCount(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) | ||||||
|  | { | ||||||
|  | 	return data->getGroups(handle, bRadio); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group) | ||||||
|  | { | ||||||
|  | 	return data->getGroupMembers(handle, group); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Channels */ | ||||||
|  | PVR_ERROR OpenDialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  |  | ||||||
|  | int GetChannelsAmount(void) | ||||||
|  | { | ||||||
|  | 	return data->getChannelCount(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) | ||||||
|  | { | ||||||
|  | 	return data->getChannels(handle, bRadio); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | PVR_ERROR DeleteChannel(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR RenameChannel(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR MoveChannel(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR OpenDialogChannelSettings(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  |  | ||||||
|  | /* Recordings */ | ||||||
|  | int GetRecordingsAmount(bool deleted) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR DeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR DeleteAllRecordingsFromTrash() { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR RenameRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING& recording, int count) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY edl[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | int GetTimersAmount(void) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR GetTimers(ADDON_HANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR AddTimer(const PVR_TIMER& timer) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR DeleteTimer(const PVR_TIMER& timer, bool bForceDelete) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  | PVR_ERROR UpdateTimer(const PVR_TIMER& timer) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  |  | ||||||
|  | /* PVR stream handling */ | ||||||
|  | /* entirely unused, as we use standard RTSP+TS mux, which can be handlded by | ||||||
|  |  * Kodi core */ | ||||||
|  | bool OpenLiveStream(const PVR_CHANNEL& channel) { | ||||||
|  | 	return rtsp_open(data->getName(channel.iUniqueId), data->getUrl(channel.iUniqueId)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) { | ||||||
|  | 	return rtsp_read(pBuffer, iBufferSize); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CloseLiveStream(void) { | ||||||
|  | 	rtsp_close(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | long long SeekLiveStream(long long iPosition, int iWhence) { return -1; } | ||||||
|  | long long PositionLiveStream(void) { return -1; } | ||||||
|  | long long LengthLiveStream(void) { return -1; } | ||||||
|  | bool IsRealTimeStream(void) { return true; } | ||||||
|  |  | ||||||
|  | bool SwitchChannel(const PVR_CHANNEL& channel) { | ||||||
|  | 	CloseLiveStream(); | ||||||
|  | 	return OpenLiveStream(channel); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS& signalStatus) { | ||||||
|  | 	memset(&signalStatus, 0, sizeof(PVR_SIGNAL_STATUS)); | ||||||
|  | 	rtsp_fill_signal_status(signalStatus); | ||||||
|  | 	return PVR_ERROR_NO_ERROR; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char* GetLiveStreamURL(const PVR_CHANNEL& channel) { return NULL; } | ||||||
|  | PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  |  | ||||||
|  | /* Recording stream handling */ | ||||||
|  | bool OpenRecordedStream(const PVR_RECORDING& recording) { return false; } | ||||||
|  | void CloseRecordedStream(void) {} | ||||||
|  | int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize) { return -1; } | ||||||
|  | long long SeekRecordedStream(long long iPosition, int iWhence) { return -1; } | ||||||
|  | long long PositionRecordedStream(void) { return -1; } | ||||||
|  | long long LengthRecordedStream(void) { return -1; } | ||||||
|  |  | ||||||
|  | /* PVR demuxer */ | ||||||
|  | /* entirey unused, as we use TS */ | ||||||
|  | void DemuxReset(void) {} | ||||||
|  | void DemuxAbort(void) {} | ||||||
|  | void DemuxFlush(void) {} | ||||||
|  | DemuxPacket* DemuxRead(void) { return NULL; } | ||||||
|  |  | ||||||
|  | /* Various helper functions */ | ||||||
|  | unsigned int GetChannelSwitchDelay(void) { return 0; } | ||||||
|  | bool IsTimeshifting(void) { return false; } | ||||||
|  | bool CanPauseStream() { return false; } | ||||||
|  | bool CanSeekStream() { return false; } | ||||||
|  |  | ||||||
|  | /* Callbacks */ | ||||||
|  | void PauseStream(bool bPaused) {} | ||||||
|  | bool SeekTime(double time, bool backwards, double *startpts) { return false; } | ||||||
|  | void SetSpeed(int speed) {} | ||||||
|  | PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||||
|  |  | ||||||
|  | time_t GetPlayingTime() { return 0; } | ||||||
|  | time_t GetBufferTimeStart() { return 0; } | ||||||
|  | time_t GetBufferTimeEnd() { return 0; } | ||||||
|  |  | ||||||
|  | const char* GetBackendHostname() | ||||||
|  | { | ||||||
|  | 	return octonetAddress.c_str(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								src/client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/client.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | #pragma once | ||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Julian Scheel <julian@jusst.de> | ||||||
|  |  * Copyright (C) 2015 jusst technologies GmbH | ||||||
|  |  * Copyright (C) 2015 Digital Devices GmbH | ||||||
|  |  * | ||||||
|  |  * 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, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, | ||||||
|  |  * USA. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "libXBMC_addon.h" | ||||||
|  | #include "libXBMC_pvr.h" | ||||||
|  |  | ||||||
|  | #ifndef __func__ | ||||||
|  | #define __func__ __FUNCTION__ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | extern ADDON::CHelper_libXBMC_addon *kodi; | ||||||
|  | extern CHelper_libXBMC_pvr *pvr; | ||||||
|  |  | ||||||
|  | /* IP or hostname of the octonet to be connected to */ | ||||||
|  | extern std::string octonetAddress; | ||||||
| @@ -1,41 +1,32 @@ | |||||||
| /* |  | ||||||
|  *  Copyright (C) 2005-2020 Team Kodi |  | ||||||
|  *  https://kodi.tv |  | ||||||
|  * |  | ||||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later |  | ||||||
|  *  See LICENSE.md for more information. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "rtsp_client.hpp" | #include "rtsp_client.hpp" | ||||||
|  |  | ||||||
| #include "Socket.h" |  | ||||||
|  |  | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <cctype> | #include <cctype> | ||||||
| #include <cstring> |  | ||||||
| #include <iterator> | #include <iterator> | ||||||
|  | #include "Socket.h" | ||||||
|  | #include "client.h" | ||||||
|  | #include <p8-platform/util/util.h> | ||||||
|  | #include <libXBMC_addon.h> | ||||||
|  | #include <cstring> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  |  | ||||||
| #if defined(_WIN32) || defined(_WIN64) | #if defined(_WIN32) || defined(_WIN64) | ||||||
| #define strtok_r strtok_s | #define strtok_r strtok_s | ||||||
| #define strncasecmp _strnicmp | #define strncasecmp _strnicmp | ||||||
|  |  | ||||||
| int vasprintf(char** sptr, char* fmt, va_list argv) | int vasprintf(char **sptr, char *fmt, va_list argv) { | ||||||
| { | 	int wanted = vsnprintf(*sptr = NULL, 0, fmt, argv); | ||||||
|   int wanted = vsnprintf(*sptr = nullptr, 0, fmt, argv); | 	if((wanted < 0) || ((*sptr = (char *)malloc(1 + wanted)) == NULL)) | ||||||
|   if ((wanted < 0) || ((*sptr = (char*)malloc(1 + wanted)) == nullptr)) | 		return -1; | ||||||
|     return -1; | 	return vsprintf(*sptr, fmt, argv); | ||||||
|   return vsprintf(*sptr, fmt, argv); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int asprintf(char** sptr, char* fmt, ...) | int asprintf(char **sptr, char *fmt, ...) { | ||||||
| { | 	int retval; | ||||||
|   int retval; | 	va_list argv; | ||||||
|   va_list argv; | 	va_start(argv, fmt); | ||||||
|   va_start(argv, fmt); | 	retval = vasprintf(sptr, fmt, argv); | ||||||
|   retval = vasprintf(sptr, fmt, argv); | 	va_end(argv); | ||||||
|   va_end(argv); | 	return retval; | ||||||
|   return retval; |  | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -49,501 +40,441 @@ int asprintf(char** sptr, char* fmt, ...) | |||||||
| #define RTCP_BUFFER_SIZE 1024 | #define RTCP_BUFFER_SIZE 1024 | ||||||
|  |  | ||||||
| using namespace std; | using namespace std; | ||||||
|  | using namespace ADDON; | ||||||
| using namespace OCTO; | using namespace OCTO; | ||||||
|  |  | ||||||
| enum rtsp_state | enum rtsp_state { | ||||||
| { | 	RTSP_IDLE, | ||||||
|   RTSP_IDLE, | 	RTSP_DESCRIBE, | ||||||
|   RTSP_DESCRIBE, | 	RTSP_SETUP, | ||||||
|   RTSP_SETUP, | 	RTSP_PLAY, | ||||||
|   RTSP_PLAY, | 	RTSP_RUNNING | ||||||
|   RTSP_RUNNING |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum rtsp_result | enum rtsp_result { | ||||||
| { | 	RTSP_RESULT_OK = 200, | ||||||
|   RTSP_RESULT_OK = 200, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct rtsp_client | struct rtsp_client { | ||||||
| { | 	char *content_base; | ||||||
|   char* content_base; | 	char *control; | ||||||
|   char* control; | 	char session_id[64]; | ||||||
|   char session_id[64]; | 	uint16_t stream_id; | ||||||
|   uint16_t stream_id; | 	int keepalive_interval; | ||||||
|   int keepalive_interval; |  | ||||||
|  |  | ||||||
|   char udp_address[UDP_ADDRESS_LEN]; | 	char udp_address[UDP_ADDRESS_LEN]; | ||||||
|   uint16_t udp_port; | 	uint16_t udp_port; | ||||||
|  |  | ||||||
|   Socket tcp_sock; | 	Socket tcp_sock; | ||||||
|   Socket udp_sock; | 	Socket udp_sock; | ||||||
|   Socket rtcp_sock; | 	Socket rtcp_sock; | ||||||
|  |  | ||||||
|   enum rtsp_state state; | 	enum rtsp_state state; | ||||||
|   int cseq; | 	int cseq; | ||||||
|  |  | ||||||
|   size_t fifo_size; | 	size_t fifo_size; | ||||||
|   uint16_t last_seq_nr; | 	uint16_t last_seq_nr; | ||||||
|  |  | ||||||
|   string name; | 	string name; | ||||||
|   int level; | 	int level; | ||||||
|   int quality; | 	int quality; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct url | struct url { | ||||||
| { | 	string protocol; | ||||||
|   string protocol; | 	string host; | ||||||
|   string host; | 	int port; | ||||||
|   int port; | 	string path; | ||||||
|   string path; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct rtcp_app | struct rtcp_app { | ||||||
| { | 	uint8_t subtype; | ||||||
|   uint8_t subtype; | 	uint8_t pt; | ||||||
|   uint8_t pt; | 	uint16_t len; | ||||||
|   uint16_t len; | 	uint32_t ssrc; | ||||||
|   uint32_t ssrc; | 	char name[4]; | ||||||
|   char name[4]; | 	uint16_t identifier; | ||||||
|   uint16_t identifier; | 	uint16_t string_len; | ||||||
|   uint16_t string_len; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static rtsp_client* rtsp = nullptr; | static rtsp_client *rtsp = NULL; | ||||||
|  |  | ||||||
| static url parse_url(const std::string& str) | static url parse_url(const std::string& str) { | ||||||
| { | 	static const string prot_end = "://"; | ||||||
|   static const string prot_end = "://"; | 	static const string host_end = "/"; | ||||||
|   static const string host_end = "/"; | 	url result; | ||||||
|   url result; |  | ||||||
|  |  | ||||||
|   string::const_iterator begin = str.begin(); | 	string::const_iterator begin = str.begin(); | ||||||
|   string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); | 	string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); | ||||||
|   result.protocol.reserve(distance(begin, end)); | 	result.protocol.reserve(distance(begin, end)); | ||||||
|   transform(begin, end, back_inserter(result.protocol), ::tolower); | 	transform(begin, end, back_inserter(result.protocol), ::tolower); | ||||||
|   advance(end, prot_end.size()); | 	advance(end, prot_end.size()); | ||||||
|   begin = end; | 	begin = end; | ||||||
|  |  | ||||||
|   end = search(begin, str.end(), host_end.begin(), host_end.end()); | 	end = search(begin, str.end(), host_end.begin(), host_end.end()); | ||||||
|   result.host.reserve(distance(begin, end)); | 	result.host.reserve(distance(begin, end)); | ||||||
|   transform(begin, end, back_inserter(result.host), ::tolower); | 	transform(begin, end, back_inserter(result.host), ::tolower); | ||||||
|   advance(end, host_end.size()); | 	advance(end, host_end.size()); | ||||||
|   begin = end; | 	begin = end; | ||||||
|  |  | ||||||
|   result.port = RTSP_DEFAULT_PORT; | 	result.port = RTSP_DEFAULT_PORT; | ||||||
|  |  | ||||||
|   result.path.reserve(distance(begin, str.end())); | 	result.path.reserve(distance(begin, str.end())); | ||||||
|   transform(begin, str.end(), back_inserter(result.path), ::tolower); | 	transform(begin, str.end(), back_inserter(result.path), ::tolower); | ||||||
|  |  | ||||||
|   return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| void split_string(const string& s, char delim, vector<string>& elems) | void split_string(const string& s, char delim, vector<string>& elems) { | ||||||
| { | 	stringstream ss; | ||||||
|   stringstream ss; | 	ss.str(s); | ||||||
|   ss.str(s); |  | ||||||
|  |  | ||||||
|   string item; | 	string item; | ||||||
|   while (getline(ss, item, delim)) | 	while(getline(ss, item, delim)) { | ||||||
|   { | 		elems.push_back(item); | ||||||
|     elems.push_back(item); | 	} | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int tcp_sock_read_line(string& line) | static int tcp_sock_read_line(string &line) { | ||||||
| { | 	static string buf; | ||||||
|   static string buf; |  | ||||||
|  |  | ||||||
|   while (true) | 	while(true) { | ||||||
|   { | 		string::size_type pos = buf.find("\r\n"); | ||||||
|     string::size_type pos = buf.find("\r\n"); | 		if(pos != string::npos) { | ||||||
|     if (pos != string::npos) | 			line = buf.substr(0, pos); | ||||||
|     { | 			buf.erase(0, pos + 2); | ||||||
|       line = buf.substr(0, pos); | 			return 0; | ||||||
|       buf.erase(0, pos + 2); | 		} | ||||||
|       return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     char tmp_buf[2048]; | 		char tmp_buf[2048]; | ||||||
|     int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); | 		int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); | ||||||
|     if (size <= 0) | 		if(size <= 0) { | ||||||
|     { | 			return 1; | ||||||
|       return 1; | 		} | ||||||
|     } |  | ||||||
|  |  | ||||||
|     buf.append(&tmp_buf[0], &tmp_buf[size]); | 		buf.append(&tmp_buf[0], &tmp_buf[size]); | ||||||
|   } | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| static string compose_url(const url& u) | static string compose_url(const url& u) | ||||||
| { | { | ||||||
|   stringstream res; | 	stringstream res; | ||||||
|   res << u.protocol << "://" << u.host; | 	res << u.protocol << "://" << u.host; | ||||||
|   if (u.port > 0) | 	if (u.port > 0) | ||||||
|     res << ":" << u.port; | 		res << ":" << u.port; | ||||||
|   res << "/" << u.path; | 	res << "/" << u.path; | ||||||
|  |  | ||||||
|   return res.str(); | 	return res.str(); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void parse_session(char* request_line, char* session, unsigned max, int* timeout) | static void parse_session(char *request_line, char *session, unsigned max, int *timeout) { | ||||||
| { | 	char *state; | ||||||
|   char* state; | 	char *tok; | ||||||
|   char* tok; |  | ||||||
|  |  | ||||||
|   tok = strtok_r(request_line, ";", &state); | 	tok = strtok_r(request_line, ";", &state); | ||||||
|   if (tok == nullptr) | 	if (tok == NULL) | ||||||
|     return; | 		return; | ||||||
|   strncpy(session, tok, min(strlen(tok), (size_t)(max - 1))); | 	strncpy(session, tok, min(strlen(tok), (size_t)(max - 1))); | ||||||
|  |  | ||||||
|   while ((tok = strtok_r(nullptr, ";", &state)) != nullptr) | 	while ((tok = strtok_r(NULL, ";", &state)) != NULL) { | ||||||
|   { | 		if (strncmp(tok, "timeout=", 8) == 0) { | ||||||
|     if (strncmp(tok, "timeout=", 8) == 0) | 			*timeout = atoi(tok + 8); | ||||||
|     { | 			if (*timeout > 5) | ||||||
|       *timeout = atoi(tok + 8); | 				*timeout -= KEEPALIVE_MARGIN; | ||||||
|       if (*timeout > 5) | 			else if (*timeout > 0) | ||||||
|         *timeout -= KEEPALIVE_MARGIN; | 				*timeout = 1; | ||||||
|       else if (*timeout > 0) | 		} | ||||||
|         *timeout = 1; | 	} | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int parse_port(char* str, uint16_t* port) | static int parse_port(char *str, uint16_t *port) | ||||||
| { | { | ||||||
|   int p = atoi(str); | 	int p = atoi(str); | ||||||
|   if (p < 0 || p > UINT16_MAX) | 	if (p < 0 || p > UINT16_MAX) | ||||||
|     return -1; | 		return -1; | ||||||
|  |  | ||||||
|   *port = p; | 	*port = p; | ||||||
|  |  | ||||||
|   return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int parse_transport(char* request_line) | static int parse_transport(char *request_line) { | ||||||
| { | 	char *state; | ||||||
|   char* state; | 	char *tok; | ||||||
|   char* tok; | 	int err; | ||||||
|   int err; |  | ||||||
|  |  | ||||||
|   tok = strtok_r(request_line, ";", &state); | 	tok = strtok_r(request_line, ";", &state); | ||||||
|   if (tok == nullptr || strncmp(tok, "RTP/AVP", 7) != 0) | 	if (tok == NULL || strncmp(tok, "RTP/AVP", 7) != 0) | ||||||
|     return -1; | 		return -1; | ||||||
|  |  | ||||||
|   tok = strtok_r(nullptr, ";", &state); | 	tok = strtok_r(NULL, ";", &state); | ||||||
|   if (tok == nullptr || strncmp(tok, "multicast", 9) != 0) | 	if (tok == NULL || strncmp(tok, "multicast", 9) != 0) | ||||||
|     return 0; | 		return 0; | ||||||
|  |  | ||||||
|   while ((tok = strtok_r(nullptr, ";", &state)) != nullptr) | 	while ((tok = strtok_r(NULL, ";", &state)) != NULL) { | ||||||
|   { | 		if (strncmp(tok, "destination=", 12) == 0) { | ||||||
|     if (strncmp(tok, "destination=", 12) == 0) | 			strncpy(rtsp->udp_address, tok + 12, min(strlen(tok + 12), (size_t)(UDP_ADDRESS_LEN - 1))); | ||||||
|     { | 		} else if (strncmp(tok, "port=", 5) == 0) { | ||||||
|       strncpy(rtsp->udp_address, tok + 12, min(strlen(tok + 12), (size_t)(UDP_ADDRESS_LEN - 1))); | 			char port[6]; | ||||||
|     } | 			char *end; | ||||||
|     else if (strncmp(tok, "port=", 5) == 0) |  | ||||||
|     { |  | ||||||
|       char port[6]; |  | ||||||
|       char* end; |  | ||||||
|  |  | ||||||
|       memset(port, 0x00, 6); | 			memset(port, 0x00, 6); | ||||||
|       strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5)); | 			strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5)); | ||||||
|       if ((end = strstr(port, "-")) != nullptr) | 			if ((end = strstr(port, "-")) != NULL) | ||||||
|         *end = '\0'; | 				*end = '\0'; | ||||||
|       err = parse_port(port, &rtsp->udp_port); | 			err = parse_port(port, &rtsp->udp_port); | ||||||
|       if (err) | 			if (err) | ||||||
|         return err; | 				return err; | ||||||
|     } | 		} | ||||||
|   } | 	} | ||||||
|  |  | ||||||
|   return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| #define skip_whitespace(x) \ | #define skip_whitespace(x) while(*x == ' ') x++ | ||||||
|   while (*x == ' ') \ | static enum rtsp_result rtsp_handle() { | ||||||
|   x++ | 	uint8_t buffer[512]; | ||||||
| static enum rtsp_result rtsp_handle() | 	int rtsp_result = 0; | ||||||
| { | 	bool have_header = false; | ||||||
|   uint8_t buffer[512]; | 	size_t content_length = 0; | ||||||
|   int rtsp_result = 0; | 	size_t read = 0; | ||||||
|   bool have_header = false; | 	char *in, *val; | ||||||
|   size_t content_length = 0; | 	string in_str; | ||||||
|   size_t read = 0; |  | ||||||
|   char *in, *val; |  | ||||||
|   string in_str; |  | ||||||
|  |  | ||||||
|   /* Parse header */ | 	/* Parse header */ | ||||||
|   while (!have_header) | 	while (!have_header) { | ||||||
|   { | 		if (tcp_sock_read_line(in_str) < 0) | ||||||
|     if (tcp_sock_read_line(in_str) < 0) | 			break; | ||||||
|       break; | 		in = const_cast<char *>(in_str.c_str()); | ||||||
|     in = const_cast<char*>(in_str.c_str()); |  | ||||||
|  |  | ||||||
|     if (strncmp(in, "RTSP/1.0 ", 9) == 0) | 		if (strncmp(in, "RTSP/1.0 ", 9) == 0) { | ||||||
|     { | 			rtsp_result = atoi(in + 9); | ||||||
|       rtsp_result = atoi(in + 9); | 		} else if (strncmp(in, "Content-Base:", 13) == 0) { | ||||||
|     } | 			free(rtsp->content_base); | ||||||
|     else if (strncmp(in, "Content-Base:", 13) == 0) |  | ||||||
|     { |  | ||||||
|       free(rtsp->content_base); |  | ||||||
|  |  | ||||||
|       val = in + 13; | 			val = in + 13; | ||||||
|       skip_whitespace(val); | 			skip_whitespace(val); | ||||||
|  |  | ||||||
|       rtsp->content_base = strdup(val); | 			rtsp->content_base = strdup(val); | ||||||
|     } | 		} else if (strncmp(in, "Content-Length:", 15) == 0) { | ||||||
|     else if (strncmp(in, "Content-Length:", 15) == 0) | 			val = in + 16; | ||||||
|     { | 			skip_whitespace(val); | ||||||
|       val = in + 16; |  | ||||||
|       skip_whitespace(val); |  | ||||||
|  |  | ||||||
|       content_length = atoi(val); | 			content_length = atoi(val); | ||||||
|     } | 		} else if (strncmp("Session:", in, 8) == 0) { | ||||||
|     else if (strncmp("Session:", in, 8) == 0) | 			val = in + 8; | ||||||
|     { | 			skip_whitespace(val); | ||||||
|       val = in + 8; |  | ||||||
|       skip_whitespace(val); |  | ||||||
|  |  | ||||||
|       parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval); | 			parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval); | ||||||
|     } | 		} else if (strncmp("Transport:", in, 10) == 0) { | ||||||
|     else if (strncmp("Transport:", in, 10) == 0) | 			val = in + 10; | ||||||
|     { | 			skip_whitespace(val); | ||||||
|       val = in + 10; |  | ||||||
|       skip_whitespace(val); |  | ||||||
|  |  | ||||||
|       if (parse_transport(val) != 0) | 			if (parse_transport(val) != 0) { | ||||||
|       { | 				rtsp_result = -1; | ||||||
|         rtsp_result = -1; | 				break; | ||||||
|         break; | 			} | ||||||
|       } | 		} else if (strncmp("com.ses.streamID:", in, 17) == 0) { | ||||||
|     } | 			val = in + 17; | ||||||
|     else if (strncmp("com.ses.streamID:", in, 17) == 0) | 			skip_whitespace(val); | ||||||
|     { |  | ||||||
|       val = in + 17; |  | ||||||
|       skip_whitespace(val); |  | ||||||
|  |  | ||||||
|       rtsp->stream_id = atoi(val); | 			rtsp->stream_id = atoi(val); | ||||||
|     } | 		} else if (in[0] == '\0') { | ||||||
|     else if (in[0] == '\0') | 			have_header = true; | ||||||
|     { | 		} | ||||||
|       have_header = true; | 	} | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /* Discard further content */ | 	/* Discard further content */ | ||||||
|   while (content_length > 0 && (read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer), | 	while (content_length > 0 && | ||||||
|                                                               min(sizeof(buffer), content_length)))) | 			(read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer), min(sizeof(buffer), content_length)))) | ||||||
|     content_length -= read; | 		content_length -= read; | ||||||
|  |  | ||||||
|   return (enum rtsp_result)rtsp_result; | 	return (enum rtsp_result)rtsp_result; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool rtsp_open(const string& name, const string& url_str) | bool rtsp_open(const string& name, const string& url_str) | ||||||
| { | { | ||||||
|   string setup_url_str; | 	string setup_url_str; | ||||||
|   const char* psz_setup_url; | 	const char *psz_setup_url; | ||||||
|   stringstream setup_ss; | 	stringstream setup_ss; | ||||||
|   stringstream play_ss; | 	stringstream play_ss; | ||||||
|   url setup_url; | 	url setup_url; | ||||||
|  |  | ||||||
|   rtsp_close(); | 	rtsp_close(); | ||||||
|   rtsp = new rtsp_client(); | 	rtsp = new rtsp_client(); | ||||||
|   if (rtsp == nullptr) | 	if (rtsp == NULL) | ||||||
|     return false; | 		return false; | ||||||
|  |  | ||||||
|   rtsp->name = name; | 	rtsp->name = name; | ||||||
|   rtsp->level = 0; | 	rtsp->level = 0; | ||||||
|   rtsp->quality = 0; | 	rtsp->quality = 0; | ||||||
|  |  | ||||||
|   kodi::Log(ADDON_LOG_DEBUG, "try to open '%s'", url_str.c_str()); | 	kodi->Log(LOG_DEBUG, "try to open '%s'", url_str.c_str()); | ||||||
|  |  | ||||||
|   url dst = parse_url(url_str); | 	url dst = parse_url(url_str); | ||||||
|   kodi::Log(ADDON_LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); | 	kodi->Log(LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); | ||||||
|  |  | ||||||
|   if (!rtsp->tcp_sock.connect(dst.host, dst.port)) | 	if(!rtsp->tcp_sock.connect(dst.host, dst.port)) { | ||||||
|   { | 		kodi->Log(LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(), dst.port); | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(), | 		goto error; | ||||||
|               dst.port); | 	} | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // TODO: tcp keep alive? | 	// TODO: tcp keep alive? | ||||||
|  |  | ||||||
|   if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), dst.port) < 0) | 	if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), | ||||||
|   { | 				dst.port) < 0) { | ||||||
|     rtsp->content_base = nullptr; | 		rtsp->content_base = NULL; | ||||||
|     goto error; | 		goto error; | ||||||
|   } | 	} | ||||||
|  |  | ||||||
|   rtsp->last_seq_nr = 0; | 	rtsp->last_seq_nr = 0; | ||||||
|   rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN); | 	rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN); | ||||||
|  |  | ||||||
|   setup_url = dst; | 	setup_url = dst; | ||||||
|  |  | ||||||
|   // reverse the satip protocol trick, as SAT>IP believes to be RTSP | 	// reverse the satip protocol trick, as SAT>IP believes to be RTSP | ||||||
|   if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) | 	if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) { | ||||||
|   { | 		setup_url.protocol = "rtsp"; | ||||||
|     setup_url.protocol = "rtsp"; | 	} | ||||||
|   } |  | ||||||
|  |  | ||||||
|   setup_url_str = compose_url(setup_url); | 	setup_url_str = compose_url(setup_url); | ||||||
|   psz_setup_url = setup_url_str.c_str(); | 	psz_setup_url = setup_url_str.c_str(); | ||||||
|  |  | ||||||
|   // TODO: Find available port | 	// TODO: Find available port | ||||||
|   rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | 	rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | ||||||
|   rtsp->udp_port = 6785; | 	rtsp->udp_port = 6785; | ||||||
|   if (!rtsp->udp_sock.bind(rtsp->udp_port)) | 	if(!rtsp->udp_sock.bind(rtsp->udp_port)) { | ||||||
|   { | 		goto error; | ||||||
|     goto error; | 	} | ||||||
|   } |  | ||||||
|  |  | ||||||
|   setup_ss << "SETUP " << setup_url_str << " RTSP/1.0\r\n"; | 	setup_ss << "SETUP " << setup_url_str<< " RTSP/1.0\r\n"; | ||||||
|   setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | 	setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||||
|   setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-" | 	setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-" << (rtsp->udp_port + 1) << "\r\n\r\n"; | ||||||
|            << (rtsp->udp_port + 1) << "\r\n\r\n"; | 	rtsp->tcp_sock.send(setup_ss.str()); | ||||||
|   rtsp->tcp_sock.send(setup_ss.str()); |  | ||||||
|  |  | ||||||
|   if (rtsp_handle() != RTSP_RESULT_OK) | 	if (rtsp_handle() != RTSP_RESULT_OK) { | ||||||
|   { | 		kodi->Log(LOG_ERROR, "Failed to setup RTSP session"); | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Failed to setup RTSP session"); | 		goto error; | ||||||
|     goto error; | 	} | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0) | 	if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0) { | ||||||
|   { | 		rtsp->control = NULL; | ||||||
|     rtsp->control = nullptr; | 		goto error; | ||||||
|     goto error; | 	} | ||||||
|   } |  | ||||||
|  |  | ||||||
|   play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; | 	play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; | ||||||
|   play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | 	play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||||
|   play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | 	play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | ||||||
|   rtsp->tcp_sock.send(play_ss.str()); | 	rtsp->tcp_sock.send(play_ss.str()); | ||||||
|  |  | ||||||
|   if (rtsp_handle() != RTSP_RESULT_OK) | 	if (rtsp_handle() != RTSP_RESULT_OK) { | ||||||
|   { | 		kodi->Log(LOG_ERROR, "Failed to play RTSP session"); | ||||||
|     kodi::Log(ADDON_LOG_ERROR, "Failed to play RTSP session"); | 		goto error; | ||||||
|     goto error; | 	} | ||||||
|   } |  | ||||||
|  |  | ||||||
|   rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | 	rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | ||||||
|   if (!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) | 	if(!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) { | ||||||
|   { | 		goto error; | ||||||
|     goto error; | 	} | ||||||
|   } | 	if(!rtsp->rtcp_sock.set_non_blocking(true)) { | ||||||
|   if (!rtsp->rtcp_sock.set_non_blocking(true)) | 		goto error; | ||||||
|   { | 	} | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return true; | 	return true; | ||||||
|  |  | ||||||
| error: | error: | ||||||
|   rtsp_close(); | 	rtsp_close(); | ||||||
|   return false; | 	return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void parse_rtcp(const char* buf, int size) | static void parse_rtcp(const char *buf, int size) { | ||||||
| { | 	int offset = 0; | ||||||
|   int offset = 0; | 	while(size > 4) { | ||||||
|   while (size > 4) | 		const rtcp_app *app = reinterpret_cast<const rtcp_app *>(buf + offset); | ||||||
|   { | 		uint16_t len = 4 * (ntohs(app->len) + 1); | ||||||
|     const rtcp_app* app = reinterpret_cast<const rtcp_app*>(buf + offset); |  | ||||||
|     uint16_t len = 4 * (ntohs(app->len) + 1); |  | ||||||
|  |  | ||||||
|     if ((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0)) | 		if((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0)) { | ||||||
|     { | 			size -= len; | ||||||
|       size -= len; | 			offset += len; | ||||||
|       offset += len; | 			continue; | ||||||
|       continue; | 		} | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uint16_t string_len = ntohs(app->string_len); | 		uint16_t string_len = ntohs(app->string_len); | ||||||
|     string app_data(&buf[offset + sizeof(rtcp_app)], string_len); | 		string app_data(&buf[offset + sizeof(rtcp_app)], string_len); | ||||||
|  |  | ||||||
|     vector<string> elems; | 		vector<string> elems; | ||||||
|     split_string(app_data, ';', elems); | 		split_string(app_data, ';', elems); | ||||||
|     if (elems.size() != 4) | 		if(elems.size() != 4) { | ||||||
|     { | 			return; | ||||||
|       return; | 		} | ||||||
|     } |  | ||||||
|  |  | ||||||
|     vector<string> tuner; | 		vector<string> tuner; | ||||||
|     split_string(elems[2], ',', tuner); | 		split_string(elems[2], ',', tuner); | ||||||
|     if (tuner.size() < 4) | 		if(tuner.size() < 4) { | ||||||
|     { | 			return; | ||||||
|       return; | 		} | ||||||
|     } |  | ||||||
|  |  | ||||||
|     rtsp->level = atoi(tuner[1].c_str()); | 		rtsp->level = atoi(tuner[1].c_str()); | ||||||
|     rtsp->quality = atoi(tuner[3].c_str()); | 		rtsp->quality = atoi(tuner[3].c_str()); | ||||||
|  |  | ||||||
|     return; | 		return; | ||||||
|   } | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| int rtsp_read(void* buf, unsigned buf_size) | int rtsp_read(void *buf, unsigned buf_size) { | ||||||
| { | 	sockaddr addr; | ||||||
|   sockaddr addr; | 	socklen_t addr_len = sizeof(addr); | ||||||
|   socklen_t addr_len = sizeof(addr); | 	int ret = rtsp->udp_sock.recvfrom((char *)buf, buf_size, (sockaddr *)&addr, &addr_len); | ||||||
|   int ret = rtsp->udp_sock.recvfrom((char*)buf, buf_size, (sockaddr*)&addr, &addr_len); |  | ||||||
|  |  | ||||||
|   char rtcp_buf[RTCP_BUFFER_SIZE]; | 	char rtcp_buf[RTCP_BUFFER_SIZE]; | ||||||
|   int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr*)&addr, &addr_len); | 	int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr *)&addr, &addr_len); | ||||||
|   parse_rtcp(rtcp_buf, rtcp_len); | 	parse_rtcp(rtcp_buf, rtcp_len); | ||||||
|  |  | ||||||
|   // TODO: check ip | 	// TODO: check ip | ||||||
|  |  | ||||||
|   return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void rtsp_teardown() | static void rtsp_teardown() { | ||||||
| { | 	if(!rtsp->tcp_sock.is_valid()) { | ||||||
|   if (!rtsp->tcp_sock.is_valid()) | 		return; | ||||||
|   { | 	} | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (rtsp->session_id[0] > 0) | 	if (rtsp->session_id[0] > 0) { | ||||||
|   { | 		char *msg; | ||||||
|     char* msg; | 		int len; | ||||||
|     int len; | 		stringstream ss; | ||||||
|     stringstream ss; |  | ||||||
|  |  | ||||||
|     rtsp->udp_sock.close(); | 		rtsp->udp_sock.close(); | ||||||
|  |  | ||||||
|     ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; | 		ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; | ||||||
|     ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | 		ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||||
|     ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | 		ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | ||||||
|     rtsp->tcp_sock.send(ss.str()); | 		rtsp->tcp_sock.send(ss.str()); | ||||||
|  |  | ||||||
|     if (rtsp_handle() != RTSP_RESULT_OK) | 		if (rtsp_handle() != RTSP_RESULT_OK) { | ||||||
|     { | 			kodi->Log(LOG_ERROR, "Failed to teardown RTSP session"); | ||||||
|       kodi::Log(ADDON_LOG_ERROR, "Failed to teardown RTSP session"); | 			return; | ||||||
|       return; | 		} | ||||||
|     } | 	} | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void rtsp_close() | void rtsp_close() | ||||||
| { | { | ||||||
|   if (rtsp) | 	if(rtsp) { | ||||||
|   { | 		rtsp_teardown(); | ||||||
|     rtsp_teardown(); | 		rtsp->tcp_sock.close(); | ||||||
|     rtsp->tcp_sock.close(); | 		rtsp->udp_sock.close(); | ||||||
|     rtsp->udp_sock.close(); | 		rtsp->rtcp_sock.close(); | ||||||
|     rtsp->rtcp_sock.close(); | 		delete rtsp; | ||||||
|     delete rtsp; | 		rtsp = NULL; | ||||||
|     rtsp = nullptr; | 	} | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status) | void rtsp_fill_signal_status(PVR_SIGNAL_STATUS& signal_status) { | ||||||
| { | 	if(rtsp) { | ||||||
|   if (rtsp) | 		strncpy(signal_status.strServiceName, rtsp->name.c_str(), PVR_ADDON_NAME_STRING_LENGTH - 1); | ||||||
|   { | 		signal_status.iSNR = 0x1111 * rtsp->quality; | ||||||
|     signal_status.SetAdapterName(rtsp->name); | 		signal_status.iSignal = 0x101 * rtsp->level; | ||||||
|     signal_status.SetSNR(0x1111 * rtsp->quality); | 	} | ||||||
|     signal_status.SetSignal(0x101 * rtsp->level); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,17 +1,12 @@ | |||||||
| /* | #ifndef _RTSP_CLIENT_HPP_ | ||||||
|  *  Copyright (C) 2005-2020 Team Kodi | #define _RTSP_CLIENT_HPP_ | ||||||
|  *  https://kodi.tv |  | ||||||
|  * |  | ||||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later |  | ||||||
|  *  See LICENSE.md for more information. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <kodi/addon-instance/pvr/Channels.h> |  | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <xbmc_pvr_types.h> | ||||||
|  |  | ||||||
| bool rtsp_open(const std::string& name, const std::string& url_str); | bool rtsp_open(const std::string& name, const std::string& url_str); | ||||||
| void rtsp_close(); | void rtsp_close(); | ||||||
| int rtsp_read(void* buf, unsigned buf_size); | int rtsp_read(void *buf, unsigned buf_size); | ||||||
| void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status); | void rtsp_fill_signal_status(PVR_SIGNAL_STATUS& signal_status); | ||||||
|  |  | ||||||
|  | #endif | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user