mirror of
				https://github.com/DigitalDevices/pvr.octonet.git
				synced 2025-03-01 10:53:09 +00:00 
			
		
		
		
	Compare commits
	
		
			39 Commits
		
	
	
		
			1.2.1-Matr
			...
			4.1.0-2-Ma
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3b3e8e4a1a | ||
|  | e76a37eb1d | ||
|  | 43b2467d35 | ||
|  | 18fc3a3339 | ||
|  | 11b8d0b076 | ||
|  | 479502987a | ||
|  | 495dfe137a | ||
|  | 5a228cc805 | ||
|  | 81520e3104 | ||
|  | 1d1cede442 | ||
|  | 1736207a46 | ||
|  | e7449d9537 | ||
|  | fc5b149f2c | ||
|  | 76259ba352 | ||
|  | 525f1101c6 | ||
|  | 2a9d84fa8e | ||
|  | c537ad2337 | ||
|  | b01e24b0a2 | ||
|  | 5f9342abdd | ||
|  | cc34e52369 | ||
|  | 360fb03145 | ||
|  | ace4891f78 | ||
|  | 3d47c712fd | ||
|  | 3f04265d51 | ||
|  | d4d6628d57 | ||
|  | cbef814f8f | ||
|  | 50386e13b2 | ||
|  | cd1c5d4171 | ||
|  | 83fbcfb360 | ||
|  | bed39d0575 | ||
|  | 0bdcb49d69 | ||
|  | 5024fd99c1 | ||
|  | a19fef8af8 | ||
|  | 38f8f54c9f | ||
|  | 01c41d8ea1 | ||
|  | 237362e65a | ||
|  | c3f4d61b38 | ||
|  | c37cede8dc | ||
|  | aa4f7c0b0d | 
							
								
								
									
										88
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| --- | ||||
| # 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 | ||||
| ... | ||||
							
								
								
									
										52
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,50 @@ | ||||
| /build | ||||
| pvr.octonet/addon.xml | ||||
| # build artifacts | ||||
| 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 | ||||
|   | ||||
							
								
								
									
										43
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,11 +1,7 @@ | ||||
| language: cpp | ||||
|  | ||||
| # | ||||
| # 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++. | ||||
| # Define the builds to get up to date versions of cmake and gcc | ||||
| # | ||||
| env: | ||||
|   global: | ||||
| @@ -14,26 +10,45 @@ env: | ||||
| matrix: | ||||
|   include: | ||||
|     - os: linux | ||||
|       dist: xenial | ||||
|       dist: bionic | ||||
|       sudo: required | ||||
|       compiler: gcc | ||||
|     - os: linux | ||||
|       dist: xenial | ||||
|       dist: bionic | ||||
|       sudo: required | ||||
|       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 | ||||
|       osx_image: xcode10.2 | ||||
|  | ||||
| before_install: | ||||
|   - if [[ $DEBIAN_BUILD == true ]]; then sudo add-apt-repository -y ppa:team-xbmc/xbmc-nightly; fi | ||||
|   - if [[ $DEBIAN_BUILD == true ]]; then sudo apt-get update; fi | ||||
|   - if [[ $DEBIAN_BUILD == true ]]; then sudo apt-get install fakeroot; fi | ||||
|  | ||||
| # | ||||
| # The addon source is automatically checked out in $TRAVIS_BUILD_DIR, | ||||
| # we'll put the Kodi source on the same level | ||||
| # | ||||
| before_script: | ||||
|   - cd $TRAVIS_BUILD_DIR/.. | ||||
|   - git clone --branch master --depth=1 https://github.com/xbmc/xbmc.git | ||||
|   - cd ${app_id} && mkdir build && cd build | ||||
|   - mkdir -p definition/${app_id} | ||||
|   - echo ${app_id} $TRAVIS_BUILD_DIR $TRAVIS_COMMIT > definition/${app_id}/${app_id}.txt | ||||
|   - cmake -DADDONS_TO_BUILD=${app_id} -DADDON_SRC_PREFIX=$TRAVIS_BUILD_DIR/.. -DADDONS_DEFINITION_DIR=$TRAVIS_BUILD_DIR/build/definition -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$TRAVIS_BUILD_DIR/../xbmc/addons -DPACKAGE_ZIP=1 $TRAVIS_BUILD_DIR/../xbmc/cmake/addons | ||||
|   - if [[ $DEBIAN_BUILD != true ]]; then cd $TRAVIS_BUILD_DIR/..; fi | ||||
|   - if [[ $DEBIAN_BUILD != true ]]; then git clone --branch master --depth=1 https://github.com/xbmc/xbmc.git; fi | ||||
|   - if [[ $DEBIAN_BUILD != true ]]; then cd ${app_id} && mkdir build && cd build; fi | ||||
|   - if [[ $DEBIAN_BUILD != true ]]; then mkdir -p definition/${app_id}; fi | ||||
|   - if [[ $DEBIAN_BUILD != true ]]; then echo ${app_id} $TRAVIS_BUILD_DIR $TRAVIS_COMMIT > definition/${app_id}/${app_id}.txt; fi | ||||
|   - 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 | ||||
|   - 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: make | ||||
| script:  | ||||
|   - if [[ $DEBIAN_BUILD != true ]]; then make; fi | ||||
|   - if [[ $DEBIAN_BUILD == true ]]; then ./debian-addon-package-test.sh $TRAVIS_BUILD_DIR; fi | ||||
|   | ||||
| @@ -4,37 +4,34 @@ project(pvr.octonet) | ||||
| list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) | ||||
|  | ||||
| find_package(Kodi REQUIRED) | ||||
| find_package(p8-platform REQUIRED) | ||||
| find_package(JsonCpp REQUIRED) | ||||
|  | ||||
| include_directories( | ||||
| 	${p8-platform_INCLUDE_DIRS} | ||||
| 	${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways | ||||
| 	${JSONCPP_INCLUDE_DIRS}) | ||||
| include_directories(${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways | ||||
|                     ${JSONCPP_INCLUDE_DIRS}) | ||||
|  | ||||
| set(DEPLIBS | ||||
| 	${p8-platform_LIBRARIES} | ||||
| 	${JSONCPP_LIBRARIES}) | ||||
| set(DEPLIBS ${JSONCPP_LIBRARIES}) | ||||
|  | ||||
| set(OCTONET_SOURCES | ||||
| 	src/OctonetData.cpp | ||||
| 	src/client.cpp | ||||
| 	src/Socket.cpp | ||||
| 	src/rtsp_client.cpp) | ||||
| set(OCTONET_SOURCES src/addon.cpp | ||||
|                     src/OctonetData.cpp | ||||
|                     src/Socket.cpp | ||||
|                     src/rtsp_client.cpp) | ||||
|  | ||||
| set(OCTONET_HEADERS | ||||
| 	src/client.h | ||||
| 	src/OctonetData.h | ||||
| 	src/Socket.h) | ||||
| 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) | ||||
|  | ||||
| if(WIN32) | ||||
| 	if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore) | ||||
| 		target_link_libraries(pvr.octonet wsock32 ws2_32) | ||||
| 	else() | ||||
| 		target_link_libraries(pvr.octonet ws2_32) | ||||
| 	endif() | ||||
|   if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore) | ||||
|     target_link_libraries(pvr.octonet wsock32 ws2_32) | ||||
|   else() | ||||
|     target_link_libraries(pvr.octonet ws2_32) | ||||
|   endif() | ||||
| endif() | ||||
|  | ||||
| include(CPack) | ||||
|   | ||||
							
								
								
									
										264
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,264 @@ | ||||
| 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. | ||||
							
								
								
									
										2
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -3,7 +3,7 @@ Priority: extra | ||||
| Maintainer: Julian Scheel <julian@jusst.de> | ||||
| Build-Depends: debhelper (>= 9.0.0), cmake, libjsoncpp-dev, | ||||
|                libp8-platform-dev, kodi-addon-dev | ||||
| Standards-Version: 3.9.4 | ||||
| Standards-Version: 4.1.2 | ||||
| Section: libs | ||||
| 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: * | ||||
| Copyright: 2015-2016 Julian Scheel | ||||
|            2015-2016 jusst technologies GmbH | ||||
|            2005-2013 Team XBMC | ||||
|            2005-2020 Team Kodi | ||||
| License: GPL-2+ | ||||
|  This package is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
| @@ -24,7 +24,8 @@ License: GPL-2+ | ||||
|  Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". | ||||
|  | ||||
| Files: debian/* | ||||
| Copyright: 2016 Julian Scheel <julian@jusst.de> | ||||
| Copyright: 2020 Team Kodi | ||||
|            2016 Julian Scheel <julian@jusst.de> | ||||
|            2015 Jean-Luc Barriere | ||||
|            2015 wsnipex <wsnipex@a1.net> | ||||
| License: GPL-2+ | ||||
|   | ||||
| @@ -1,16 +1,18 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <addon | ||||
| 	id="pvr.octonet" | ||||
| 	version="1.2.1" | ||||
| 	version="4.1.0" | ||||
| 	name="Digital Devices Octopus NET Client" | ||||
| 	provider-name="digitaldevices"> | ||||
| 	<requires>@ADDON_DEPENDS@</requires> | ||||
| 	<extension | ||||
| 		point="xbmc.pvrclient" | ||||
| 		point="kodi.pvrclient" | ||||
| 		library_@PLATFORM@="@LIBRARY_FILENAME@"/> | ||||
| 	<extension point="xbmc.addon.metadata"> | ||||
| 		<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> | ||||
| 		<platform>@PLATFORM@</platform> | ||||
| 		<license>GPL-2.0-or-later</license> | ||||
| 		<source>https://github.com/DigitalDevices/pvr.octonet</source> | ||||
| 	</extension> | ||||
| </addon> | ||||
|   | ||||
| @@ -1,5 +1,18 @@ | ||||
| <?xml version="1.0" encoding="utf-8" standalone="yes"?> | ||||
| <settings> | ||||
| 	<!-- Octonet Server Address --> | ||||
| 	<setting id="octonetAddress" type="text" label="30000" default="" /> | ||||
| <settings version="1"> | ||||
|   <section id="pvr.octonet"> | ||||
|     <category id="main" label="128" help="-1"> | ||||
|       <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> | ||||
|   | ||||
| @@ -1,369 +1,458 @@ | ||||
| /* | ||||
|  * Copyright (C) 2015 Julian Scheel <julian@jusst.de> | ||||
|  * Copyright (C) 2015 jusst technologies GmbH | ||||
|  * Copyright (C) 2015 Digital Devices GmbH | ||||
|  *  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. | ||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  *  See LICENSE.md for more information. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "OctonetData.h" | ||||
|  | ||||
| #include "rtsp_client.hpp" | ||||
|  | ||||
| #include <json/json.h> | ||||
| #include <kodi/Filesystem.h> | ||||
| #include <kodi/General.h> | ||||
| #include <sstream> | ||||
| #include <string> | ||||
|  | ||||
| #include <json/json.h> | ||||
|  | ||||
| #include "OctonetData.h" | ||||
|  | ||||
| #ifdef __WINDOWS__ | ||||
| #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) | ||||
| #define timegm _mkgmtime | ||||
| #endif | ||||
|  | ||||
| using namespace ADDON; | ||||
|  | ||||
| OctonetData::OctonetData() | ||||
| OctonetData::OctonetData(const std::string& octonetAddress, | ||||
|                          KODI_HANDLE instance, | ||||
|                          const std::string& kodiVersion) | ||||
|   : kodi::addon::CInstancePVRClient(instance, kodiVersion) | ||||
| { | ||||
| 	serverAddress = octonetAddress; | ||||
| 	channels.clear(); | ||||
| 	groups.clear(); | ||||
| 	lastEpgLoad = 0; | ||||
|   m_serverAddress = octonetAddress; | ||||
|   m_channels.clear(); | ||||
|   m_groups.clear(); | ||||
|   m_lastEpgLoad = 0; | ||||
|  | ||||
| 	if (!loadChannelList()) | ||||
| 		libKodi->QueueNotification(QUEUE_ERROR, libKodi->GetLocalizedString(30001), channels.size()); | ||||
|   if (!LoadChannelList()) | ||||
|     kodi::QueueFormattedNotification(QUEUE_ERROR, kodi::GetLocalizedString(30001).c_str(), | ||||
|                                      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) | ||||
| { | ||||
| 	channels.clear(); | ||||
| 	groups.clear(); | ||||
|   /* | ||||
|   m_running = false; | ||||
|   if (m_thread.joinable()) | ||||
|     m_thread.join(); | ||||
|   */ | ||||
| } | ||||
|  | ||||
| int64_t OctonetData::parseID(std::string id) | ||||
| PVR_ERROR OctonetData::GetCapabilities(kodi::addon::PVRCapabilities& capabilities) | ||||
| { | ||||
| 	std::hash<std::string> hash_fn; | ||||
| 	int64_t nativeId = hash_fn(id); | ||||
|   capabilities.SetSupportsTV(true); | ||||
|   capabilities.SetSupportsRadio(true); | ||||
|   capabilities.SetSupportsChannelGroups(true); | ||||
|   capabilities.SetSupportsEPG(true); | ||||
|   capabilities.SetSupportsRecordings(false); | ||||
|   capabilities.SetSupportsRecordingsRename(false); | ||||
|   capabilities.SetSupportsRecordingsLifetimeChange(false); | ||||
|   capabilities.SetSupportsDescrambleInfo(false); | ||||
|  | ||||
| 	return nativeId; | ||||
|   return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| bool OctonetData::loadChannelList() | ||||
| PVR_ERROR OctonetData::GetBackendName(std::string& name) | ||||
| { | ||||
| 	std::string jsonContent; | ||||
| 	void *f = libKodi->OpenFile(("http://" + serverAddress + "/channellist.lua?select=json").c_str(), 0); | ||||
| 	if (!f) | ||||
| 		return false; | ||||
|  | ||||
| 	char buf[1024]; | ||||
| 	while (int read = libKodi->ReadFile(f, buf, 1024)) | ||||
| 		jsonContent.append(buf, read); | ||||
|  | ||||
| 	libKodi->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; | ||||
|   name = "Digital Devices Octopus NET Client"; | ||||
|   return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| OctonetChannel* OctonetData::findChannel(int64_t nativeId) | ||||
| PVR_ERROR OctonetData::GetBackendVersion(std::string& version) | ||||
| { | ||||
| 	std::vector<OctonetChannel>::iterator it; | ||||
| 	for (it = channels.begin(); it < channels.end(); ++it) { | ||||
| 		if (it->nativeId == nativeId) | ||||
| 			return &*it; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
|   version = STR(OCTONET_VERSION); | ||||
|   return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| time_t OctonetData::parseDateTime(std::string date) | ||||
| PVR_ERROR OctonetData::GetConnectionString(std::string& connection) | ||||
| { | ||||
| 	struct tm timeinfo; | ||||
|  | ||||
| 	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); | ||||
|   connection = "connected"; // FIXME: translate? | ||||
|   return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| bool OctonetData::loadEPG(void) | ||||
| PVR_ERROR OctonetData::GetBackendHostname(std::string& hostname) | ||||
| { | ||||
| 	/* Reload at most every 30 seconds */ | ||||
| 	if (lastEpgLoad + 30 > time(NULL)) | ||||
| 		return false; | ||||
|  | ||||
| 	std::string jsonContent; | ||||
| 	void *f = libKodi->OpenFile(("http://" + serverAddress + "/epg.lua?;#|encoding=gzip").c_str(), 0); | ||||
| 	if (!f) | ||||
| 		return false; | ||||
|  | ||||
| 	char buf[1024]; | ||||
| 	while (int read = libKodi->ReadFile(f, buf, 1024)) | ||||
| 		jsonContent.append(buf, read); | ||||
|  | ||||
| 	libKodi->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) { | ||||
| 			libKodi->Log(LOG_ERROR, "EPG for unknown channel."); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		channel->epg.push_back(entry); | ||||
| 	} | ||||
|  | ||||
| 	lastEpgLoad = time(NULL); | ||||
| 	return true; | ||||
|   hostname = m_serverAddress; | ||||
|   return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| void *OctonetData::Process(void) | ||||
| PVR_ERROR OctonetData::OnSystemSleep() | ||||
| { | ||||
| 	return NULL; | ||||
|   kodi::Log(ADDON_LOG_INFO, "Received event: %s", __func__); | ||||
|   // FIXME: Disconnect? | ||||
|   return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| int OctonetData::getChannelCount(void) | ||||
| PVR_ERROR OctonetData::OnSystemWake() | ||||
| { | ||||
| 	return channels.size(); | ||||
|   kodi::Log(ADDON_LOG_INFO, "Received event: %s", __func__); | ||||
|   // FIXME:Reconnect? | ||||
|   return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| PVR_ERROR OctonetData::getChannels(ADDON_HANDLE handle, bool bRadio) | ||||
| int64_t OctonetData::ParseID(std::string id) | ||||
| { | ||||
| 	for (unsigned int i = 0; i < channels.size(); i++) | ||||
| 	{ | ||||
| 		OctonetChannel &channel = channels.at(i); | ||||
| 		if (channel.radio == bRadio) | ||||
| 		{ | ||||
| 			PVR_CHANNEL chan; | ||||
| 			memset(&chan, 0, sizeof(PVR_CHANNEL)); | ||||
|   std::hash<std::string> hash_fn; | ||||
|   int64_t nativeId = hash_fn(id); | ||||
|  | ||||
| 			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; | ||||
|   return nativeId; | ||||
| } | ||||
|  | ||||
| PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end) | ||||
| bool OctonetData::LoadChannelList() | ||||
| { | ||||
| 	for (unsigned int i = 0; i < channels.size(); i++) | ||||
| 	{ | ||||
| 		OctonetChannel &chan = channels.at(i); | ||||
| 		if (iChannelUid != chan.id) | ||||
| 			continue; | ||||
|   std::string jsonContent; | ||||
|   kodi::vfs::CFile f; | ||||
|   if (!f.OpenFile("http://" + m_serverAddress + "/channellist.lua?select=json", 0)) | ||||
|     return false; | ||||
|  | ||||
| 		if(chan.epg.empty()) { | ||||
| 			loadEPG(); | ||||
| 		} | ||||
|   char buf[1024]; | ||||
|   while (int read = f.Read(buf, 1024)) | ||||
|     jsonContent.append(buf, read); | ||||
|  | ||||
| 		// FIXME: Check if reload is needed!? | ||||
|   f.Close(); | ||||
|  | ||||
| 		std::vector<OctonetEpgEntry>::iterator it; | ||||
| 		time_t last_end = 0; | ||||
| 		for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { | ||||
| 			if (it->end > last_end) | ||||
| 				last_end = it->end; | ||||
|   Json::Value root; | ||||
|   Json::Reader reader; | ||||
|  | ||||
| 			if (it->end < start || it->start > end) { | ||||
| 				continue; | ||||
| 			} | ||||
|   if (!reader.parse(jsonContent, root, false)) | ||||
|     return false; | ||||
|  | ||||
| 			EPG_TAG entry; | ||||
| 			memset(&entry, 0, sizeof(EPG_TAG)); | ||||
|   const Json::Value groupList = root["GroupList"]; | ||||
|   for (unsigned int i = 0; i < groupList.size(); i++) | ||||
|   { | ||||
|     const Json::Value channelList = groupList[i]["ChannelList"]; | ||||
|     OctonetGroup group; | ||||
|  | ||||
| 			entry.iUniqueChannelId = chan.id; | ||||
| 			entry.iUniqueBroadcastId = it->id; | ||||
| 			entry.strTitle = it->title.c_str(); | ||||
| 			entry.strPlotOutline = it->subtitle.c_str(); | ||||
| 			entry.startTime = it->start; | ||||
| 			entry.endTime = it->end; | ||||
|     group.name = groupList[i]["Title"].asString(); | ||||
|     group.radio = group.name.compare(0, 5, "Radio") ? false : true; | ||||
|  | ||||
| 			pvr->TransferEpgEntry(handle, &entry); | ||||
| 		} | ||||
|     for (unsigned int j = 0; j < channelList.size(); j++) | ||||
|     { | ||||
|       const Json::Value channel = channelList[j]; | ||||
|       OctonetChannel chan; | ||||
|  | ||||
| 		if (last_end < end) | ||||
| 			loadEPG(); | ||||
|       chan.name = channel["Title"].asString(); | ||||
|       chan.url = "rtsp://" + m_serverAddress + "/" + channel["Request"].asString(); | ||||
|       chan.radio = group.radio; | ||||
|       chan.nativeId = ParseID(channel["ID"].asString()); | ||||
|  | ||||
| 		for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { | ||||
| 			if (it->end < start || it->start > end) { | ||||
| 				continue; | ||||
| 			} | ||||
|       chan.id = 1000 + m_channels.size(); | ||||
|       group.members.push_back(m_channels.size()); | ||||
|       m_channels.push_back(chan); | ||||
|     } | ||||
|     m_groups.push_back(group); | ||||
|   } | ||||
|  | ||||
| 			EPG_TAG entry; | ||||
| 			memset(&entry, 0, sizeof(EPG_TAG)); | ||||
|  | ||||
| 			entry.iUniqueChannelId = chan.id; | ||||
| 			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; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| OctonetChannel* OctonetData::FindChannel(int64_t nativeId) | ||||
| { | ||||
| 	return groups.size(); | ||||
|   for (auto& channel : m_channels) | ||||
|   { | ||||
|     if (channel.nativeId == nativeId) | ||||
|       return &channel; | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| PVR_ERROR OctonetData::getGroups(ADDON_HANDLE handle, bool bRadio) | ||||
| time_t OctonetData::ParseDateTime(std::string date) | ||||
| { | ||||
| 	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)); | ||||
|   struct tm timeinfo; | ||||
|  | ||||
| 			g.iPosition = 0; | ||||
| 			g.bIsRadio = group.radio; | ||||
| 			strncpy(g.strGroupName, group.name.c_str(), strlen(group.name.c_str())); | ||||
|   memset(&timeinfo, 0, sizeof(timeinfo)); | ||||
|  | ||||
| 			pvr->TransferChannelGroup(handle, &g); | ||||
| 		} | ||||
| 	} | ||||
|   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; | ||||
|   } | ||||
|  | ||||
| 	return PVR_ERROR_NO_ERROR; | ||||
|   timeinfo.tm_isdst = -1; | ||||
|  | ||||
|   return timegm(&timeinfo); | ||||
| } | ||||
|  | ||||
| PVR_ERROR OctonetData::getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) | ||||
| bool OctonetData::LoadEPG(void) | ||||
| { | ||||
| 	OctonetGroup *g = findGroup(group.strGroupName); | ||||
| 	if (g == NULL) | ||||
| 		return PVR_ERROR_UNKNOWN; | ||||
|   /* Reload at most every 30 seconds */ | ||||
|   if (m_lastEpgLoad + 30 > time(nullptr)) | ||||
|     return false; | ||||
|  | ||||
| 	for (unsigned int i = 0; i < g->members.size(); i++) | ||||
| 	{ | ||||
| 		OctonetChannel &channel = channels.at(g->members[i]); | ||||
| 		PVR_CHANNEL_GROUP_MEMBER m; | ||||
| 		memset(&m, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); | ||||
|   std::string jsonContent; | ||||
|   kodi::vfs::CFile f; | ||||
|   if (!f.OpenFile("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip", 0)) | ||||
|     return false; | ||||
|  | ||||
| 		strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName)); | ||||
| 		m.iChannelUniqueId = channel.id; | ||||
| 		m.iChannelNumber = channel.id; | ||||
|   char buf[1024]; | ||||
|   while (int read = f.Read(buf, 1024)) | ||||
|     jsonContent.append(buf, read); | ||||
|  | ||||
| 		pvr->TransferChannelGroupMember(handle, &m); | ||||
| 	} | ||||
|   f.Close(); | ||||
|  | ||||
| 	return PVR_ERROR_NO_ERROR; | ||||
|   Json::Value root; | ||||
|   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; | ||||
| } | ||||
|  | ||||
| OctonetGroup* OctonetData::findGroup(const std::string &name) | ||||
| void OctonetData::Process() | ||||
| { | ||||
| 	for (unsigned int i = 0; i < groups.size(); i++) | ||||
| 	{ | ||||
| 		if (groups.at(i).name == name) | ||||
| 			return &groups.at(i); | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
|   return; | ||||
| } | ||||
|  | ||||
| PVR_ERROR OctonetData::GetChannelsAmount(int& amount) | ||||
| { | ||||
|   amount = m_channels.size(); | ||||
|   return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| 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,92 +1,102 @@ | ||||
| #pragma once | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 2015 Julian Scheel <julian@jusst.de> | ||||
|  * Copyright (C) 2015 jusst technologies GmbH | ||||
|  * Copyright (C) 2015 Digital Devices GmbH | ||||
|  *  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. | ||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  *  See LICENSE.md for more information. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <vector> | ||||
| #pragma once | ||||
|  | ||||
| #include "p8-platform/threads/threads.h" | ||||
| #include "client.h" | ||||
| #include <atomic> | ||||
| #include <kodi/addon-instance/PVR.h> | ||||
| #include <thread> | ||||
| #include <vector> | ||||
|  | ||||
| struct OctonetEpgEntry | ||||
| { | ||||
| 	int64_t channelId; | ||||
| 	time_t start; | ||||
| 	time_t end; | ||||
| 	int id; | ||||
| 	std::string title; | ||||
| 	std::string subtitle; | ||||
|   int64_t channelId; | ||||
|   time_t start; | ||||
|   time_t end; | ||||
|   int id; | ||||
|   std::string title; | ||||
|   std::string subtitle; | ||||
| }; | ||||
|  | ||||
| struct OctonetChannel | ||||
| { | ||||
| 	int64_t nativeId; | ||||
| 	std::string name; | ||||
| 	std::string url; | ||||
| 	bool radio; | ||||
| 	int id; | ||||
|   int64_t nativeId; | ||||
|   std::string name; | ||||
|   std::string url; | ||||
|   bool radio; | ||||
|   int id; | ||||
|  | ||||
| 	std::vector<OctonetEpgEntry> epg; | ||||
|   std::vector<OctonetEpgEntry> epg; | ||||
| }; | ||||
|  | ||||
| struct OctonetGroup | ||||
| { | ||||
| 	std::string name; | ||||
| 	bool radio; | ||||
| 	std::vector<int> members; | ||||
|   std::string name; | ||||
|   bool radio; | ||||
|   std::vector<int> members; | ||||
| }; | ||||
|  | ||||
| class OctonetData : public P8PLATFORM::CThread | ||||
| class ATTRIBUTE_HIDDEN OctonetData : public kodi::addon::CInstancePVRClient | ||||
| { | ||||
| 	public: | ||||
| 		OctonetData(void); | ||||
| 		virtual ~OctonetData(void); | ||||
| public: | ||||
|   OctonetData(const std::string& octonetAddress, | ||||
|               KODI_HANDLE instance, | ||||
|               const std::string& kodiVersion); | ||||
|   ~OctonetData() override; | ||||
|  | ||||
| 		virtual int getChannelCount(void); | ||||
| 		virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio); | ||||
|   PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override; | ||||
|   PVR_ERROR GetBackendName(std::string& name) override; | ||||
|   PVR_ERROR GetBackendVersion(std::string& version) override; | ||||
|   PVR_ERROR GetConnectionString(std::string& connection) override; | ||||
|   PVR_ERROR GetBackendHostname(std::string& hostname) override; | ||||
|  | ||||
| 		virtual int getGroupCount(void); | ||||
| 		virtual PVR_ERROR getGroups(ADDON_HANDLE handle, bool bRadio); | ||||
| 		virtual PVR_ERROR getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group); | ||||
|   PVR_ERROR OnSystemSleep() override; | ||||
|   PVR_ERROR OnSystemWake() override; | ||||
|  | ||||
| 		virtual PVR_ERROR getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end); | ||||
| 		const std::string& getUrl(int id) const; | ||||
| 		const std::string& getName(int id) const; | ||||
|   PVR_ERROR GetChannelsAmount(int& amount) override; | ||||
|   PVR_ERROR GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results) override; | ||||
|  | ||||
| 	protected: | ||||
| 		virtual bool loadChannelList(void); | ||||
| 		virtual bool loadEPG(void); | ||||
| 		virtual OctonetGroup* findGroup(const std::string &name); | ||||
|   PVR_ERROR GetChannelGroupsAmount(int& amount) override; | ||||
|   PVR_ERROR GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results) override; | ||||
|   PVR_ERROR GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group, | ||||
|                                    kodi::addon::PVRChannelGroupMembersResultSet& results) override; | ||||
|  | ||||
| 		virtual void *Process(void); | ||||
|   PVR_ERROR GetEPGForChannel(int channelUid, | ||||
|                              time_t start, | ||||
|                              time_t end, | ||||
|                              kodi::addon::PVREPGTagsResultSet& results) override; | ||||
|  | ||||
| 		OctonetChannel* findChannel(int64_t nativeId); | ||||
| 		time_t parseDateTime(std::string date); | ||||
| 		int64_t parseID(std::string id); | ||||
|   bool OpenLiveStream(const kodi::addon::PVRChannel& channelinfo) override; | ||||
|   int ReadLiveStream(unsigned char* buffer, unsigned int size) override; | ||||
|   void CloseLiveStream() override; | ||||
|  | ||||
| 	private: | ||||
| 		std::string serverAddress; | ||||
| 		std::vector<OctonetChannel> channels; | ||||
| 		std::vector<OctonetGroup> groups; | ||||
| protected: | ||||
|   void Process(); | ||||
|  | ||||
| 		time_t lastEpgLoad; | ||||
|   const std::string& GetUrl(int id) const; | ||||
|   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; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										481
									
								
								src/Socket.cpp
									
									
									
									
									
								
							
							
						
						
									
										481
									
								
								src/Socket.cpp
									
									
									
									
									
								
							| @@ -1,30 +1,18 @@ | ||||
| /* | ||||
|  *      Copyright (C) 2005-2011 Team XBMC | ||||
|  *      http://www.xbmc.org | ||||
|  * | ||||
|  *  This program is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 2 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  *  Copyright (C) 2005-2020 Team Kodi | ||||
|  *  https://kodi.tv | ||||
|  * | ||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  *  See LICENSE.md for more information. | ||||
|  */ | ||||
| #include "kodi/libXBMC_addon.h" | ||||
| #include <string> | ||||
| #include "p8-platform/os.h" | ||||
| #include "client.h" | ||||
|  | ||||
| #include "Socket.h" | ||||
|  | ||||
| #include <cstdio> | ||||
| #include <kodi/General.h> | ||||
| #include <string> | ||||
|  | ||||
| using namespace std; | ||||
| using namespace ADDON; | ||||
|  | ||||
| namespace OCTO | ||||
| { | ||||
| @@ -32,28 +20,31 @@ namespace OCTO | ||||
| /* Master defines for client control */ | ||||
| #define RECEIVE_TIMEOUT 6 //sec | ||||
|  | ||||
| Socket::Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol) | ||||
| Socket::Socket(const enum SocketFamily family, | ||||
|                const enum SocketDomain domain, | ||||
|                const enum SocketType type, | ||||
|                const enum SocketProtocol protocol) | ||||
| { | ||||
|   _sd = INVALID_SOCKET; | ||||
|   _family = family; | ||||
|   _domain = domain; | ||||
|   _type = type; | ||||
|   _protocol = protocol; | ||||
|   _port = 0; | ||||
|   memset (&_sockaddr, 0, sizeof( _sockaddr ) ); | ||||
|   m_sd = INVALID_SOCKET; | ||||
|   m_family = family; | ||||
|   m_domain = domain; | ||||
|   m_type = type; | ||||
|   m_protocol = protocol; | ||||
|   m_port = 0; | ||||
|   memset(&m_sockaddr, 0, sizeof(m_sockaddr)); | ||||
| } | ||||
|  | ||||
|  | ||||
| Socket::Socket() | ||||
| { | ||||
|   // Default constructor, default settings | ||||
|   _sd = INVALID_SOCKET; | ||||
|   _family = af_inet; | ||||
|   _domain = pf_inet; | ||||
|   _type = sock_stream; | ||||
|   _protocol = tcp; | ||||
|   _port = 0; | ||||
|   memset (&_sockaddr, 0, sizeof( _sockaddr ) ); | ||||
|   m_sd = INVALID_SOCKET; | ||||
|   m_family = af_inet; | ||||
|   m_domain = pf_inet; | ||||
|   m_type = sock_stream; | ||||
|   m_protocol = tcp; | ||||
|   m_port = 0; | ||||
|   memset(&m_sockaddr, 0, sizeof(m_sockaddr)); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -65,7 +56,7 @@ Socket::~Socket() | ||||
|  | ||||
| bool Socket::setHostname(const std::string& host) | ||||
| { | ||||
|   _hostname = host; | ||||
|   m_hostname = host; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| @@ -73,9 +64,9 @@ bool Socket::close() | ||||
| { | ||||
|   if (is_valid()) | ||||
|   { | ||||
|     if (_sd != SOCKET_ERROR) | ||||
|       closesocket(_sd); | ||||
|     _sd = INVALID_SOCKET; | ||||
|     if (m_sd != SOCKET_ERROR) | ||||
|       closesocket(m_sd); | ||||
|     m_sd = INVALID_SOCKET; | ||||
|     return true; | ||||
|   } | ||||
|   return false; | ||||
| @@ -85,7 +76,7 @@ bool Socket::create() | ||||
| { | ||||
|   close(); | ||||
|  | ||||
|   if(!osInit()) | ||||
|   if (!osInit()) | ||||
|   { | ||||
|     return false; | ||||
|   } | ||||
| @@ -94,25 +85,25 @@ bool Socket::create() | ||||
| } | ||||
|  | ||||
|  | ||||
| bool Socket::bind ( const unsigned short port ) | ||||
| bool Socket::bind(const unsigned short port) | ||||
| { | ||||
|  | ||||
|   if (is_valid()) | ||||
|   { | ||||
|       close(); | ||||
|     close(); | ||||
|   } | ||||
|  | ||||
|   _sd = socket(_family, _type, _protocol); | ||||
|   _port = port; | ||||
|   _sockaddr.sin_family = (sa_family_t) _family; | ||||
|   _sockaddr.sin_addr.s_addr = INADDR_ANY;  //listen to all | ||||
|   _sockaddr.sin_port = htons( _port ); | ||||
|   m_sd = socket(m_family, m_type, m_protocol); | ||||
|   m_port = port; | ||||
|   m_sockaddr.sin_family = (sa_family_t)m_family; | ||||
|   m_sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all | ||||
|   m_sockaddr.sin_port = htons(m_port); | ||||
|  | ||||
|   int bind_return = ::bind(_sd, (sockaddr*)(&_sockaddr), sizeof(_sockaddr)); | ||||
|   int bind_return = ::bind(m_sd, (sockaddr*)(&m_sockaddr), sizeof(m_sockaddr)); | ||||
|  | ||||
|   if ( bind_return == -1 ) | ||||
|   if (bind_return == -1) | ||||
|   { | ||||
|     errormessage( getLastError(), "Socket::bind" ); | ||||
|     errormessage(getLastError(), "Socket::bind"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
| @@ -128,13 +119,13 @@ bool Socket::listen() const | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   int listen_return = ::listen (_sd, SOMAXCONN); | ||||
|   int listen_return = ::listen(m_sd, SOMAXCONN); | ||||
|   //This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h. | ||||
|   //linux 128//MAXCONNECTIONS =1 | ||||
|  | ||||
|   if (listen_return == -1) | ||||
|   { | ||||
|     errormessage( getLastError(), "Socket::listen" ); | ||||
|     errormessage(getLastError(), "Socket::listen"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
| @@ -142,23 +133,24 @@ bool Socket::listen() const | ||||
| } | ||||
|  | ||||
|  | ||||
| bool Socket::accept ( Socket& new_socket ) const | ||||
| bool Socket::accept(Socket& new_socket) const | ||||
| { | ||||
|   if (!is_valid()) | ||||
|   { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   socklen_t addr_length = sizeof( _sockaddr ); | ||||
|   new_socket._sd = ::accept(_sd, const_cast<sockaddr*>( (const sockaddr*) &_sockaddr), &addr_length ); | ||||
|   socklen_t addr_length = sizeof(m_sockaddr); | ||||
|   new_socket.m_sd = | ||||
|       ::accept(m_sd, const_cast<sockaddr*>((const sockaddr*)&m_sockaddr), &addr_length); | ||||
|  | ||||
| #ifdef TARGET_WINDOWS | ||||
|   if (new_socket._sd == INVALID_SOCKET) | ||||
|   if (new_socket.m_sd == INVALID_SOCKET) | ||||
| #else | ||||
|   if (new_socket._sd <= 0) | ||||
|   if (new_socket.m_sd <= 0) | ||||
| #endif | ||||
|   { | ||||
|     errormessage( getLastError(), "Socket::accept" ); | ||||
|     errormessage(getLastError(), "Socket::accept"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
| @@ -166,17 +158,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; | ||||
|   struct timeval tv; | ||||
|   int  result; | ||||
|   int result; | ||||
|  | ||||
|   if (!is_valid()) | ||||
|   { | ||||
| @@ -184,35 +176,35 @@ int Socket::send ( const char* data, const unsigned int len ) | ||||
|   } | ||||
|  | ||||
|   // fill with new data | ||||
|   tv.tv_sec  = 0; | ||||
|   tv.tv_sec = 0; | ||||
|   tv.tv_usec = 0; | ||||
|  | ||||
|   FD_ZERO(&set_w); | ||||
|   FD_ZERO(&set_e); | ||||
|   FD_SET(_sd, &set_w); | ||||
|   FD_SET(_sd, &set_e); | ||||
|   FD_SET(m_sd, &set_w); | ||||
|   FD_SET(m_sd, &set_e); | ||||
|  | ||||
|   result = select(FD_SETSIZE, &set_w, NULL, &set_e, &tv); | ||||
|   result = select(FD_SETSIZE, &set_w, nullptr, &set_e, &tv); | ||||
|  | ||||
|   if (result < 0) | ||||
|   { | ||||
|     libKodi->Log(LOG_ERROR, "Socket::send  - select failed"); | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::send  - select failed"); | ||||
|     close(); | ||||
|     return 0; | ||||
|   } | ||||
|   if (FD_ISSET(_sd, &set_w)) | ||||
|   if (FD_ISSET(m_sd, &set_w)) | ||||
|   { | ||||
|     libKodi->Log(LOG_ERROR, "Socket::send  - failed to send data"); | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::send  - failed to send data"); | ||||
|     close(); | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   int status = ::send(_sd, data, len, 0 ); | ||||
|   int status = ::send(m_sd, data, len, 0); | ||||
|  | ||||
|   if (status == -1) | ||||
|   { | ||||
|     errormessage( getLastError(), "Socket::send"); | ||||
|     libKodi->Log(LOG_ERROR, "Socket::send  - failed to send data"); | ||||
|     errormessage(getLastError(), "Socket::send"); | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::send  - failed to send data"); | ||||
|     close(); | ||||
|     return 0; | ||||
|   } | ||||
| @@ -220,31 +212,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 i; | ||||
|  | ||||
|   do | ||||
|   { | ||||
|     i = ::sendto(_sd, data, size, 0, (const struct sockaddr*) &_sockaddr, sizeof( _sockaddr ) ); | ||||
|     i = ::sendto(m_sd, data, size, 0, (const struct sockaddr*)&m_sockaddr, sizeof(m_sockaddr)); | ||||
|  | ||||
|     if (i <= 0) | ||||
|     { | ||||
|       errormessage( getLastError(), "Socket::sendto"); | ||||
|       errormessage(getLastError(), "Socket::sendto"); | ||||
|       osCleanup(); | ||||
|       return i; | ||||
|     } | ||||
|     sentbytes += i; | ||||
|   } while ( (sentbytes < (int) size) && (sendcompletebuffer == true)); | ||||
|   } while ((sentbytes < (int)size) && (sendcompletebuffer == true)); | ||||
|  | ||||
|   return i; | ||||
| } | ||||
|  | ||||
|  | ||||
| int Socket::receive ( std::string& data, unsigned int minpacketsize ) const | ||||
| int Socket::receive(std::string& data, unsigned int minpacketsize) const | ||||
| { | ||||
|   char * buf = NULL; | ||||
|   char* buf = nullptr; | ||||
|   int status = 0; | ||||
|  | ||||
|   if (!is_valid()) | ||||
| @@ -252,10 +244,10 @@ int Socket::receive ( std::string& data, unsigned int minpacketsize ) const | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   buf = new char [ minpacketsize + 1 ]; | ||||
|   memset ( buf, 0, minpacketsize + 1 ); | ||||
|   buf = new char[minpacketsize + 1]; | ||||
|   memset(buf, 0, minpacketsize + 1); | ||||
|  | ||||
|   status = receive( buf, minpacketsize, minpacketsize ); | ||||
|   status = receive(buf, minpacketsize, minpacketsize); | ||||
|  | ||||
|   data = buf; | ||||
|  | ||||
| @@ -265,12 +257,12 @@ int Socket::receive ( std::string& data, unsigned int minpacketsize ) const | ||||
|  | ||||
|  | ||||
| //Receive until error or \n | ||||
| bool Socket::ReadLine (string& line) | ||||
| bool Socket::ReadLine(string& line) | ||||
| { | ||||
|   fd_set         set_r, set_e; | ||||
|   timeval        timeout; | ||||
|   int            retries = 6; | ||||
|   char           buffer[2048]; | ||||
|   fd_set set_r, set_e; | ||||
|   timeval timeout; | ||||
|   int retries = 6; | ||||
|   char buffer[2048]; | ||||
|  | ||||
|   if (!is_valid()) | ||||
|     return false; | ||||
| @@ -284,20 +276,20 @@ bool Socket::ReadLine (string& line) | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     timeout.tv_sec  = RECEIVE_TIMEOUT; | ||||
|     timeout.tv_sec = RECEIVE_TIMEOUT; | ||||
|     timeout.tv_usec = 0; | ||||
|  | ||||
|     // fill with new data | ||||
|     FD_ZERO(&set_r); | ||||
|     FD_ZERO(&set_e); | ||||
|     FD_SET(_sd, &set_r); | ||||
|     FD_SET(_sd, &set_e); | ||||
|     int result = select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout); | ||||
|     FD_SET(m_sd, &set_r); | ||||
|     FD_SET(m_sd, &set_e); | ||||
|     int result = select(FD_SETSIZE, &set_r, nullptr, &set_e, &timeout); | ||||
|  | ||||
|     if (result < 0) | ||||
|     { | ||||
|       libKodi->Log(LOG_DEBUG, "%s: select failed", __FUNCTION__); | ||||
|       errormessage(getLastError(), __FUNCTION__); | ||||
|       kodi::Log(ADDON_LOG_DEBUG, "%s: select failed", __func__); | ||||
|       errormessage(getLastError(), __func__); | ||||
|       close(); | ||||
|       return false; | ||||
|     } | ||||
| @@ -306,20 +298,24 @@ bool Socket::ReadLine (string& line) | ||||
|     { | ||||
|       if (retries != 0) | ||||
|       { | ||||
|          libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__, retries); | ||||
|          retries--; | ||||
|         kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __func__, | ||||
|                   retries); | ||||
|         retries--; | ||||
|         continue; | ||||
|       } else { | ||||
|          libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", __FUNCTION__); | ||||
|          return false; | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", | ||||
|                   __func__); | ||||
|         return false; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     result = recv(_sd, buffer, sizeof(buffer) - 1, 0); | ||||
|     result = recv(m_sd, buffer, sizeof(buffer) - 1, 0); | ||||
|     if (result < 0) | ||||
|     { | ||||
|       libKodi->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__); | ||||
|       errormessage(getLastError(), __FUNCTION__); | ||||
|       kodi::Log(ADDON_LOG_DEBUG, "%s: recv failed", __func__); | ||||
|       errormessage(getLastError(), __func__); | ||||
|       close(); | ||||
|       return false; | ||||
|     } | ||||
| @@ -332,39 +328,41 @@ bool Socket::ReadLine (string& line) | ||||
| } | ||||
|  | ||||
|  | ||||
| int Socket::receive ( std::string& data) const | ||||
| int Socket::receive(std::string& data) const | ||||
| { | ||||
|   char buf[MAXRECV + 1]; | ||||
|   int status = 0; | ||||
|  | ||||
|   if ( !is_valid() ) | ||||
|   if (!is_valid()) | ||||
|   { | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   memset ( buf, 0, MAXRECV + 1 ); | ||||
|   status = receive( buf, MAXRECV, 0 ); | ||||
|   memset(buf, 0, MAXRECV + 1); | ||||
|   status = receive(buf, MAXRECV, 0); | ||||
|   data = buf; | ||||
|  | ||||
|   return status; | ||||
| } | ||||
|  | ||||
| int Socket::receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const | ||||
| int Socket::receive(char* data, | ||||
|                     const unsigned int buffersize, | ||||
|                     const unsigned int minpacketsize) const | ||||
| { | ||||
|   unsigned int receivedsize = 0; | ||||
|  | ||||
|   if ( !is_valid() ) | ||||
|   if (!is_valid()) | ||||
|   { | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   while ( (receivedsize <= minpacketsize) && (receivedsize < buffersize) ) | ||||
|   while ((receivedsize <= minpacketsize) && (receivedsize < buffersize)) | ||||
|   { | ||||
|     int status = ::recv(_sd, data+receivedsize, (buffersize - receivedsize), 0 ); | ||||
|     int status = ::recv(m_sd, data + receivedsize, (buffersize - receivedsize), 0); | ||||
|  | ||||
|     if ( status == SOCKET_ERROR ) | ||||
|     if (status == SOCKET_ERROR) | ||||
|     { | ||||
|       errormessage( getLastError(), "Socket::receive" ); | ||||
|       errormessage(getLastError(), "Socket::receive"); | ||||
|       return status; | ||||
|     } | ||||
|  | ||||
| @@ -375,35 +373,38 @@ int Socket::receive ( char* data, const unsigned int buffersize, const unsigned | ||||
| } | ||||
|  | ||||
|  | ||||
| int Socket::recvfrom ( char* data, const int buffersize, struct sockaddr* from, socklen_t* fromlen) const | ||||
| int Socket::recvfrom(char* data, | ||||
|                      const int buffersize, | ||||
|                      struct sockaddr* from, | ||||
|                      socklen_t* fromlen) const | ||||
| { | ||||
|   int status = ::recvfrom(_sd, data, buffersize, 0, from, fromlen); | ||||
|   int status = ::recvfrom(m_sd, data, buffersize, 0, from, fromlen); | ||||
|  | ||||
|   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(); | ||||
|  | ||||
|   if ( !setHostname( host ) ) | ||||
|   if (!setHostname(host)) | ||||
|   { | ||||
|     libKodi->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); | ||||
|     return false; | ||||
|   } | ||||
|   _port = port; | ||||
|   m_port = port; | ||||
|  | ||||
|   char strPort[15]; | ||||
|   snprintf(strPort, 15, "%hu", port); | ||||
|  | ||||
|   struct addrinfo hints; | ||||
|   struct addrinfo* result = NULL; | ||||
|   struct addrinfo *address = NULL; | ||||
|   struct addrinfo* result = nullptr; | ||||
|   struct addrinfo* address = nullptr; | ||||
|   memset(&hints, 0, sizeof(hints)); | ||||
|   hints.ai_family = _family; | ||||
|   hints.ai_socktype = _type; | ||||
|   hints.ai_protocol = _protocol; | ||||
|   hints.ai_family = m_family; | ||||
|   hints.ai_socktype = m_type; | ||||
|   hints.ai_protocol = m_protocol; | ||||
|  | ||||
|   int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); | ||||
|   if (retval != 0) | ||||
| @@ -412,18 +413,18 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   for (address = result; address != NULL; address = address->ai_next) | ||||
|   for (address = result; address != nullptr; address = address->ai_next) | ||||
|   { | ||||
|     // Create the socket | ||||
|     _sd = socket(address->ai_family, address->ai_socktype, address->ai_protocol); | ||||
|     m_sd = socket(address->ai_family, address->ai_socktype, address->ai_protocol); | ||||
|  | ||||
|     if (_sd == INVALID_SOCKET) | ||||
|     if (m_sd == INVALID_SOCKET) | ||||
|     { | ||||
|       errormessage(getLastError(), "Socket::create"); | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     int status = ::connect(_sd, address->ai_addr, address->ai_addrlen); | ||||
|     int status = ::connect(m_sd, address->ai_addr, address->ai_addrlen); | ||||
|     if (status == SOCKET_ERROR) | ||||
|     { | ||||
|       close(); | ||||
| @@ -436,9 +437,9 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) | ||||
|  | ||||
|   freeaddrinfo(result); | ||||
|  | ||||
|   if (address == NULL) | ||||
|   if (address == nullptr) | ||||
|   { | ||||
|     libKodi->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); | ||||
|     errormessage(getLastError(), "Socket::connect"); | ||||
|     close(); | ||||
|     return false; | ||||
| @@ -449,123 +450,124 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) | ||||
|  | ||||
| bool Socket::reconnect() | ||||
| { | ||||
|   if ( is_valid() ) | ||||
|   if (is_valid()) | ||||
|   { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   return connect(_hostname, _port); | ||||
|   return connect(m_hostname, m_port); | ||||
| } | ||||
|  | ||||
| bool Socket::is_valid() const | ||||
| { | ||||
|   return (_sd != INVALID_SOCKET); | ||||
|   return (m_sd != INVALID_SOCKET); | ||||
| } | ||||
|  | ||||
| #if defined(TARGET_WINDOWS) | ||||
| bool Socket::set_non_blocking ( const bool b ) | ||||
| bool Socket::set_non_blocking(const bool b) | ||||
| { | ||||
|   u_long iMode; | ||||
|  | ||||
|   if ( b ) | ||||
|     iMode = 1;  // enable non_blocking | ||||
|   if (b) | ||||
|     iMode = 1; // enable non_blocking | ||||
|   else | ||||
|     iMode = 0;  // disable non_blocking | ||||
|     iMode = 0; // disable non_blocking | ||||
|  | ||||
|   if (ioctlsocket(_sd, FIONBIO, &iMode) == -1) | ||||
|   if (ioctlsocket(m_sd, FIONBIO, &iMode) == -1) | ||||
|   { | ||||
|     libKodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode); | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", | ||||
|               iMode); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void Socket::errormessage( int errnum, const char* functionname) const | ||||
| void Socket::errormessage(int errnum, const char* functionname) const | ||||
| { | ||||
|   const char* errmsg = NULL; | ||||
|   const char* errmsg = nullptr; | ||||
|  | ||||
|   switch (errnum) | ||||
|   { | ||||
|   case WSANOTINITIALISED: | ||||
|     errmsg = "A successful WSAStartup call must occur before using this function."; | ||||
|     break; | ||||
|   case WSAENETDOWN: | ||||
|     errmsg = "The network subsystem or the associated service provider has failed"; | ||||
|     break; | ||||
|   case WSA_NOT_ENOUGH_MEMORY: | ||||
|     errmsg = "Insufficient memory available"; | ||||
|     break; | ||||
|   case WSA_INVALID_PARAMETER: | ||||
|     errmsg = "One or more parameters are invalid"; | ||||
|     break; | ||||
|   case WSA_OPERATION_ABORTED: | ||||
|     errmsg = "Overlapped operation aborted"; | ||||
|     break; | ||||
|   case WSAEINTR: | ||||
|     errmsg = "Interrupted function call"; | ||||
|     break; | ||||
|   case WSAEBADF: | ||||
|     errmsg = "File handle is not valid"; | ||||
|     break; | ||||
|   case WSAEACCES: | ||||
|     errmsg = "Permission denied"; | ||||
|     break; | ||||
|   case WSAEFAULT: | ||||
|     errmsg = "Bad address"; | ||||
|     break; | ||||
|   case WSAEINVAL: | ||||
|     errmsg = "Invalid argument"; | ||||
|     break; | ||||
|   case WSAENOTSOCK: | ||||
|     errmsg = "Socket operation on nonsocket"; | ||||
|     break; | ||||
|   case WSAEDESTADDRREQ: | ||||
|     errmsg = "Destination address required"; | ||||
|     break; | ||||
|   case WSAEMSGSIZE: | ||||
|     errmsg = "Message too long"; | ||||
|     break; | ||||
|   case WSAEPROTOTYPE: | ||||
|     errmsg = "Protocol wrong type for socket"; | ||||
|     break; | ||||
|   case WSAENOPROTOOPT: | ||||
|     errmsg = "Bad protocol option"; | ||||
|     break; | ||||
|   case WSAEPFNOSUPPORT: | ||||
|     errmsg = "Protocol family not supported"; | ||||
|     break; | ||||
|   case WSAEAFNOSUPPORT: | ||||
|     errmsg = "Address family not supported by protocol family"; | ||||
|     break; | ||||
|   case WSAEADDRINUSE: | ||||
|     errmsg = "Address already in use"; | ||||
|     break; | ||||
|   case WSAECONNRESET: | ||||
|     errmsg = "Connection reset by peer"; | ||||
|     break; | ||||
|   case WSAHOST_NOT_FOUND: | ||||
|     errmsg = "Authoritative answer host not found"; | ||||
|     break; | ||||
|   case WSATRY_AGAIN: | ||||
|     errmsg = "Nonauthoritative host not found, or server failure"; | ||||
|     break; | ||||
|   case WSAEISCONN: | ||||
|     errmsg = "Socket is already connected"; | ||||
|     break; | ||||
|   case WSAETIMEDOUT: | ||||
|     errmsg = "Connection timed out"; | ||||
|     break; | ||||
|   case WSAECONNREFUSED: | ||||
|     errmsg = "Connection refused"; | ||||
|     break; | ||||
|   case WSANO_DATA: | ||||
|     errmsg = "Valid name, no data record of requested type"; | ||||
|     break; | ||||
|   default: | ||||
|     errmsg = "WSA Error"; | ||||
|     case WSANOTINITIALISED: | ||||
|       errmsg = "A successful WSAStartup call must occur before using this function."; | ||||
|       break; | ||||
|     case WSAENETDOWN: | ||||
|       errmsg = "The network subsystem or the associated service provider has failed"; | ||||
|       break; | ||||
|     case WSA_NOT_ENOUGH_MEMORY: | ||||
|       errmsg = "Insufficient memory available"; | ||||
|       break; | ||||
|     case WSA_INVALID_PARAMETER: | ||||
|       errmsg = "One or more parameters are invalid"; | ||||
|       break; | ||||
|     case WSA_OPERATION_ABORTED: | ||||
|       errmsg = "Overlapped operation aborted"; | ||||
|       break; | ||||
|     case WSAEINTR: | ||||
|       errmsg = "Interrupted function call"; | ||||
|       break; | ||||
|     case WSAEBADF: | ||||
|       errmsg = "File handle is not valid"; | ||||
|       break; | ||||
|     case WSAEACCES: | ||||
|       errmsg = "Permission denied"; | ||||
|       break; | ||||
|     case WSAEFAULT: | ||||
|       errmsg = "Bad address"; | ||||
|       break; | ||||
|     case WSAEINVAL: | ||||
|       errmsg = "Invalid argument"; | ||||
|       break; | ||||
|     case WSAENOTSOCK: | ||||
|       errmsg = "Socket operation on nonsocket"; | ||||
|       break; | ||||
|     case WSAEDESTADDRREQ: | ||||
|       errmsg = "Destination address required"; | ||||
|       break; | ||||
|     case WSAEMSGSIZE: | ||||
|       errmsg = "Message too long"; | ||||
|       break; | ||||
|     case WSAEPROTOTYPE: | ||||
|       errmsg = "Protocol wrong type for socket"; | ||||
|       break; | ||||
|     case WSAENOPROTOOPT: | ||||
|       errmsg = "Bad protocol option"; | ||||
|       break; | ||||
|     case WSAEPFNOSUPPORT: | ||||
|       errmsg = "Protocol family not supported"; | ||||
|       break; | ||||
|     case WSAEAFNOSUPPORT: | ||||
|       errmsg = "Address family not supported by protocol family"; | ||||
|       break; | ||||
|     case WSAEADDRINUSE: | ||||
|       errmsg = "Address already in use"; | ||||
|       break; | ||||
|     case WSAECONNRESET: | ||||
|       errmsg = "Connection reset by peer"; | ||||
|       break; | ||||
|     case WSAHOST_NOT_FOUND: | ||||
|       errmsg = "Authoritative answer host not found"; | ||||
|       break; | ||||
|     case WSATRY_AGAIN: | ||||
|       errmsg = "Nonauthoritative host not found, or server failure"; | ||||
|       break; | ||||
|     case WSAEISCONN: | ||||
|       errmsg = "Socket is already connected"; | ||||
|       break; | ||||
|     case WSAETIMEDOUT: | ||||
|       errmsg = "Connection timed out"; | ||||
|       break; | ||||
|     case WSAECONNREFUSED: | ||||
|       errmsg = "Connection refused"; | ||||
|       break; | ||||
|     case WSANO_DATA: | ||||
|       errmsg = "Valid name, no data record of requested type"; | ||||
|       break; | ||||
|     default: | ||||
|       errmsg = "WSA Error"; | ||||
|   } | ||||
|   libKodi->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); | ||||
|   kodi::Log(ADDON_LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); | ||||
| } | ||||
|  | ||||
| int Socket::getLastError() const | ||||
| @@ -579,15 +581,15 @@ bool Socket::osInit() | ||||
| { | ||||
|   win_usage_count++; | ||||
|   // initialize winsock: | ||||
|   if (WSAStartup(MAKEWORD(2,2),&_wsaData) != 0) | ||||
|   if (WSAStartup(MAKEWORD(2, 2), &m_wsaData) != 0) | ||||
|   { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   WORD wVersionRequested = MAKEWORD(2,2); | ||||
|   WORD wVersionRequested = MAKEWORD(2, 2); | ||||
|  | ||||
|   // check version | ||||
|   if (_wsaData.wVersion != wVersionRequested) | ||||
|   if (m_wsaData.wVersion != wVersionRequested) | ||||
|   { | ||||
|     return false; | ||||
|   } | ||||
| @@ -598,42 +600,42 @@ bool Socket::osInit() | ||||
| void Socket::osCleanup() | ||||
| { | ||||
|   win_usage_count--; | ||||
|   if(win_usage_count == 0) | ||||
|   if (win_usage_count == 0) | ||||
|   { | ||||
|     WSACleanup(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| #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; | ||||
|  | ||||
|   opts = fcntl(_sd, F_GETFL); | ||||
|   opts = fcntl(m_sd, F_GETFL); | ||||
|  | ||||
|   if ( opts < 0 ) | ||||
|   if (opts < 0) | ||||
|   { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if ( b ) | ||||
|     opts = ( opts | O_NONBLOCK ); | ||||
|   if (b) | ||||
|     opts = (opts | O_NONBLOCK); | ||||
|   else | ||||
|     opts = ( opts & ~O_NONBLOCK ); | ||||
|     opts = (opts & ~O_NONBLOCK); | ||||
|  | ||||
|   if(fcntl (_sd , F_SETFL, opts) == -1) | ||||
|   if (fcntl(m_sd, F_SETFL, opts) == -1) | ||||
|   { | ||||
|     libKodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); | ||||
|     return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void Socket::errormessage( int errnum, const char* functionname) const | ||||
| void Socket::errormessage(int errnum, const char* functionname) const | ||||
| { | ||||
|   const char* errmsg = NULL; | ||||
|   const char* errmsg = nullptr; | ||||
|  | ||||
|   switch ( errnum ) | ||||
|   switch (errnum) | ||||
|   { | ||||
|     case EAGAIN: //same as EWOULDBLOCK | ||||
|       errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block"; | ||||
| @@ -660,7 +662,8 @@ void Socket::errormessage( int errnum, const char* functionname) const | ||||
|       errmsg = "ENOTSOCK: The argument is not a valid socket"; | ||||
|       break; | ||||
|     case EMSGSIZE: | ||||
|       errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the message to be sent made this impossible"; | ||||
|       errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the " | ||||
|                "message to be sent made this impossible"; | ||||
|       break; | ||||
|     case ENOBUFS: | ||||
|       errmsg = "ENOBUFS: The output queue for a network interface was full"; | ||||
| @@ -672,7 +675,8 @@ void Socket::errormessage( int errnum, const char* functionname) const | ||||
|       errmsg = "EPIPE: The local end has been shut down on a connection oriented socket"; | ||||
|       break; | ||||
|     case EPROTONOSUPPORT: | ||||
|       errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported within this domain"; | ||||
|       errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported " | ||||
|                "within this domain"; | ||||
|       break; | ||||
|     case EAFNOSUPPORT: | ||||
|       errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family"; | ||||
| @@ -684,13 +688,16 @@ void Socket::errormessage( int errnum, const char* functionname) const | ||||
|       errmsg = "EMFILE: Process file table overflow"; | ||||
|       break; | ||||
|     case EACCES: | ||||
|       errmsg = "EACCES: Permission to create a socket of the specified type and/or protocol is denied"; | ||||
|       errmsg = | ||||
|           "EACCES: Permission to create a socket of the specified type and/or protocol is denied"; | ||||
|       break; | ||||
|     case ECONNREFUSED: | ||||
|       errmsg = "ECONNREFUSED: A remote host refused to allow the network connection (typically because it is not running the requested service)"; | ||||
|       errmsg = "ECONNREFUSED: A remote host refused to allow the network connection (typically " | ||||
|                "because it is not running the requested service)"; | ||||
|       break; | ||||
|     case ENOTCONN: | ||||
|       errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not been connected"; | ||||
|       errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not " | ||||
|                "been connected"; | ||||
|       break; | ||||
|     //case E: | ||||
|     //	errmsg = ""; | ||||
| @@ -699,7 +706,7 @@ void Socket::errormessage( int errnum, const char* functionname) const | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   libKodi->Log(LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); | ||||
|   kodi::Log(ADDON_LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); | ||||
| } | ||||
|  | ||||
| int Socket::getLastError() const | ||||
|   | ||||
							
								
								
									
										428
									
								
								src/Socket.h
									
									
									
									
									
								
							
							
						
						
									
										428
									
								
								src/Socket.h
									
									
									
									
									
								
							| @@ -1,83 +1,73 @@ | ||||
| /* | ||||
|  *      Copyright (C) 2005-2011 Team XBMC | ||||
|  *      http://www.xbmc.org | ||||
|  * | ||||
|  *  This Program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2, or (at your option) | ||||
|  *  any later version. | ||||
|  * | ||||
|  *  This Program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  *  Copyright (C) 2005-2020 Team Kodi | ||||
|  *  https://kodi.tv | ||||
|  * | ||||
|  *  SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  *  See LICENSE.md for more information. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| //Include platform specific datatypes, header files, defines and constants: | ||||
| #if defined TARGET_WINDOWS | ||||
|   #define WIN32_LEAN_AND_MEAN           // Enable LEAN_AND_MEAN support | ||||
|   #pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" | ||||
|   #include <winsock2.h> | ||||
|   #include <WS2tcpip.h> | ||||
|   #pragma warning(default:4005) | ||||
|   #include <windows.h> | ||||
| #define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support | ||||
| #pragma warning(disable : 4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" | ||||
| #include <WS2tcpip.h> | ||||
| #include <winsock2.h> | ||||
| #pragma warning(default : 4005) | ||||
| #include <windows.h> | ||||
|  | ||||
|   #ifndef NI_MAXHOST | ||||
|     #define NI_MAXHOST 1025 | ||||
|   #endif | ||||
| #ifndef NI_MAXHOST | ||||
| #define NI_MAXHOST 1025 | ||||
| #endif | ||||
|  | ||||
|   #ifndef socklen_t | ||||
|     typedef int socklen_t; | ||||
|   #endif | ||||
|   #ifndef ipaddr_t | ||||
|     typedef unsigned long ipaddr_t; | ||||
|   #endif | ||||
|   #ifndef port_t | ||||
|     typedef unsigned short port_t; | ||||
|   #endif | ||||
|   #ifndef sa_family_t | ||||
|     #define sa_family_t ADDRESS_FAMILY | ||||
|   #endif | ||||
| #ifndef socklen_t | ||||
| typedef int socklen_t; | ||||
| #endif | ||||
| #ifndef ipaddr_t | ||||
| typedef unsigned long ipaddr_t; | ||||
| #endif | ||||
| #ifndef port_t | ||||
| typedef unsigned short port_t; | ||||
| #endif | ||||
| #ifndef sa_family_t | ||||
| #define sa_family_t ADDRESS_FAMILY | ||||
| #endif | ||||
| #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||
| #ifdef SOCKADDR_IN | ||||
| #undef SOCKADDR_IN | ||||
| #endif | ||||
|   #include <sys/types.h>     /* for socket,connect */ | ||||
|   #include <sys/socket.h>    /* for socket,connect */ | ||||
|   #include <sys/un.h>        /* for Unix socket */ | ||||
|   #include <arpa/inet.h>     /* for inet_pton */ | ||||
|   #include <netdb.h>         /* for gethostbyname */ | ||||
|   #include <netinet/in.h>    /* for htons */ | ||||
|   #include <unistd.h>        /* for read, write, close */ | ||||
|   #include <errno.h> | ||||
|   #include <fcntl.h> | ||||
| #include <arpa/inet.h> /* for inet_pton */ | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <netdb.h> /* for gethostbyname */ | ||||
| #include <netinet/in.h> /* for htons */ | ||||
| #include <sys/socket.h> /* for socket,connect */ | ||||
| #include <sys/types.h> /* for socket,connect */ | ||||
| #include <sys/un.h> /* for Unix socket */ | ||||
| #include <unistd.h> /* for read, write, close */ | ||||
|  | ||||
|   typedef int SOCKET; | ||||
|   typedef sockaddr SOCKADDR; | ||||
|   typedef sockaddr_in SOCKADDR_IN; | ||||
|   #ifndef INVALID_SOCKET | ||||
|   #define INVALID_SOCKET (-1) | ||||
|   #endif | ||||
|   #define SOCKET_ERROR (-1) | ||||
| typedef int SOCKET; | ||||
| typedef sockaddr SOCKADDR; | ||||
| typedef sockaddr_in SOCKADDR_IN; | ||||
| #ifndef INVALID_SOCKET | ||||
| #define INVALID_SOCKET (-1) | ||||
| #endif | ||||
| #define SOCKET_ERROR (-1) | ||||
|  | ||||
|   #define closesocket(sd) ::close(sd) | ||||
| #define closesocket(sd) ::close(sd) | ||||
| #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 | ||||
|  | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| namespace OCTO | ||||
| { | ||||
|  | ||||
| #define MAXCONNECTIONS 1  ///< Maximum number of pending connections before "Connection refused" | ||||
| #define MAXRECV 1500      ///< Maximum packet size | ||||
| #define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused" | ||||
| #define MAXRECV 1500 ///< Maximum packet size | ||||
|  | ||||
| enum SocketFamily | ||||
| { | ||||
| @@ -88,10 +78,10 @@ enum SocketFamily | ||||
|  | ||||
| enum SocketDomain | ||||
| { | ||||
|   #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||
|     pf_unix  = PF_UNIX, | ||||
|     pf_local = PF_LOCAL, | ||||
|   #endif | ||||
| #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||
|   pf_unix = PF_UNIX, | ||||
|   pf_local = PF_LOCAL, | ||||
| #endif | ||||
|   pf_inet = PF_INET | ||||
| }; | ||||
|  | ||||
| @@ -109,197 +99,187 @@ enum SocketProtocol | ||||
|  | ||||
| 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(); | ||||
|  | ||||
|     /*! | ||||
|      * 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 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 setFamily | ||||
|      * \param family    Can be af_inet or af_inet6. Default: af_inet | ||||
|      */ | ||||
|     void setFamily(const enum SocketFamily family) | ||||
|     { | ||||
|       _family = family; | ||||
|     }; | ||||
|   /*! | ||||
|    * Socket setDomain | ||||
|    * \param domain    Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet | ||||
|    */ | ||||
|   void setDomain(const enum SocketDomain domain) { m_domain = domain; }; | ||||
|  | ||||
|     /*! | ||||
|      * Socket setDomain | ||||
|      * \param domain    Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet | ||||
|      */ | ||||
|     void setDomain(const enum SocketDomain domain) | ||||
|     { | ||||
|       _domain = domain; | ||||
|     }; | ||||
|   /*! | ||||
|    * Socket setType | ||||
|    * \param type    Can be sock_stream or sock_dgram. Default: sock_stream. | ||||
|    */ | ||||
|   void setType(const enum SocketType type) { m_type = type; }; | ||||
|  | ||||
|     /*! | ||||
|      * Socket setType | ||||
|      * \param type    Can be sock_stream or sock_dgram. Default: sock_stream. | ||||
|      */ | ||||
|     void setType(const enum SocketType type) | ||||
|     { | ||||
|       _type = type; | ||||
|     }; | ||||
|   /*! | ||||
|    * Socket setProtocol | ||||
|    * \param protocol    Can be tcp or udp. Default: tcp. | ||||
|    */ | ||||
|   void setProtocol(const enum SocketProtocol protocol) { m_protocol = protocol; }; | ||||
|  | ||||
|     /*! | ||||
|      * Socket setProtocol | ||||
|      * \param protocol    Can be tcp or udp. Default: tcp. | ||||
|      */ | ||||
|     void setProtocol(const enum SocketProtocol protocol) | ||||
|     { | ||||
|       _protocol = protocol; | ||||
|     }; | ||||
|   /*! | ||||
|    * Socket setPort | ||||
|    * \param port    port number for socket communication | ||||
|    */ | ||||
|   void setPort(const unsigned short port) { m_sockaddr.sin_port = htons(port); }; | ||||
|  | ||||
|     /*! | ||||
|      * Socket setPort | ||||
|      * \param port    port number for socket communication | ||||
|      */ | ||||
|     void setPort (const unsigned short port) | ||||
|     { | ||||
|       _sockaddr.sin_port = htons ( port ); | ||||
|     }; | ||||
|   bool setHostname(const std::string& host); | ||||
|  | ||||
|     bool setHostname ( const std::string& host ); | ||||
|   // Server initialization | ||||
|  | ||||
|     // Server initialization | ||||
|   /*! | ||||
|    * Socket create | ||||
|    * Create a new socket | ||||
|    * \return     True if succesful | ||||
|    */ | ||||
|   bool create(); | ||||
|  | ||||
|     /*! | ||||
|      * Socket create | ||||
|      * Create a new socket | ||||
|      * \return     True if succesful | ||||
|      */ | ||||
|     bool create(); | ||||
|   /*! | ||||
|    * Socket close | ||||
|    * Close the socket | ||||
|    * \return     True if succesful | ||||
|    */ | ||||
|   bool close(); | ||||
|  | ||||
|     /*! | ||||
|      * Socket close | ||||
|      * Close the socket | ||||
|      * \return     True if succesful | ||||
|      */ | ||||
|     bool close(); | ||||
|   /*! | ||||
|    * Socket bind | ||||
|    */ | ||||
|   bool bind(const unsigned short port); | ||||
|   bool listen() const; | ||||
|   bool accept(Socket& socket) const; | ||||
|  | ||||
|     /*! | ||||
|      * Socket bind | ||||
|      */ | ||||
|     bool bind ( const unsigned short port ); | ||||
|     bool listen() const; | ||||
|     bool accept ( Socket& socket ) const; | ||||
|   // Client initialization | ||||
|   bool connect(const std::string& host, const unsigned short port); | ||||
|  | ||||
|     // Client initialization | ||||
|     bool connect ( const std::string& host, const unsigned short port ); | ||||
|   bool reconnect(); | ||||
|  | ||||
|     bool reconnect(); | ||||
|   // Data Transmission | ||||
|  | ||||
|     // Data Transmission | ||||
|   /*! | ||||
|    * Socket send function | ||||
|    * | ||||
|    * \param data    Reference to a std::string with the data to transmit | ||||
|    * \return    Number of bytes send or -1 in case of an error | ||||
|    */ | ||||
|   int send(const std::string& data); | ||||
|  | ||||
|     /*! | ||||
|      * Socket send function | ||||
|      * | ||||
|      * \param data    Reference to a std::string with the data to transmit | ||||
|      * \return    Number of bytes send or -1 in case of an error | ||||
|      */ | ||||
|     int send ( const std::string& data ); | ||||
|   /*! | ||||
|    * Socket send function | ||||
|    * | ||||
|    * \param data    Pointer to a character array of size 'size' with the data to transmit | ||||
|    * \param size    Length of the data to transmit | ||||
|    * \return    Number of bytes send or -1 in case of an error | ||||
|    */ | ||||
|   int send(const char* data, const unsigned int size); | ||||
|  | ||||
|     /*! | ||||
|      * Socket send function | ||||
|      * | ||||
|      * \param data    Pointer to a character array of size 'size' with the data to transmit | ||||
|      * \param size    Length of the data to transmit | ||||
|      * \return    Number of bytes send or -1 in case of an error | ||||
|      */ | ||||
|     int send ( const char* data, const unsigned int size ); | ||||
|   /*! | ||||
|    * Socket sendto function | ||||
|    * | ||||
|    * \param data    Reference to a std::string with the data to transmit | ||||
|    * \param size    Length of the data to transmit | ||||
|    * \param sendcompletebuffer    If 'true': do not return until the complete buffer is transmitted | ||||
|    * \return    Number of bytes send or -1 in case of an error | ||||
|    */ | ||||
|   int sendto(const char* data, unsigned int size, bool sendcompletebuffer = false); | ||||
|   // Data Receive | ||||
|  | ||||
|     /*! | ||||
|      * Socket sendto function | ||||
|      * | ||||
|      * \param data    Reference to a std::string with the data to transmit | ||||
|      * \param size    Length of the data to transmit | ||||
|      * \param sendcompletebuffer    If 'true': do not return until the complete buffer is transmitted | ||||
|      * \return    Number of bytes send or -1 in case of an error | ||||
|      */ | ||||
|     int sendto ( const char* data, unsigned int size, bool sendcompletebuffer = false); | ||||
|     // Data Receive | ||||
|   /*! | ||||
|    * Socket receive function | ||||
|    * | ||||
|    * \param data    Reference to a std::string for storage of the received data. | ||||
|    * \param minpacketsize    The minimum number of bytes that should be received before returning from this function | ||||
|    * \return    Number of bytes received or SOCKET_ERROR | ||||
|    */ | ||||
|   int receive(std::string& data, unsigned int minpacketsize) const; | ||||
|  | ||||
|     /*! | ||||
|      * Socket receive function | ||||
|      * | ||||
|      * \param data    Reference to a std::string for storage of the received data. | ||||
|      * \param minpacketsize    The minimum number of bytes that should be received before returning from this function | ||||
|      * \return    Number of bytes received or SOCKET_ERROR | ||||
|      */ | ||||
|     int receive ( std::string& data, unsigned int minpacketsize ) const; | ||||
|   /*! | ||||
|    * Socket receive function | ||||
|    * | ||||
|    * \param data    Reference to a std::string for storage of the received data. | ||||
|    * \return    Number of bytes received or SOCKET_ERROR | ||||
|    */ | ||||
|   int receive(std::string& data) const; | ||||
|  | ||||
|     /*! | ||||
|      * Socket receive function | ||||
|      * | ||||
|      * \param data    Reference to a std::string for storage of the received data. | ||||
|      * \return    Number of bytes received or SOCKET_ERROR | ||||
|      */ | ||||
|     int receive ( std::string& data ) const; | ||||
|   /*! | ||||
|    * Socket receive function | ||||
|    * | ||||
|    * \param data    Pointer to a character array of size buffersize. Used to store the received data. | ||||
|    * \param buffersize    Size of the 'data' buffer | ||||
|    * \param minpacketsize    Specifies the minimum number of bytes that need to be received before returning | ||||
|    * \return    Number of bytes received or SOCKET_ERROR | ||||
|    */ | ||||
|   int receive(char* data, const unsigned int buffersize, const unsigned int minpacketsize) const; | ||||
|  | ||||
|     /*! | ||||
|      * Socket receive function | ||||
|      * | ||||
|      * \param data    Pointer to a character array of size buffersize. Used to store the received data. | ||||
|      * \param buffersize    Size of the 'data' buffer | ||||
|      * \param minpacketsize    Specifies the minimum number of bytes that need to be received before returning | ||||
|      * \return    Number of bytes received or SOCKET_ERROR | ||||
|      */ | ||||
|     int receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const; | ||||
|   /*! | ||||
|    * Socket recvfrom function | ||||
|    * | ||||
|    * \param data    Pointer to a character array of size buffersize. Used to store the received data. | ||||
|    * \param buffersize    Size of the 'data' buffer | ||||
|    * \param from        Optional: pointer to a sockaddr struct that will get the address from which the data is received | ||||
|    * \param fromlen    Optional, only required if 'from' is given: length of from struct | ||||
|    * \return    Number of bytes received or SOCKET_ERROR | ||||
|    */ | ||||
|   int recvfrom(char* data, | ||||
|                const int buffersize, | ||||
|                struct sockaddr* from = nullptr, | ||||
|                socklen_t* fromlen = nullptr) const; | ||||
|  | ||||
|     /*! | ||||
|      * Socket recvfrom function | ||||
|      * | ||||
|      * \param data    Pointer to a character array of size buffersize. Used to store the received data. | ||||
|      * \param buffersize    Size of the 'data' buffer | ||||
|      * \param from        Optional: pointer to a sockaddr struct that will get the address from which the data is received | ||||
|      * \param fromlen    Optional, only required if 'from' is given: length of from struct | ||||
|      * \return    Number of bytes received or SOCKET_ERROR | ||||
|      */ | ||||
|     int recvfrom ( char* data, const int buffersize, struct sockaddr* from = NULL, socklen_t* fromlen = NULL) const; | ||||
|   bool set_non_blocking(const bool); | ||||
|  | ||||
|     bool set_non_blocking ( const bool ); | ||||
|   bool ReadLine(std::string& line); | ||||
|  | ||||
|     bool ReadLine (std::string& line); | ||||
|   bool is_valid() const; | ||||
|  | ||||
|     bool is_valid() const; | ||||
| private: | ||||
|   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 | ||||
|  | ||||
|   private: | ||||
|   enum SocketFamily m_family; ///< Socket Address Family | ||||
|   enum SocketProtocol m_protocol; ///< Socket Protocol | ||||
|   enum SocketType m_type; ///< Socket Type | ||||
|   enum SocketDomain m_domain; ///< Socket domain | ||||
|  | ||||
|     SOCKET _sd;                         ///< Socket Descriptor | ||||
|     SOCKADDR_IN _sockaddr;              ///< Socket Address | ||||
|     //struct addrinfo* _addrinfo;         ///< Socket address info | ||||
|     std::string _hostname;              ///< Hostname | ||||
|     unsigned short _port;               ///< Port number | ||||
| #ifdef TARGET_WINDOWS | ||||
|   WSADATA m_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 | ||||
|  | ||||
|     enum SocketFamily _family;          ///< Socket Address Family | ||||
|     enum SocketProtocol _protocol;      ///< Socket Protocol | ||||
|     enum SocketType _type;              ///< Socket Type | ||||
|     enum SocketDomain _domain;          ///< Socket domain | ||||
|  | ||||
|     #ifdef TARGET_WINDOWS | ||||
|       WSADATA _wsaData;                 ///< Windows Socket data | ||||
|       static int win_usage_count;       ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used | ||||
|     #endif | ||||
|  | ||||
|     void errormessage( int errornum, const char* functionname = NULL) const; | ||||
|     int getLastError(void) const; | ||||
|     bool osInit(); | ||||
|     void osCleanup(); | ||||
|   void errormessage(int errornum, const char* functionname = nullptr) const; | ||||
|   int getLastError(void) const; | ||||
|   bool osInit(); | ||||
|   void osCleanup(); | ||||
| }; | ||||
|  | ||||
| } //namespace OCTO | ||||
|   | ||||
							
								
								
									
										62
									
								
								src/addon.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/addon.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| /* | ||||
|  *  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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/addon.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  *  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; | ||||
| }; | ||||
							
								
								
									
										286
									
								
								src/client.cpp
									
									
									
									
									
								
							
							
						
						
									
										286
									
								
								src/client.cpp
									
									
									
									
									
								
							| @@ -1,286 +0,0 @@ | ||||
| /* | ||||
|  * 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 <kodi/xbmc_pvr_dll.h> | ||||
| #include <kodi/libXBMC_addon.h> | ||||
| #include <p8-platform/util/util.h> | ||||
| #include <kodi/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 *libKodi = 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 (libKodi->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; | ||||
| 	libKodi = new CHelper_libXBMC_addon; | ||||
| 	if (!libKodi->RegisterMe(callbacks)) { | ||||
| 		libKodi->Log(LOG_ERROR, "%s: Failed to register octonet addon", __func__); | ||||
| 		SAFE_DELETE(libKodi); | ||||
| 		return ADDON_STATUS_PERMANENT_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	pvr = new CHelper_libXBMC_pvr; | ||||
| 	if (!pvr->RegisterMe(callbacks)) { | ||||
| 		libKodi->Log(LOG_ERROR, "%s: Failed to register octonet pvr addon", __func__); | ||||
| 		SAFE_DELETE(pvr); | ||||
| 		SAFE_DELETE(libKodi); | ||||
| 		return ADDON_STATUS_PERMANENT_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	libKodi->Log(LOG_DEBUG, "%s: Creating octonet pvr addon", __func__); | ||||
| 	ADDON_ReadSettings(); | ||||
|  | ||||
| 	data = new OctonetData; | ||||
|  | ||||
| 	addonStatus = ADDON_STATUS_OK; | ||||
| 	return addonStatus; | ||||
| } | ||||
|  | ||||
| void ADDON_Destroy() | ||||
| { | ||||
| 	delete pvr; | ||||
| 	delete libKodi; | ||||
| 	addonStatus = ADDON_STATUS_UNKNOWN; | ||||
| } | ||||
|  | ||||
| ADDON_STATUS ADDON_GetStatus() | ||||
| { | ||||
| 	return addonStatus; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| /* KODI PVR Addon functions | ||||
|  * see xbmc_pvr_dll.h */ | ||||
| extern "C" | ||||
| { | ||||
|  | ||||
| PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities) | ||||
| { | ||||
| 	pCapabilities->bSupportsTV = true; | ||||
| 	pCapabilities->bSupportsRadio = true; | ||||
| 	pCapabilities->bSupportsChannelGroups = true; | ||||
| 	pCapabilities->bSupportsEPG = true; | ||||
| 	pCapabilities->bSupportsRecordings = false; | ||||
| 	pCapabilities->bSupportsRecordingsRename = false; | ||||
| 	pCapabilities->bSupportsRecordingsLifetimeChange = false; | ||||
| 	pCapabilities->bSupportsDescrambleInfo = false; | ||||
|  | ||||
| 	return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| const char* GetBackendName(void) | ||||
| { | ||||
| 	return "Digital Devices Octopus NET Client"; | ||||
| } | ||||
|  | ||||
| const char* GetBackendVersion(void) | ||||
| { | ||||
| 	return STR(OCTONET_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() { | ||||
| 	libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); | ||||
| 	// FIXME: Disconnect? | ||||
| } | ||||
|  | ||||
| void OnSystemWake() { | ||||
| 	libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); | ||||
| 	// FIXME:Reconnect? | ||||
| } | ||||
|  | ||||
| void OnPowerSavingActivated() {} | ||||
| void OnPowerSavingDeactivated() {} | ||||
|  | ||||
| /* EPG */ | ||||
| PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, int iChannelUid, time_t iStart, time_t iEnd) | ||||
| { | ||||
| 	return data->getEPG(handle, iChannelUid, iStart, iEnd); | ||||
| } | ||||
|  | ||||
| PVR_ERROR IsEPGTagRecordable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
| PVR_ERROR IsEPGTagPlayable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
|  | ||||
| /* 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 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 SetRecordingLifetime(const PVR_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 properties handling */ | ||||
| PVR_ERROR GetStreamReadChunkSize(int* chunksize) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
| PVR_ERROR GetChannelStreamProperties(const PVR_CHANNEL*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
| PVR_ERROR GetRecordingStreamProperties(const PVR_RECORDING*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
| PVR_ERROR GetEPGTagStreamProperties(const EPG_TAG*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
| PVR_ERROR GetEPGTagEdl(const EPG_TAG* epgTag, PVR_EDL_ENTRY edl[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
|  | ||||
| /* 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 LengthLiveStream(void) { return -1; } | ||||
| bool IsRealTimeStream(void) { return true; } | ||||
|  | ||||
| PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS& signalStatus) { | ||||
| 	memset(&signalStatus, 0, sizeof(PVR_SIGNAL_STATUS)); | ||||
| 	rtsp_fill_signal_status(signalStatus); | ||||
| 	return PVR_ERROR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES *times) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
| PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) { return PVR_ERROR_NOT_IMPLEMENTED; } | ||||
| PVR_ERROR GetDescrambleInfo(PVR_DESCRAMBLE_INFO*) { 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 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; } | ||||
| void FillBuffer(bool mode) {} | ||||
|  | ||||
| /* Various helper functions */ | ||||
| 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; } | ||||
|  | ||||
| const char* GetBackendHostname() | ||||
| { | ||||
| 	return octonetAddress.c_str(); | ||||
| } | ||||
|  | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/client.h
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								src/client.h
									
									
									
									
									
								
							| @@ -1,35 +0,0 @@ | ||||
| #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 "kodi/libXBMC_addon.h" | ||||
| #include "kodi/libXBMC_pvr.h" | ||||
|  | ||||
| #ifndef __func__ | ||||
| #define __func__ __FUNCTION__ | ||||
| #endif | ||||
|  | ||||
| extern ADDON::CHelper_libXBMC_addon *libKodi; | ||||
| extern CHelper_libXBMC_pvr *pvr; | ||||
|  | ||||
| /* IP or hostname of the octonet to be connected to */ | ||||
| extern std::string octonetAddress; | ||||
| @@ -1,32 +1,41 @@ | ||||
| /* | ||||
|  *  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 "Socket.h" | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <cctype> | ||||
| #include <iterator> | ||||
| #include "Socket.h" | ||||
| #include "client.h" | ||||
| #include <p8-platform/util/util.h> | ||||
| #include <kodi/libXBMC_addon.h> | ||||
| #include <cstring> | ||||
| #include <iterator> | ||||
| #include <sstream> | ||||
|  | ||||
| #if defined(_WIN32) || defined(_WIN64) | ||||
| #define strtok_r strtok_s | ||||
| #define strncasecmp _strnicmp | ||||
|  | ||||
| int vasprintf(char **sptr, char *fmt, va_list argv) { | ||||
| 	int wanted = vsnprintf(*sptr = NULL, 0, fmt, argv); | ||||
| 	if((wanted < 0) || ((*sptr = (char *)malloc(1 + wanted)) == NULL)) | ||||
| 		return -1; | ||||
| 	return vsprintf(*sptr, fmt, argv); | ||||
| int vasprintf(char** sptr, char* fmt, va_list argv) | ||||
| { | ||||
|   int wanted = vsnprintf(*sptr = nullptr, 0, fmt, argv); | ||||
|   if ((wanted < 0) || ((*sptr = (char*)malloc(1 + wanted)) == nullptr)) | ||||
|     return -1; | ||||
|   return vsprintf(*sptr, fmt, argv); | ||||
| } | ||||
|  | ||||
| int asprintf(char **sptr, char *fmt, ...) { | ||||
| 	int retval; | ||||
| 	va_list argv; | ||||
| 	va_start(argv, fmt); | ||||
| 	retval = vasprintf(sptr, fmt, argv); | ||||
| 	va_end(argv); | ||||
| 	return retval; | ||||
| int asprintf(char** sptr, char* fmt, ...) | ||||
| { | ||||
|   int retval; | ||||
|   va_list argv; | ||||
|   va_start(argv, fmt); | ||||
|   retval = vasprintf(sptr, fmt, argv); | ||||
|   va_end(argv); | ||||
|   return retval; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -40,441 +49,501 @@ int asprintf(char **sptr, char *fmt, ...) { | ||||
| #define RTCP_BUFFER_SIZE 1024 | ||||
|  | ||||
| using namespace std; | ||||
| using namespace ADDON; | ||||
| using namespace OCTO; | ||||
|  | ||||
| enum rtsp_state { | ||||
| 	RTSP_IDLE, | ||||
| 	RTSP_DESCRIBE, | ||||
| 	RTSP_SETUP, | ||||
| 	RTSP_PLAY, | ||||
| 	RTSP_RUNNING | ||||
| enum rtsp_state | ||||
| { | ||||
|   RTSP_IDLE, | ||||
|   RTSP_DESCRIBE, | ||||
|   RTSP_SETUP, | ||||
|   RTSP_PLAY, | ||||
|   RTSP_RUNNING | ||||
| }; | ||||
|  | ||||
| enum rtsp_result { | ||||
| 	RTSP_RESULT_OK = 200, | ||||
| enum rtsp_result | ||||
| { | ||||
|   RTSP_RESULT_OK = 200, | ||||
| }; | ||||
|  | ||||
| struct rtsp_client { | ||||
| 	char *content_base; | ||||
| 	char *control; | ||||
| 	char session_id[64]; | ||||
| 	uint16_t stream_id; | ||||
| 	int keepalive_interval; | ||||
| struct rtsp_client | ||||
| { | ||||
|   char* content_base; | ||||
|   char* control; | ||||
|   char session_id[64]; | ||||
|   uint16_t stream_id; | ||||
|   int keepalive_interval; | ||||
|  | ||||
| 	char udp_address[UDP_ADDRESS_LEN]; | ||||
| 	uint16_t udp_port; | ||||
|   char udp_address[UDP_ADDRESS_LEN]; | ||||
|   uint16_t udp_port; | ||||
|  | ||||
| 	Socket tcp_sock; | ||||
| 	Socket udp_sock; | ||||
| 	Socket rtcp_sock; | ||||
|   Socket tcp_sock; | ||||
|   Socket udp_sock; | ||||
|   Socket rtcp_sock; | ||||
|  | ||||
| 	enum rtsp_state state; | ||||
| 	int cseq; | ||||
|   enum rtsp_state state; | ||||
|   int cseq; | ||||
|  | ||||
| 	size_t fifo_size; | ||||
| 	uint16_t last_seq_nr; | ||||
|   size_t fifo_size; | ||||
|   uint16_t last_seq_nr; | ||||
|  | ||||
| 	string name; | ||||
| 	int level; | ||||
| 	int quality; | ||||
|   string name; | ||||
|   int level; | ||||
|   int quality; | ||||
| }; | ||||
|  | ||||
| struct url { | ||||
| 	string protocol; | ||||
| 	string host; | ||||
| 	int port; | ||||
| 	string path; | ||||
| struct url | ||||
| { | ||||
|   string protocol; | ||||
|   string host; | ||||
|   int port; | ||||
|   string path; | ||||
| }; | ||||
|  | ||||
| struct rtcp_app { | ||||
| 	uint8_t subtype; | ||||
| 	uint8_t pt; | ||||
| 	uint16_t len; | ||||
| 	uint32_t ssrc; | ||||
| 	char name[4]; | ||||
| 	uint16_t identifier; | ||||
| 	uint16_t string_len; | ||||
| struct rtcp_app | ||||
| { | ||||
|   uint8_t subtype; | ||||
|   uint8_t pt; | ||||
|   uint16_t len; | ||||
|   uint32_t ssrc; | ||||
|   char name[4]; | ||||
|   uint16_t identifier; | ||||
|   uint16_t string_len; | ||||
| }; | ||||
|  | ||||
| static rtsp_client *rtsp = NULL; | ||||
| static rtsp_client* rtsp = nullptr; | ||||
|  | ||||
| static url parse_url(const std::string& str) { | ||||
| 	static const string prot_end = "://"; | ||||
| 	static const string host_end = "/"; | ||||
| 	url result; | ||||
| static url parse_url(const std::string& str) | ||||
| { | ||||
|   static const string prot_end = "://"; | ||||
|   static const string host_end = "/"; | ||||
|   url result; | ||||
|  | ||||
| 	string::const_iterator begin = str.begin(); | ||||
| 	string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); | ||||
| 	result.protocol.reserve(distance(begin, end)); | ||||
| 	transform(begin, end, back_inserter(result.protocol), ::tolower); | ||||
| 	advance(end, prot_end.size()); | ||||
| 	begin = end; | ||||
|   string::const_iterator begin = str.begin(); | ||||
|   string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); | ||||
|   result.protocol.reserve(distance(begin, end)); | ||||
|   transform(begin, end, back_inserter(result.protocol), ::tolower); | ||||
|   advance(end, prot_end.size()); | ||||
|   begin = end; | ||||
|  | ||||
| 	end = search(begin, str.end(), host_end.begin(), host_end.end()); | ||||
| 	result.host.reserve(distance(begin, end)); | ||||
| 	transform(begin, end, back_inserter(result.host), ::tolower); | ||||
| 	advance(end, host_end.size()); | ||||
| 	begin = end; | ||||
|   end = search(begin, str.end(), host_end.begin(), host_end.end()); | ||||
|   result.host.reserve(distance(begin, end)); | ||||
|   transform(begin, end, back_inserter(result.host), ::tolower); | ||||
|   advance(end, host_end.size()); | ||||
|   begin = end; | ||||
|  | ||||
| 	result.port = RTSP_DEFAULT_PORT; | ||||
|   result.port = RTSP_DEFAULT_PORT; | ||||
|  | ||||
| 	result.path.reserve(distance(begin, str.end())); | ||||
| 	transform(begin, str.end(), back_inserter(result.path), ::tolower); | ||||
|   result.path.reserve(distance(begin, str.end())); | ||||
|   transform(begin, str.end(), back_inserter(result.path), ::tolower); | ||||
|  | ||||
| 	return result; | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| void split_string(const string& s, char delim, vector<string>& elems) { | ||||
| 	stringstream ss; | ||||
| 	ss.str(s); | ||||
| void split_string(const string& s, char delim, vector<string>& elems) | ||||
| { | ||||
|   stringstream ss; | ||||
|   ss.str(s); | ||||
|  | ||||
| 	string item; | ||||
| 	while(getline(ss, item, delim)) { | ||||
| 		elems.push_back(item); | ||||
| 	} | ||||
|   string item; | ||||
|   while (getline(ss, item, delim)) | ||||
|   { | ||||
|     elems.push_back(item); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static int tcp_sock_read_line(string &line) { | ||||
| 	static string buf; | ||||
| static int tcp_sock_read_line(string& line) | ||||
| { | ||||
|   static string buf; | ||||
|  | ||||
| 	while(true) { | ||||
| 		string::size_type pos = buf.find("\r\n"); | ||||
| 		if(pos != string::npos) { | ||||
| 			line = buf.substr(0, pos); | ||||
| 			buf.erase(0, pos + 2); | ||||
| 			return 0; | ||||
| 		} | ||||
|   while (true) | ||||
|   { | ||||
|     string::size_type pos = buf.find("\r\n"); | ||||
|     if (pos != string::npos) | ||||
|     { | ||||
|       line = buf.substr(0, pos); | ||||
|       buf.erase(0, pos + 2); | ||||
|       return 0; | ||||
|     } | ||||
|  | ||||
| 		char tmp_buf[2048]; | ||||
| 		int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); | ||||
| 		if(size <= 0) { | ||||
| 			return 1; | ||||
| 		} | ||||
|     char tmp_buf[2048]; | ||||
|     int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); | ||||
|     if (size <= 0) | ||||
|     { | ||||
|       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) | ||||
| { | ||||
| 	stringstream res; | ||||
| 	res << u.protocol << "://" << u.host; | ||||
| 	if (u.port > 0) | ||||
| 		res << ":" << u.port; | ||||
| 	res << "/" << u.path; | ||||
|   stringstream res; | ||||
|   res << u.protocol << "://" << u.host; | ||||
|   if (u.port > 0) | ||||
|     res << ":" << u.port; | ||||
|   res << "/" << u.path; | ||||
|  | ||||
| 	return res.str(); | ||||
|   return res.str(); | ||||
| } | ||||
|  | ||||
| static void parse_session(char *request_line, char *session, unsigned max, int *timeout) { | ||||
| 	char *state; | ||||
| 	char *tok; | ||||
|  | ||||
| 	tok = strtok_r(request_line, ";", &state); | ||||
| 	if (tok == NULL) | ||||
| 		return; | ||||
| 	strncpy(session, tok, min(strlen(tok), (size_t)(max - 1))); | ||||
|  | ||||
| 	while ((tok = strtok_r(NULL, ";", &state)) != NULL) { | ||||
| 		if (strncmp(tok, "timeout=", 8) == 0) { | ||||
| 			*timeout = atoi(tok + 8); | ||||
| 			if (*timeout > 5) | ||||
| 				*timeout -= KEEPALIVE_MARGIN; | ||||
| 			else if (*timeout > 0) | ||||
| 				*timeout = 1; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int parse_port(char *str, uint16_t *port) | ||||
| static void parse_session(char* request_line, char* session, unsigned max, int* timeout) | ||||
| { | ||||
| 	int p = atoi(str); | ||||
| 	if (p < 0 || p > UINT16_MAX) | ||||
| 		return -1; | ||||
|   char* state; | ||||
|   char* tok; | ||||
|  | ||||
| 	*port = p; | ||||
|   tok = strtok_r(request_line, ";", &state); | ||||
|   if (tok == nullptr) | ||||
|     return; | ||||
|   strncpy(session, tok, min(strlen(tok), (size_t)(max - 1))); | ||||
|  | ||||
| 	return 0; | ||||
|   while ((tok = strtok_r(nullptr, ";", &state)) != nullptr) | ||||
|   { | ||||
|     if (strncmp(tok, "timeout=", 8) == 0) | ||||
|     { | ||||
|       *timeout = atoi(tok + 8); | ||||
|       if (*timeout > 5) | ||||
|         *timeout -= KEEPALIVE_MARGIN; | ||||
|       else if (*timeout > 0) | ||||
|         *timeout = 1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| static int parse_transport(char *request_line) { | ||||
| 	char *state; | ||||
| 	char *tok; | ||||
| 	int err; | ||||
| static int parse_port(char* str, uint16_t* port) | ||||
| { | ||||
|   int p = atoi(str); | ||||
|   if (p < 0 || p > UINT16_MAX) | ||||
|     return -1; | ||||
|  | ||||
| 	tok = strtok_r(request_line, ";", &state); | ||||
| 	if (tok == NULL || strncmp(tok, "RTP/AVP", 7) != 0) | ||||
| 		return -1; | ||||
|   *port = p; | ||||
|  | ||||
| 	tok = strtok_r(NULL, ";", &state); | ||||
| 	if (tok == NULL || strncmp(tok, "multicast", 9) != 0) | ||||
| 		return 0; | ||||
|  | ||||
| 	while ((tok = strtok_r(NULL, ";", &state)) != NULL) { | ||||
| 		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) { | ||||
| 			char port[6]; | ||||
| 			char *end; | ||||
|  | ||||
| 			memset(port, 0x00, 6); | ||||
| 			strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5)); | ||||
| 			if ((end = strstr(port, "-")) != NULL) | ||||
| 				*end = '\0'; | ||||
| 			err = parse_port(port, &rtsp->udp_port); | ||||
| 			if (err) | ||||
| 				return err; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| #define skip_whitespace(x) while(*x == ' ') x++ | ||||
| static enum rtsp_result rtsp_handle() { | ||||
| 	uint8_t buffer[512]; | ||||
| 	int rtsp_result = 0; | ||||
| 	bool have_header = false; | ||||
| 	size_t content_length = 0; | ||||
| 	size_t read = 0; | ||||
| 	char *in, *val; | ||||
| 	string in_str; | ||||
| static int parse_transport(char* request_line) | ||||
| { | ||||
|   char* state; | ||||
|   char* tok; | ||||
|   int err; | ||||
|  | ||||
| 	/* Parse header */ | ||||
| 	while (!have_header) { | ||||
| 		if (tcp_sock_read_line(in_str) < 0) | ||||
| 			break; | ||||
| 		in = const_cast<char *>(in_str.c_str()); | ||||
|   tok = strtok_r(request_line, ";", &state); | ||||
|   if (tok == nullptr || strncmp(tok, "RTP/AVP", 7) != 0) | ||||
|     return -1; | ||||
|  | ||||
| 		if (strncmp(in, "RTSP/1.0 ", 9) == 0) { | ||||
| 			rtsp_result = atoi(in + 9); | ||||
| 		} else if (strncmp(in, "Content-Base:", 13) == 0) { | ||||
| 			free(rtsp->content_base); | ||||
|   tok = strtok_r(nullptr, ";", &state); | ||||
|   if (tok == nullptr || strncmp(tok, "multicast", 9) != 0) | ||||
|     return 0; | ||||
|  | ||||
| 			val = in + 13; | ||||
| 			skip_whitespace(val); | ||||
|   while ((tok = strtok_r(nullptr, ";", &state)) != nullptr) | ||||
|   { | ||||
|     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) | ||||
|     { | ||||
|       char port[6]; | ||||
|       char* end; | ||||
|  | ||||
| 			rtsp->content_base = strdup(val); | ||||
| 		} else if (strncmp(in, "Content-Length:", 15) == 0) { | ||||
| 			val = in + 16; | ||||
| 			skip_whitespace(val); | ||||
|       memset(port, 0x00, 6); | ||||
|       strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5)); | ||||
|       if ((end = strstr(port, "-")) != nullptr) | ||||
|         *end = '\0'; | ||||
|       err = parse_port(port, &rtsp->udp_port); | ||||
|       if (err) | ||||
|         return err; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| 			content_length = atoi(val); | ||||
| 		} else if (strncmp("Session:", in, 8) == 0) { | ||||
| 			val = in + 8; | ||||
| 			skip_whitespace(val); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| 			parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval); | ||||
| 		} else if (strncmp("Transport:", in, 10) == 0) { | ||||
| 			val = in + 10; | ||||
| 			skip_whitespace(val); | ||||
| #define skip_whitespace(x) \ | ||||
|   while (*x == ' ') \ | ||||
|   x++ | ||||
| static enum rtsp_result rtsp_handle() | ||||
| { | ||||
|   uint8_t buffer[512]; | ||||
|   int rtsp_result = 0; | ||||
|   bool have_header = false; | ||||
|   size_t content_length = 0; | ||||
|   size_t read = 0; | ||||
|   char *in, *val; | ||||
|   string in_str; | ||||
|  | ||||
| 			if (parse_transport(val) != 0) { | ||||
| 				rtsp_result = -1; | ||||
| 				break; | ||||
| 			} | ||||
| 		} else if (strncmp("com.ses.streamID:", in, 17) == 0) { | ||||
| 			val = in + 17; | ||||
| 			skip_whitespace(val); | ||||
|   /* Parse header */ | ||||
|   while (!have_header) | ||||
|   { | ||||
|     if (tcp_sock_read_line(in_str) < 0) | ||||
|       break; | ||||
|     in = const_cast<char*>(in_str.c_str()); | ||||
|  | ||||
| 			rtsp->stream_id = atoi(val); | ||||
| 		} else if (in[0] == '\0') { | ||||
| 			have_header = true; | ||||
| 		} | ||||
| 	} | ||||
|     if (strncmp(in, "RTSP/1.0 ", 9) == 0) | ||||
|     { | ||||
|       rtsp_result = atoi(in + 9); | ||||
|     } | ||||
|     else if (strncmp(in, "Content-Base:", 13) == 0) | ||||
|     { | ||||
|       free(rtsp->content_base); | ||||
|  | ||||
| 	/* Discard further content */ | ||||
| 	while (content_length > 0 && | ||||
| 			(read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer), min(sizeof(buffer), content_length)))) | ||||
| 		content_length -= read; | ||||
|       val = in + 13; | ||||
|       skip_whitespace(val); | ||||
|  | ||||
| 	return (enum rtsp_result)rtsp_result; | ||||
|       rtsp->content_base = strdup(val); | ||||
|     } | ||||
|     else if (strncmp(in, "Content-Length:", 15) == 0) | ||||
|     { | ||||
|       val = in + 16; | ||||
|       skip_whitespace(val); | ||||
|  | ||||
|       content_length = atoi(val); | ||||
|     } | ||||
|     else if (strncmp("Session:", in, 8) == 0) | ||||
|     { | ||||
|       val = in + 8; | ||||
|       skip_whitespace(val); | ||||
|  | ||||
|       parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval); | ||||
|     } | ||||
|     else if (strncmp("Transport:", in, 10) == 0) | ||||
|     { | ||||
|       val = in + 10; | ||||
|       skip_whitespace(val); | ||||
|  | ||||
|       if (parse_transport(val) != 0) | ||||
|       { | ||||
|         rtsp_result = -1; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     else if (strncmp("com.ses.streamID:", in, 17) == 0) | ||||
|     { | ||||
|       val = in + 17; | ||||
|       skip_whitespace(val); | ||||
|  | ||||
|       rtsp->stream_id = atoi(val); | ||||
|     } | ||||
|     else if (in[0] == '\0') | ||||
|     { | ||||
|       have_header = true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* Discard further content */ | ||||
|   while (content_length > 0 && (read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer), | ||||
|                                                               min(sizeof(buffer), content_length)))) | ||||
|     content_length -= read; | ||||
|  | ||||
|   return (enum rtsp_result)rtsp_result; | ||||
| } | ||||
|  | ||||
| bool rtsp_open(const string& name, const string& url_str) | ||||
| { | ||||
| 	string setup_url_str; | ||||
| 	const char *psz_setup_url; | ||||
| 	stringstream setup_ss; | ||||
| 	stringstream play_ss; | ||||
| 	url setup_url; | ||||
|   string setup_url_str; | ||||
|   const char* psz_setup_url; | ||||
|   stringstream setup_ss; | ||||
|   stringstream play_ss; | ||||
|   url setup_url; | ||||
|  | ||||
| 	rtsp_close(); | ||||
| 	rtsp = new rtsp_client(); | ||||
| 	if (rtsp == NULL) | ||||
| 		return false; | ||||
|   rtsp_close(); | ||||
|   rtsp = new rtsp_client(); | ||||
|   if (rtsp == nullptr) | ||||
|     return false; | ||||
|  | ||||
| 	rtsp->name = name; | ||||
| 	rtsp->level = 0; | ||||
| 	rtsp->quality = 0; | ||||
|   rtsp->name = name; | ||||
|   rtsp->level = 0; | ||||
|   rtsp->quality = 0; | ||||
|  | ||||
| 	libKodi->Log(LOG_DEBUG, "try to open '%s'", url_str.c_str()); | ||||
|   kodi::Log(ADDON_LOG_DEBUG, "try to open '%s'", url_str.c_str()); | ||||
|  | ||||
| 	url dst = parse_url(url_str); | ||||
| 	libKodi->Log(LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); | ||||
|   url dst = parse_url(url_str); | ||||
|   kodi::Log(ADDON_LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); | ||||
|  | ||||
| 	if(!rtsp->tcp_sock.connect(dst.host, dst.port)) { | ||||
| 		libKodi->Log(LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(), dst.port); | ||||
| 		goto error; | ||||
| 	} | ||||
|   if (!rtsp->tcp_sock.connect(dst.host, dst.port)) | ||||
|   { | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(), | ||||
|               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) { | ||||
| 		rtsp->content_base = NULL; | ||||
| 		goto error; | ||||
| 	} | ||||
|   if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), dst.port) < 0) | ||||
|   { | ||||
|     rtsp->content_base = nullptr; | ||||
|     goto error; | ||||
|   } | ||||
|  | ||||
| 	rtsp->last_seq_nr = 0; | ||||
| 	rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN); | ||||
|   rtsp->last_seq_nr = 0; | ||||
|   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 | ||||
| 	if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) { | ||||
| 		setup_url.protocol = "rtsp"; | ||||
| 	} | ||||
|   // reverse the satip protocol trick, as SAT>IP believes to be RTSP | ||||
|   if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) | ||||
|   { | ||||
|     setup_url.protocol = "rtsp"; | ||||
|   } | ||||
|  | ||||
| 	setup_url_str = compose_url(setup_url); | ||||
| 	psz_setup_url = setup_url_str.c_str(); | ||||
|   setup_url_str = compose_url(setup_url); | ||||
|   psz_setup_url = setup_url_str.c_str(); | ||||
|  | ||||
| 	// TODO: Find available port | ||||
| 	rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | ||||
| 	rtsp->udp_port = 6785; | ||||
| 	if(!rtsp->udp_sock.bind(rtsp->udp_port)) { | ||||
| 		goto error; | ||||
| 	} | ||||
|   // TODO: Find available port | ||||
|   rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | ||||
|   rtsp->udp_port = 6785; | ||||
|   if (!rtsp->udp_sock.bind(rtsp->udp_port)) | ||||
|   { | ||||
|     goto error; | ||||
|   } | ||||
|  | ||||
| 	setup_ss << "SETUP " << setup_url_str<< " RTSP/1.0\r\n"; | ||||
| 	setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||
| 	setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-" << (rtsp->udp_port + 1) << "\r\n\r\n"; | ||||
| 	rtsp->tcp_sock.send(setup_ss.str()); | ||||
|   setup_ss << "SETUP " << setup_url_str << " RTSP/1.0\r\n"; | ||||
|   setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||
|   setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-" | ||||
|            << (rtsp->udp_port + 1) << "\r\n\r\n"; | ||||
|   rtsp->tcp_sock.send(setup_ss.str()); | ||||
|  | ||||
| 	if (rtsp_handle() != RTSP_RESULT_OK) { | ||||
| 		libKodi->Log(LOG_ERROR, "Failed to setup RTSP session"); | ||||
| 		goto error; | ||||
| 	} | ||||
|   if (rtsp_handle() != RTSP_RESULT_OK) | ||||
|   { | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Failed to setup RTSP session"); | ||||
|     goto error; | ||||
|   } | ||||
|  | ||||
| 	if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0) { | ||||
| 		rtsp->control = NULL; | ||||
| 		goto error; | ||||
| 	} | ||||
|   if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0) | ||||
|   { | ||||
|     rtsp->control = nullptr; | ||||
|     goto error; | ||||
|   } | ||||
|  | ||||
| 	play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; | ||||
| 	play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||
| 	play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | ||||
| 	rtsp->tcp_sock.send(play_ss.str()); | ||||
|   play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; | ||||
|   play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||
|   play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | ||||
|   rtsp->tcp_sock.send(play_ss.str()); | ||||
|  | ||||
| 	if (rtsp_handle() != RTSP_RESULT_OK) { | ||||
| 		libKodi->Log(LOG_ERROR, "Failed to play RTSP session"); | ||||
| 		goto error; | ||||
| 	} | ||||
|   if (rtsp_handle() != RTSP_RESULT_OK) | ||||
|   { | ||||
|     kodi::Log(ADDON_LOG_ERROR, "Failed to play RTSP session"); | ||||
|     goto error; | ||||
|   } | ||||
|  | ||||
| 	rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | ||||
| 	if(!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) { | ||||
| 		goto error; | ||||
| 	} | ||||
| 	if(!rtsp->rtcp_sock.set_non_blocking(true)) { | ||||
| 		goto error; | ||||
| 	} | ||||
|   rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | ||||
|   if (!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) | ||||
|   { | ||||
|     goto error; | ||||
|   } | ||||
|   if (!rtsp->rtcp_sock.set_non_blocking(true)) | ||||
|   { | ||||
|     goto error; | ||||
|   } | ||||
|  | ||||
| 	return true; | ||||
|   return true; | ||||
|  | ||||
| error: | ||||
| 	rtsp_close(); | ||||
| 	return false; | ||||
|   rtsp_close(); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| static void parse_rtcp(const char *buf, int size) { | ||||
| 	int offset = 0; | ||||
| 	while(size > 4) { | ||||
| 		const rtcp_app *app = reinterpret_cast<const rtcp_app *>(buf + offset); | ||||
| 		uint16_t len = 4 * (ntohs(app->len) + 1); | ||||
| static void parse_rtcp(const char* buf, int size) | ||||
| { | ||||
|   int offset = 0; | ||||
|   while (size > 4) | ||||
|   { | ||||
|     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)) { | ||||
| 			size -= len; | ||||
| 			offset += len; | ||||
| 			continue; | ||||
| 		} | ||||
|     if ((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0)) | ||||
|     { | ||||
|       size -= len; | ||||
|       offset += len; | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
| 		uint16_t string_len = ntohs(app->string_len); | ||||
| 		string app_data(&buf[offset + sizeof(rtcp_app)], string_len); | ||||
|     uint16_t string_len = ntohs(app->string_len); | ||||
|     string app_data(&buf[offset + sizeof(rtcp_app)], string_len); | ||||
|  | ||||
| 		vector<string> elems; | ||||
| 		split_string(app_data, ';', elems); | ||||
| 		if(elems.size() != 4) { | ||||
| 			return; | ||||
| 		} | ||||
|     vector<string> elems; | ||||
|     split_string(app_data, ';', elems); | ||||
|     if (elems.size() != 4) | ||||
|     { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| 		vector<string> tuner; | ||||
| 		split_string(elems[2], ',', tuner); | ||||
| 		if(tuner.size() < 4) { | ||||
| 			return; | ||||
| 		} | ||||
|     vector<string> tuner; | ||||
|     split_string(elems[2], ',', tuner); | ||||
|     if (tuner.size() < 4) | ||||
|     { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| 		rtsp->level = atoi(tuner[1].c_str()); | ||||
| 		rtsp->quality = atoi(tuner[3].c_str()); | ||||
|     rtsp->level = atoi(tuner[1].c_str()); | ||||
|     rtsp->quality = atoi(tuner[3].c_str()); | ||||
|  | ||||
| 		return; | ||||
| 	} | ||||
|     return; | ||||
|   } | ||||
| } | ||||
|  | ||||
| int rtsp_read(void *buf, unsigned buf_size) { | ||||
| 	sockaddr addr; | ||||
| 	socklen_t addr_len = sizeof(addr); | ||||
| 	int ret = rtsp->udp_sock.recvfrom((char *)buf, buf_size, (sockaddr *)&addr, &addr_len); | ||||
| int rtsp_read(void* buf, unsigned buf_size) | ||||
| { | ||||
|   sockaddr addr; | ||||
|   socklen_t addr_len = sizeof(addr); | ||||
|   int ret = rtsp->udp_sock.recvfrom((char*)buf, buf_size, (sockaddr*)&addr, &addr_len); | ||||
|  | ||||
| 	char rtcp_buf[RTCP_BUFFER_SIZE]; | ||||
| 	int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr *)&addr, &addr_len); | ||||
| 	parse_rtcp(rtcp_buf, rtcp_len); | ||||
|   char rtcp_buf[RTCP_BUFFER_SIZE]; | ||||
|   int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr*)&addr, &addr_len); | ||||
|   parse_rtcp(rtcp_buf, rtcp_len); | ||||
|  | ||||
| 	// TODO: check ip | ||||
|   // TODO: check ip | ||||
|  | ||||
| 	return ret; | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static void rtsp_teardown() { | ||||
| 	if(!rtsp->tcp_sock.is_valid()) { | ||||
| 		return; | ||||
| 	} | ||||
| static void rtsp_teardown() | ||||
| { | ||||
|   if (!rtsp->tcp_sock.is_valid()) | ||||
|   { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
| 	if (rtsp->session_id[0] > 0) { | ||||
| 		char *msg; | ||||
| 		int len; | ||||
| 		stringstream ss; | ||||
|   if (rtsp->session_id[0] > 0) | ||||
|   { | ||||
|     char* msg; | ||||
|     int len; | ||||
|     stringstream ss; | ||||
|  | ||||
| 		rtsp->udp_sock.close(); | ||||
|     rtsp->udp_sock.close(); | ||||
|  | ||||
| 		ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; | ||||
| 		ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||
| 		ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | ||||
| 		rtsp->tcp_sock.send(ss.str()); | ||||
|     ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; | ||||
|     ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||
|     ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | ||||
|     rtsp->tcp_sock.send(ss.str()); | ||||
|  | ||||
| 		if (rtsp_handle() != RTSP_RESULT_OK) { | ||||
| 			libKodi->Log(LOG_ERROR, "Failed to teardown RTSP session"); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
|     if (rtsp_handle() != RTSP_RESULT_OK) | ||||
|     { | ||||
|       kodi::Log(ADDON_LOG_ERROR, "Failed to teardown RTSP session"); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void rtsp_close() | ||||
| { | ||||
| 	if(rtsp) { | ||||
| 		rtsp_teardown(); | ||||
| 		rtsp->tcp_sock.close(); | ||||
| 		rtsp->udp_sock.close(); | ||||
| 		rtsp->rtcp_sock.close(); | ||||
| 		delete rtsp; | ||||
| 		rtsp = NULL; | ||||
| 	} | ||||
|   if (rtsp) | ||||
|   { | ||||
|     rtsp_teardown(); | ||||
|     rtsp->tcp_sock.close(); | ||||
|     rtsp->udp_sock.close(); | ||||
|     rtsp->rtcp_sock.close(); | ||||
|     delete rtsp; | ||||
|     rtsp = nullptr; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void rtsp_fill_signal_status(PVR_SIGNAL_STATUS& signal_status) { | ||||
| 	if(rtsp) { | ||||
| 		strncpy(signal_status.strServiceName, rtsp->name.c_str(), PVR_ADDON_NAME_STRING_LENGTH - 1); | ||||
| 		signal_status.iSNR = 0x1111 * rtsp->quality; | ||||
| 		signal_status.iSignal = 0x101 * rtsp->level; | ||||
| 	} | ||||
| void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status) | ||||
| { | ||||
|   if (rtsp) | ||||
|   { | ||||
|     signal_status.SetAdapterName(rtsp->name); | ||||
|     signal_status.SetSNR(0x1111 * rtsp->quality); | ||||
|     signal_status.SetSignal(0x101 * rtsp->level); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,12 +1,17 @@ | ||||
| #ifndef _RTSP_CLIENT_HPP_ | ||||
| #define _RTSP_CLIENT_HPP_ | ||||
| /* | ||||
|  *  Copyright (C) 2005-2020 Team Kodi | ||||
|  *  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 <kodi/xbmc_pvr_types.h> | ||||
|  | ||||
| bool rtsp_open(const std::string& name, const std::string& url_str); | ||||
| void rtsp_close(); | ||||
| int rtsp_read(void *buf, unsigned buf_size); | ||||
| void rtsp_fill_signal_status(PVR_SIGNAL_STATUS& signal_status); | ||||
|  | ||||
| #endif | ||||
| int rtsp_read(void* buf, unsigned buf_size); | ||||
| void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user