mirror of
				https://github.com/DigitalDevices/pvr.octonet.git
				synced 2025-03-01 10:53:09 +00:00 
			
		
		
		
	Compare commits
	
		
			49 Commits
		
	
	
		
			Leia
			...
			3.0.0-Matr
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | ||
|  | 8032f085fd | ||
|  | 0f6cd141c4 | ||
|  | d5ec59e00e | ||
|  | e2a73af36e | ||
|  | a55552e768 | ||
|  | 14d3e9d225 | ||
|  | cf9ee98727 | ||
|  | 9d98f2bba4 | ||
|  | ca692f14be | ||
|  | 69da8dbd0c | ||
|  | 6d4f3847c7 | ||
|  | 38e25e1f1c | ||
|  | 472dc2beae | ||
|  | ec08c20540 | ||
|  | 63ef363522 | ||
|  | c91061f84b | ||
|  | 414e756912 | ||
|  | 9de3166203 | 
							
								
								
									
										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 | # build artifacts | ||||||
| pvr.octonet/addon.xml | 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 | ||||||
|   | |||||||
| @@ -22,9 +22,7 @@ matrix: | |||||||
|       sudo: required |       sudo: required | ||||||
|       compiler: clang |       compiler: clang | ||||||
|     - os: osx |     - os: osx | ||||||
|       osx_image: xcode9 |       osx_image: xcode10.2 | ||||||
|     - os: osx |  | ||||||
|       osx_image: xcode9.4 |  | ||||||
|  |  | ||||||
| # | # | ||||||
| # The addon source is automatically checked out in $TRAVIS_BUILD_DIR, | # The addon source is automatically checked out in $TRAVIS_BUILD_DIR, | ||||||
| @@ -32,7 +30,7 @@ matrix: | |||||||
| # | # | ||||||
| before_script: | before_script: | ||||||
|   - cd $TRAVIS_BUILD_DIR/.. |   - cd $TRAVIS_BUILD_DIR/.. | ||||||
|   - git clone --branch Leia --depth=1 https://github.com/xbmc/xbmc.git |   - git clone --branch master --depth=1 https://github.com/xbmc/xbmc.git | ||||||
|   - cd ${app_id} && mkdir build && cd build |   - cd ${app_id} && mkdir build && cd build | ||||||
|   - mkdir -p definition/${app_id} |   - mkdir -p definition/${app_id} | ||||||
|   - echo ${app_id} $TRAVIS_BUILD_DIR $TRAVIS_COMMIT > definition/${app_id}/${app_id}.txt |   - echo ${app_id} $TRAVIS_BUILD_DIR $TRAVIS_COMMIT > definition/${app_id}/${app_id}.txt | ||||||
|   | |||||||
| @@ -4,39 +4,34 @@ project(pvr.octonet) | |||||||
| list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) | list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) | ||||||
|  |  | ||||||
| find_package(Kodi REQUIRED) | find_package(Kodi REQUIRED) | ||||||
| find_package(kodiplatform REQUIRED) |  | ||||||
| find_package(p8-platform REQUIRED) |  | ||||||
| find_package(JsonCpp REQUIRED) | find_package(JsonCpp REQUIRED) | ||||||
|  |  | ||||||
| include_directories( | include_directories(${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways | ||||||
| 	${kodiplatform_INCLUDE_DIRS} |                     ${JSONCPP_INCLUDE_DIRS}) | ||||||
| 	${p8-platform_INCLUDE_DIRS} |  | ||||||
| 	${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways |  | ||||||
| 	${JSONCPP_INCLUDE_DIRS}) |  | ||||||
|  |  | ||||||
| set(DEPLIBS | set(DEPLIBS ${JSONCPP_LIBRARIES}) | ||||||
| 	${p8-platform_LIBRARIES} |  | ||||||
| 	${JSONCPP_LIBRARIES}) |  | ||||||
|  |  | ||||||
| set(OCTONET_SOURCES | set(OCTONET_SOURCES src/addon.cpp | ||||||
| 	src/OctonetData.cpp |                     src/OctonetData.cpp | ||||||
| 	src/client.cpp |                     src/Socket.cpp | ||||||
| 	src/Socket.cpp |                     src/rtsp_client.cpp) | ||||||
| 	src/rtsp_client.cpp) |  | ||||||
|  |  | ||||||
| set(OCTONET_HEADERS | set(OCTONET_HEADERS src/addon.h | ||||||
| 	src/client.h |                     src/OctonetData.h | ||||||
| 	src/OctonetData.h |                     src/Socket.h | ||||||
| 	src/Socket.h) |                     src/rtsp_client.hpp) | ||||||
|  |  | ||||||
|  | addon_version(pvr.octonet OCTONET) | ||||||
|  | add_definitions(-DOCTONET_VERSION=${OCTONET_VERSION}) | ||||||
|  |  | ||||||
| build_addon(pvr.octonet OCTONET DEPLIBS) | build_addon(pvr.octonet OCTONET DEPLIBS) | ||||||
|  |  | ||||||
| if(WIN32) | if(WIN32) | ||||||
| 	if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore) |   if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore) | ||||||
| 		target_link_libraries(pvr.octonet wsock32 ws2_32) |     target_link_libraries(pvr.octonet wsock32 ws2_32) | ||||||
| 	else() |   else() | ||||||
| 		target_link_libraries(pvr.octonet ws2_32) |     target_link_libraries(pvr.octonet ws2_32) | ||||||
| 	endif() |   endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| include(CPack) | include(CPack) | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1 @@ | |||||||
| buildPlugin(version: "Leia") | buildPlugin(version: "Matrix") | ||||||
|   | |||||||
							
								
								
									
										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. | ||||||
| @@ -20,7 +20,7 @@ $ git clone https://github.com/DigitalDevices/pvr.octonet.git | |||||||
| Clone the Kodi repository: | Clone the Kodi repository: | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| $ git clone --branch Leia https://github.com/xbmc/xbmc.git | $ git clone --branch master https://github.com/xbmc/xbmc.git | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -29,7 +29,7 @@ artifacts: | |||||||
| build_script: | build_script: | ||||||
|   - cd .. |   - cd .. | ||||||
|   - set ROOT=%cd% |   - set ROOT=%cd% | ||||||
|   - git clone --branch Leia --depth=1 https://github.com/xbmc/xbmc.git |   - git clone --branch master --depth=1 https://github.com/xbmc/xbmc.git | ||||||
|   - mkdir xbmc\cmake\addons\addons\pvr.octonet |   - mkdir xbmc\cmake\addons\addons\pvr.octonet | ||||||
|   - echo pvr.octonet https://github.com/DigitalDevices/pvr.octonet master > xbmc\cmake\addons\addons\pvr.octonet\pvr.octonet.txt |   - echo pvr.octonet https://github.com/DigitalDevices/pvr.octonet master > xbmc\cmake\addons\addons\pvr.octonet\pvr.octonet.txt | ||||||
|   - cd %ADDON% |   - cd %ADDON% | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -2,8 +2,8 @@ Source: kodi-pvr-octonet | |||||||
| Priority: extra | Priority: extra | ||||||
| Maintainer: Julian Scheel <julian@jusst.de> | Maintainer: Julian Scheel <julian@jusst.de> | ||||||
| Build-Depends: debhelper (>= 9.0.0), cmake, libjsoncpp-dev, | Build-Depends: debhelper (>= 9.0.0), cmake, libjsoncpp-dev, | ||||||
|                libkodiplatform-dev (>= 16.0.0), kodi-addon-dev |                libp8-platform-dev, kodi-addon-dev | ||||||
| Standards-Version: 3.9.4 | Standards-Version: 4.1.2 | ||||||
| Section: libs | Section: libs | ||||||
| Homepage: https://github.com/DigitalDevices/pvr.octonet | Homepage: https://github.com/DigitalDevices/pvr.octonet | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -5,7 +5,7 @@ Source: https://github.com/DigitalDevices/pvr.octonet | |||||||
| Files: * | Files: * | ||||||
| Copyright: 2015-2016 Julian Scheel | Copyright: 2015-2016 Julian Scheel | ||||||
|            2015-2016 jusst technologies GmbH |            2015-2016 jusst technologies GmbH | ||||||
|            2005-2013 Team XBMC |            2005-2020 Team Kodi | ||||||
| License: GPL-2+ | License: GPL-2+ | ||||||
|  This package is free software; you can redistribute it and/or modify |  This package is free software; you can redistribute it and/or modify | ||||||
|  it under the terms of the GNU General Public License as published by |  it under the terms of the GNU General Public License as published by | ||||||
| @@ -24,7 +24,8 @@ License: GPL-2+ | |||||||
|  Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". |  Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". | ||||||
|  |  | ||||||
| Files: debian/* | Files: debian/* | ||||||
| Copyright: 2016 Julian Scheel <julian@jusst.de> | Copyright: 2020 Team Kodi | ||||||
|  |            2016 Julian Scheel <julian@jusst.de> | ||||||
|            2015 Jean-Luc Barriere |            2015 Jean-Luc Barriere | ||||||
|            2015 wsnipex <wsnipex@a1.net> |            2015 wsnipex <wsnipex@a1.net> | ||||||
| License: GPL-2+ | License: GPL-2+ | ||||||
|   | |||||||
| @@ -1,16 +1,18 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <addon | <addon | ||||||
| 	id="pvr.octonet" | 	id="pvr.octonet" | ||||||
| 	version="0.7.1" | 	version="3.0.0" | ||||||
| 	name="Digital Devices Octopus NET Client" | 	name="Digital Devices Octopus NET Client" | ||||||
| 	provider-name="digitaldevices"> | 	provider-name="digitaldevices"> | ||||||
| 	<requires>@ADDON_DEPENDS@</requires> | 	<requires>@ADDON_DEPENDS@</requires> | ||||||
| 	<extension | 	<extension | ||||||
| 		point="xbmc.pvrclient" | 		point="kodi.pvrclient" | ||||||
| 		library_@PLATFORM@="@LIBRARY_FILENAME@"/> | 		library_@PLATFORM@="@LIBRARY_FILENAME@"/> | ||||||
| 	<extension point="xbmc.addon.metadata"> | 	<extension point="xbmc.addon.metadata"> | ||||||
| 		<summary lang="de_DE">Kodi PVR Addon für Digital Devices Octopus NET Streams</summary> | 		<summary lang="de_DE">Kodi PVR Addon für Digital Devices Octopus NET Streams</summary> | ||||||
| 		<summary lang="en_US">Kodi PVR Addon for Digital Devices Octopus NET Streams</summary> | 		<summary lang="en_US">Kodi PVR Addon for Digital Devices Octopus NET Streams</summary> | ||||||
| 		<platform>@PLATFORM@</platform> | 		<platform>@PLATFORM@</platform> | ||||||
|  | 		<license>GPL-2.0-or-later</license> | ||||||
|  | 		<source>https://github.com/DigitalDevices/pvr.octonet</source> | ||||||
| 	</extension> | 	</extension> | ||||||
| </addon> | </addon> | ||||||
|   | |||||||
| @@ -1,5 +1,18 @@ | |||||||
| <?xml version="1.0" encoding="utf-8" standalone="yes"?> | <?xml version="1.0" encoding="utf-8" standalone="yes"?> | ||||||
| <settings> | <settings version="1"> | ||||||
| 	<!-- Octonet Server Address --> |   <section id="pvr.octonet"> | ||||||
| 	<setting id="octonetAddress" type="text" label="30000" default="" /> |     <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> | </settings> | ||||||
|   | |||||||
| @@ -1,369 +1,458 @@ | |||||||
| /* | /* | ||||||
|  * Copyright (C) 2015 Julian Scheel <julian@jusst.de> |  *  Copyright (C) 2015 Julian Scheel <julian@jusst.de> | ||||||
|  * Copyright (C) 2015 jusst technologies GmbH |  *  Copyright (C) 2015 jusst technologies GmbH | ||||||
|  * Copyright (C) 2015 Digital Devices GmbH |  *  Copyright (C) 2015 Digital Devices GmbH | ||||||
|  * |  * | ||||||
|  * This program is free software; you can redistribute it and/or |  *  SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  * modify it under the terms of the GNU General Public License |  *  See LICENSE.md for more information. | ||||||
|  * 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 <sstream> | #include "OctonetData.h" | ||||||
| #include <string> |  | ||||||
|  | #include "rtsp_client.hpp" | ||||||
|  |  | ||||||
| #include <json/json.h> | #include <json/json.h> | ||||||
|  | #include <kodi/Filesystem.h> | ||||||
| #include "OctonetData.h" | #include <kodi/General.h> | ||||||
|  | #include <sstream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
| #ifdef __WINDOWS__ | #ifdef __WINDOWS__ | ||||||
| #define timegm _mkgmtime | #define timegm _mkgmtime | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| using namespace ADDON; | OctonetData::OctonetData(const std::string& octonetAddress, | ||||||
|  |                          KODI_HANDLE instance, | ||||||
| OctonetData::OctonetData() |                          const std::string& kodiVersion) | ||||||
|  |   : kodi::addon::CInstancePVRClient(instance, kodiVersion) | ||||||
| { | { | ||||||
| 	serverAddress = octonetAddress; |   m_serverAddress = octonetAddress; | ||||||
| 	channels.clear(); |   m_channels.clear(); | ||||||
| 	groups.clear(); |   m_groups.clear(); | ||||||
| 	lastEpgLoad = 0; |   m_lastEpgLoad = 0; | ||||||
|  |  | ||||||
| 	if (!loadChannelList()) |   if (!LoadChannelList()) | ||||||
| 		libKodi->QueueNotification(QUEUE_ERROR, libKodi->GetLocalizedString(30001), channels.size()); |     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) | 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; |   capabilities.SetSupportsTV(true); | ||||||
| 	int64_t nativeId = hash_fn(id); |   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; |   name = "Digital Devices Octopus NET Client"; | ||||||
| 	void *f = libKodi->OpenFile(("http://" + serverAddress + "/channellist.lua?select=json").c_str(), 0); |   return PVR_ERROR_NO_ERROR; | ||||||
| 	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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| OctonetChannel* OctonetData::findChannel(int64_t nativeId) | PVR_ERROR OctonetData::GetBackendVersion(std::string& version) | ||||||
| { | { | ||||||
| 	std::vector<OctonetChannel>::iterator it; |   version = STR(OCTONET_VERSION); | ||||||
| 	for (it = channels.begin(); it < channels.end(); ++it) { |   return PVR_ERROR_NO_ERROR; | ||||||
| 		if (it->nativeId == nativeId) |  | ||||||
| 			return &*it; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return NULL; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| time_t OctonetData::parseDateTime(std::string date) | PVR_ERROR OctonetData::GetConnectionString(std::string& connection) | ||||||
| { | { | ||||||
| 	struct tm timeinfo; |   connection = "connected"; // FIXME: translate? | ||||||
|  |   return PVR_ERROR_NO_ERROR; | ||||||
| 	memset(&timeinfo, 0, sizeof(timeinfo)); |  | ||||||
|  |  | ||||||
| 	if (date.length() > 8) { |  | ||||||
| 		sscanf(date.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", |  | ||||||
| 				&timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, |  | ||||||
| 				&timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); |  | ||||||
| 		timeinfo.tm_mon -= 1; |  | ||||||
| 		timeinfo.tm_year -= 1900; |  | ||||||
| 	} else { |  | ||||||
| 		sscanf(date.c_str(), "%02d:%02d:%02d", |  | ||||||
| 				&timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); |  | ||||||
| 		timeinfo.tm_year = 70; // unix timestamps start 1970 |  | ||||||
| 		timeinfo.tm_mday = 1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	timeinfo.tm_isdst = -1; |  | ||||||
|  |  | ||||||
| 	return timegm(&timeinfo); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool OctonetData::loadEPG(void) | PVR_ERROR OctonetData::GetBackendHostname(std::string& hostname) | ||||||
| { | { | ||||||
| 	/* Reload at most every 30 seconds */ |   hostname = m_serverAddress; | ||||||
| 	if (lastEpgLoad + 30 > time(NULL)) |   return PVR_ERROR_NO_ERROR; | ||||||
| 		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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| 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++) |   std::hash<std::string> hash_fn; | ||||||
| 	{ |   int64_t nativeId = hash_fn(id); | ||||||
| 		OctonetChannel &channel = channels.at(i); |  | ||||||
| 		if (channel.radio == bRadio) |  | ||||||
| 		{ |  | ||||||
| 			PVR_CHANNEL chan; |  | ||||||
| 			memset(&chan, 0, sizeof(PVR_CHANNEL)); |  | ||||||
|  |  | ||||||
| 			chan.iUniqueId = channel.id; |   return nativeId; | ||||||
| 			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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end) | bool OctonetData::LoadChannelList() | ||||||
| { | { | ||||||
| 	for (unsigned int i = 0; i < channels.size(); i++) |   std::string jsonContent; | ||||||
| 	{ |   kodi::vfs::CFile f; | ||||||
| 		OctonetChannel &chan = channels.at(i); |   if (!f.OpenFile("http://" + m_serverAddress + "/channellist.lua?select=json", 0)) | ||||||
| 		if (channel.iUniqueId != chan.id) |     return false; | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		if(chan.epg.empty()) { |   char buf[1024]; | ||||||
| 			loadEPG(); |   while (int read = f.Read(buf, 1024)) | ||||||
| 		} |     jsonContent.append(buf, read); | ||||||
|  |  | ||||||
| 		// FIXME: Check if reload is needed!? |   f.Close(); | ||||||
|  |  | ||||||
| 		std::vector<OctonetEpgEntry>::iterator it; |   Json::Value root; | ||||||
| 		time_t last_end = 0; |   Json::Reader reader; | ||||||
| 		for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { |  | ||||||
| 			if (it->end > last_end) |  | ||||||
| 				last_end = it->end; |  | ||||||
|  |  | ||||||
| 			if (it->end < start || it->start > end) { |   if (!reader.parse(jsonContent, root, false)) | ||||||
| 				continue; |     return false; | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			EPG_TAG entry; |   const Json::Value groupList = root["GroupList"]; | ||||||
| 			memset(&entry, 0, sizeof(EPG_TAG)); |   for (unsigned int i = 0; i < groupList.size(); i++) | ||||||
|  |   { | ||||||
|  |     const Json::Value channelList = groupList[i]["ChannelList"]; | ||||||
|  |     OctonetGroup group; | ||||||
|  |  | ||||||
| 			entry.iUniqueChannelId = chan.id; |     group.name = groupList[i]["Title"].asString(); | ||||||
| 			entry.iUniqueBroadcastId = it->id; |     group.radio = group.name.compare(0, 5, "Radio") ? false : true; | ||||||
| 			entry.strTitle = it->title.c_str(); |  | ||||||
| 			entry.strPlotOutline = it->subtitle.c_str(); |  | ||||||
| 			entry.startTime = it->start; |  | ||||||
| 			entry.endTime = it->end; |  | ||||||
|  |  | ||||||
| 			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) |       chan.name = channel["Title"].asString(); | ||||||
| 			loadEPG(); |       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) { |       chan.id = 1000 + m_channels.size(); | ||||||
| 			if (it->end < start || it->start > end) { |       group.members.push_back(m_channels.size()); | ||||||
| 				continue; |       m_channels.push_back(chan); | ||||||
| 			} |     } | ||||||
|  |     m_groups.push_back(group); | ||||||
|  |   } | ||||||
|  |  | ||||||
| 			EPG_TAG entry; |   return true; | ||||||
| 			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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| const std::string& OctonetData::getUrl(int id) const { | OctonetChannel* OctonetData::FindChannel(int64_t nativeId) | ||||||
| 	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) |  | ||||||
| { | { | ||||||
| 	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++) |   struct tm timeinfo; | ||||||
| 	{ |  | ||||||
| 		OctonetGroup &group = groups.at(i); |  | ||||||
| 		if (group.radio == bRadio) |  | ||||||
| 		{ |  | ||||||
| 			PVR_CHANNEL_GROUP g; |  | ||||||
| 			memset(&g, 0, sizeof(PVR_CHANNEL_GROUP)); |  | ||||||
|  |  | ||||||
| 			g.iPosition = 0; |   memset(&timeinfo, 0, sizeof(timeinfo)); | ||||||
| 			g.bIsRadio = group.radio; |  | ||||||
| 			strncpy(g.strGroupName, group.name.c_str(), strlen(group.name.c_str())); |  | ||||||
|  |  | ||||||
| 			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); |   /* Reload at most every 30 seconds */ | ||||||
| 	if (g == NULL) |   if (m_lastEpgLoad + 30 > time(nullptr)) | ||||||
| 		return PVR_ERROR_UNKNOWN; |     return false; | ||||||
|  |  | ||||||
| 	for (unsigned int i = 0; i < g->members.size(); i++) |   std::string jsonContent; | ||||||
| 	{ |   kodi::vfs::CFile f; | ||||||
| 		OctonetChannel &channel = channels.at(g->members[i]); |   if (!f.OpenFile("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip", 0)) | ||||||
| 		PVR_CHANNEL_GROUP_MEMBER m; |     return false; | ||||||
| 		memset(&m, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); |  | ||||||
|  |  | ||||||
| 		strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName)); |   char buf[1024]; | ||||||
| 		m.iChannelUniqueId = channel.id; |   while (int read = f.Read(buf, 1024)) | ||||||
| 		m.iChannelNumber = channel.id; |     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++) |   return; | ||||||
| 	{ | } | ||||||
| 		if (groups.at(i).name == name) |  | ||||||
| 			return &groups.at(i); | PVR_ERROR OctonetData::GetChannelsAmount(int& amount) | ||||||
| 	} | { | ||||||
|  |   amount = m_channels.size(); | ||||||
| 	return NULL; |   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 Julian Scheel <julian@jusst.de> | ||||||
|  * Copyright (C) 2015 jusst technologies GmbH |  *  Copyright (C) 2015 jusst technologies GmbH | ||||||
|  * Copyright (C) 2015 Digital Devices GmbH |  *  Copyright (C) 2015 Digital Devices GmbH | ||||||
|  * |  * | ||||||
|  * This program is free software; you can redistribute it and/or |  *  SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  * modify it under the terms of the GNU General Public License |  *  See LICENSE.md for more information. | ||||||
|  * 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 <vector> | #pragma once | ||||||
|  |  | ||||||
| #include "p8-platform/threads/threads.h" | #include <atomic> | ||||||
| #include "client.h" | #include <kodi/addon-instance/PVR.h> | ||||||
|  | #include <thread> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
| struct OctonetEpgEntry | struct OctonetEpgEntry | ||||||
| { | { | ||||||
| 	int64_t channelId; |   int64_t channelId; | ||||||
| 	time_t start; |   time_t start; | ||||||
| 	time_t end; |   time_t end; | ||||||
| 	int id; |   int id; | ||||||
| 	std::string title; |   std::string title; | ||||||
| 	std::string subtitle; |   std::string subtitle; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct OctonetChannel | struct OctonetChannel | ||||||
| { | { | ||||||
| 	int64_t nativeId; |   int64_t nativeId; | ||||||
| 	std::string name; |   std::string name; | ||||||
| 	std::string url; |   std::string url; | ||||||
| 	bool radio; |   bool radio; | ||||||
| 	int id; |   int id; | ||||||
|  |  | ||||||
| 	std::vector<OctonetEpgEntry> epg; |   std::vector<OctonetEpgEntry> epg; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct OctonetGroup | struct OctonetGroup | ||||||
| { | { | ||||||
| 	std::string name; |   std::string name; | ||||||
| 	bool radio; |   bool radio; | ||||||
| 	std::vector<int> members; |   std::vector<int> members; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class OctonetData : public P8PLATFORM::CThread | class ATTRIBUTE_HIDDEN OctonetData : public kodi::addon::CInstancePVRClient | ||||||
| { | { | ||||||
| 	public: | public: | ||||||
| 		OctonetData(void); |   OctonetData(const std::string& octonetAddress, | ||||||
| 		virtual ~OctonetData(void); |               KODI_HANDLE instance, | ||||||
|  |               const std::string& kodiVersion); | ||||||
|  |   ~OctonetData() override; | ||||||
|  |  | ||||||
| 		virtual int getChannelCount(void); |   PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override; | ||||||
| 		virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio); |   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); |   PVR_ERROR OnSystemSleep() override; | ||||||
| 		virtual PVR_ERROR getGroups(ADDON_HANDLE handle, bool bRadio); |   PVR_ERROR OnSystemWake() override; | ||||||
| 		virtual PVR_ERROR getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group); |  | ||||||
|  |  | ||||||
| 		virtual PVR_ERROR getEPG(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end); |   PVR_ERROR GetChannelsAmount(int& amount) override; | ||||||
| 		const std::string& getUrl(int id) const; |   PVR_ERROR GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results) override; | ||||||
| 		const std::string& getName(int id) const; |  | ||||||
|  |  | ||||||
| 	protected: |   PVR_ERROR GetChannelGroupsAmount(int& amount) override; | ||||||
| 		virtual bool loadChannelList(void); |   PVR_ERROR GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results) override; | ||||||
| 		virtual bool loadEPG(void); |   PVR_ERROR GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group, | ||||||
| 		virtual OctonetGroup* findGroup(const std::string &name); |                                    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); |   bool OpenLiveStream(const kodi::addon::PVRChannel& channelinfo) override; | ||||||
| 		time_t parseDateTime(std::string date); |   int ReadLiveStream(unsigned char* buffer, unsigned int size) override; | ||||||
| 		int64_t parseID(std::string id); |   void CloseLiveStream() override; | ||||||
|  |  | ||||||
| 	private: | protected: | ||||||
| 		std::string serverAddress; |   void Process(); | ||||||
| 		std::vector<OctonetChannel> channels; |  | ||||||
| 		std::vector<OctonetGroup> groups; |  | ||||||
|  |  | ||||||
| 		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 |  *  Copyright (C) 2005-2020 Team Kodi | ||||||
|  *      http://www.xbmc.org |  *  https://kodi.tv | ||||||
|  * |  | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  | ||||||
|  *  it under the terms of the GNU General Public License as published by |  | ||||||
|  *  the Free Software Foundation, either version 2 of the License, or |  | ||||||
|  *  (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  *  This program is distributed in the hope that it will be useful, |  | ||||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  *  GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  *  You should have received a copy of the GNU General Public License |  | ||||||
|  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  * | ||||||
|  |  *  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 "Socket.h" | ||||||
|  |  | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
|  | #include <kodi/General.h> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
| using namespace std; | using namespace std; | ||||||
| using namespace ADDON; |  | ||||||
|  |  | ||||||
| namespace OCTO | namespace OCTO | ||||||
| { | { | ||||||
| @@ -32,28 +20,31 @@ namespace OCTO | |||||||
| /* Master defines for client control */ | /* Master defines for client control */ | ||||||
| #define RECEIVE_TIMEOUT 6 //sec | #define RECEIVE_TIMEOUT 6 //sec | ||||||
|  |  | ||||||
| Socket::Socket(const enum SocketFamily family, 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; |   m_sd = INVALID_SOCKET; | ||||||
|   _family = family; |   m_family = family; | ||||||
|   _domain = domain; |   m_domain = domain; | ||||||
|   _type = type; |   m_type = type; | ||||||
|   _protocol = protocol; |   m_protocol = protocol; | ||||||
|   _port = 0; |   m_port = 0; | ||||||
|   memset (&_sockaddr, 0, sizeof( _sockaddr ) ); |   memset(&m_sockaddr, 0, sizeof(m_sockaddr)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| Socket::Socket() | Socket::Socket() | ||||||
| { | { | ||||||
|   // Default constructor, default settings |   // Default constructor, default settings | ||||||
|   _sd = INVALID_SOCKET; |   m_sd = INVALID_SOCKET; | ||||||
|   _family = af_inet; |   m_family = af_inet; | ||||||
|   _domain = pf_inet; |   m_domain = pf_inet; | ||||||
|   _type = sock_stream; |   m_type = sock_stream; | ||||||
|   _protocol = tcp; |   m_protocol = tcp; | ||||||
|   _port = 0; |   m_port = 0; | ||||||
|   memset (&_sockaddr, 0, sizeof( _sockaddr ) ); |   memset(&m_sockaddr, 0, sizeof(m_sockaddr)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -65,7 +56,7 @@ Socket::~Socket() | |||||||
|  |  | ||||||
| bool Socket::setHostname(const std::string& host) | bool Socket::setHostname(const std::string& host) | ||||||
| { | { | ||||||
|   _hostname = host; |   m_hostname = host; | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -73,9 +64,9 @@ bool Socket::close() | |||||||
| { | { | ||||||
|   if (is_valid()) |   if (is_valid()) | ||||||
|   { |   { | ||||||
|     if (_sd != SOCKET_ERROR) |     if (m_sd != SOCKET_ERROR) | ||||||
|       closesocket(_sd); |       closesocket(m_sd); | ||||||
|     _sd = INVALID_SOCKET; |     m_sd = INVALID_SOCKET; | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   return false; |   return false; | ||||||
| @@ -85,7 +76,7 @@ bool Socket::create() | |||||||
| { | { | ||||||
|   close(); |   close(); | ||||||
|  |  | ||||||
|   if(!osInit()) |   if (!osInit()) | ||||||
|   { |   { | ||||||
|     return false; |     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()) |   if (is_valid()) | ||||||
|   { |   { | ||||||
|       close(); |     close(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   _sd = socket(_family, _type, _protocol); |   m_sd = socket(m_family, m_type, m_protocol); | ||||||
|   _port = port; |   m_port = port; | ||||||
|   _sockaddr.sin_family = (sa_family_t) _family; |   m_sockaddr.sin_family = (sa_family_t)m_family; | ||||||
|   _sockaddr.sin_addr.s_addr = INADDR_ANY;  //listen to all |   m_sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all | ||||||
|   _sockaddr.sin_port = htons( _port ); |   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; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -128,13 +119,13 @@ bool Socket::listen() const | |||||||
|     return false; |     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. |   //This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h. | ||||||
|   //linux 128//MAXCONNECTIONS =1 |   //linux 128//MAXCONNECTIONS =1 | ||||||
|  |  | ||||||
|   if (listen_return == -1) |   if (listen_return == -1) | ||||||
|   { |   { | ||||||
|     errormessage( getLastError(), "Socket::listen" ); |     errormessage(getLastError(), "Socket::listen"); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -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()) |   if (!is_valid()) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   socklen_t addr_length = sizeof( _sockaddr ); |   socklen_t addr_length = sizeof(m_sockaddr); | ||||||
|   new_socket._sd = ::accept(_sd, const_cast<sockaddr*>( (const sockaddr*) &_sockaddr), &addr_length ); |   new_socket.m_sd = | ||||||
|  |       ::accept(m_sd, const_cast<sockaddr*>((const sockaddr*)&m_sockaddr), &addr_length); | ||||||
|  |  | ||||||
| #ifdef TARGET_WINDOWS | #ifdef TARGET_WINDOWS | ||||||
|   if (new_socket._sd == INVALID_SOCKET) |   if (new_socket.m_sd == INVALID_SOCKET) | ||||||
| #else | #else | ||||||
|   if (new_socket._sd <= 0) |   if (new_socket.m_sd <= 0) | ||||||
| #endif | #endif | ||||||
|   { |   { | ||||||
|     errormessage( getLastError(), "Socket::accept" ); |     errormessage(getLastError(), "Socket::accept"); | ||||||
|     return false; |     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; |   fd_set set_w, set_e; | ||||||
|   struct timeval tv; |   struct timeval tv; | ||||||
|   int  result; |   int result; | ||||||
|  |  | ||||||
|   if (!is_valid()) |   if (!is_valid()) | ||||||
|   { |   { | ||||||
| @@ -184,35 +176,35 @@ int Socket::send ( const char* data, const unsigned int len ) | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // fill with new data |   // fill with new data | ||||||
|   tv.tv_sec  = 0; |   tv.tv_sec = 0; | ||||||
|   tv.tv_usec = 0; |   tv.tv_usec = 0; | ||||||
|  |  | ||||||
|   FD_ZERO(&set_w); |   FD_ZERO(&set_w); | ||||||
|   FD_ZERO(&set_e); |   FD_ZERO(&set_e); | ||||||
|   FD_SET(_sd, &set_w); |   FD_SET(m_sd, &set_w); | ||||||
|   FD_SET(_sd, &set_e); |   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) |   if (result < 0) | ||||||
|   { |   { | ||||||
|     libKodi->Log(LOG_ERROR, "Socket::send  - select failed"); |     kodi::Log(ADDON_LOG_ERROR, "Socket::send  - select failed"); | ||||||
|     close(); |     close(); | ||||||
|     return 0; |     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(); |     close(); | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   int status = ::send(_sd, data, len, 0 ); |   int status = ::send(m_sd, data, len, 0); | ||||||
|  |  | ||||||
|   if (status == -1) |   if (status == -1) | ||||||
|   { |   { | ||||||
|     errormessage( getLastError(), "Socket::send"); |     errormessage(getLastError(), "Socket::send"); | ||||||
|     libKodi->Log(LOG_ERROR, "Socket::send  - failed to send data"); |     kodi::Log(ADDON_LOG_ERROR, "Socket::send  - failed to send data"); | ||||||
|     close(); |     close(); | ||||||
|     return 0; |     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 sentbytes = 0; | ||||||
|   int i; |   int i; | ||||||
|  |  | ||||||
|   do |   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) |     if (i <= 0) | ||||||
|     { |     { | ||||||
|       errormessage( getLastError(), "Socket::sendto"); |       errormessage(getLastError(), "Socket::sendto"); | ||||||
|       osCleanup(); |       osCleanup(); | ||||||
|       return i; |       return i; | ||||||
|     } |     } | ||||||
|     sentbytes += i; |     sentbytes += i; | ||||||
|   } while ( (sentbytes < (int) size) && (sendcompletebuffer == true)); |   } while ((sentbytes < (int)size) && (sendcompletebuffer == true)); | ||||||
|  |  | ||||||
|   return i; |   return i; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| int Socket::receive ( std::string& data, unsigned int minpacketsize ) const | int Socket::receive(std::string& data, unsigned int minpacketsize) const | ||||||
| { | { | ||||||
|   char * buf = NULL; |   char* buf = nullptr; | ||||||
|   int status = 0; |   int status = 0; | ||||||
|  |  | ||||||
|   if (!is_valid()) |   if (!is_valid()) | ||||||
| @@ -252,10 +244,10 @@ int Socket::receive ( std::string& data, unsigned int minpacketsize ) const | |||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   buf = new char [ minpacketsize + 1 ]; |   buf = new char[minpacketsize + 1]; | ||||||
|   memset ( buf, 0, minpacketsize + 1 ); |   memset(buf, 0, minpacketsize + 1); | ||||||
|  |  | ||||||
|   status = receive( buf, minpacketsize, minpacketsize ); |   status = receive(buf, minpacketsize, minpacketsize); | ||||||
|  |  | ||||||
|   data = buf; |   data = buf; | ||||||
|  |  | ||||||
| @@ -265,12 +257,12 @@ int Socket::receive ( std::string& data, unsigned int minpacketsize ) const | |||||||
|  |  | ||||||
|  |  | ||||||
| //Receive until error or \n | //Receive until error or \n | ||||||
| bool Socket::ReadLine (string& line) | bool Socket::ReadLine(string& line) | ||||||
| { | { | ||||||
|   fd_set         set_r, set_e; |   fd_set set_r, set_e; | ||||||
|   timeval        timeout; |   timeval timeout; | ||||||
|   int            retries = 6; |   int retries = 6; | ||||||
|   char           buffer[2048]; |   char buffer[2048]; | ||||||
|  |  | ||||||
|   if (!is_valid()) |   if (!is_valid()) | ||||||
|     return false; |     return false; | ||||||
| @@ -284,20 +276,20 @@ bool Socket::ReadLine (string& line) | |||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     timeout.tv_sec  = RECEIVE_TIMEOUT; |     timeout.tv_sec = RECEIVE_TIMEOUT; | ||||||
|     timeout.tv_usec = 0; |     timeout.tv_usec = 0; | ||||||
|  |  | ||||||
|     // fill with new data |     // fill with new data | ||||||
|     FD_ZERO(&set_r); |     FD_ZERO(&set_r); | ||||||
|     FD_ZERO(&set_e); |     FD_ZERO(&set_e); | ||||||
|     FD_SET(_sd, &set_r); |     FD_SET(m_sd, &set_r); | ||||||
|     FD_SET(_sd, &set_e); |     FD_SET(m_sd, &set_e); | ||||||
|     int result = select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout); |     int result = select(FD_SETSIZE, &set_r, nullptr, &set_e, &timeout); | ||||||
|  |  | ||||||
|     if (result < 0) |     if (result < 0) | ||||||
|     { |     { | ||||||
|       libKodi->Log(LOG_DEBUG, "%s: select failed", __FUNCTION__); |       kodi::Log(ADDON_LOG_DEBUG, "%s: select failed", __func__); | ||||||
|       errormessage(getLastError(), __FUNCTION__); |       errormessage(getLastError(), __func__); | ||||||
|       close(); |       close(); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| @@ -306,20 +298,24 @@ bool Socket::ReadLine (string& line) | |||||||
|     { |     { | ||||||
|       if (retries != 0) |       if (retries != 0) | ||||||
|       { |       { | ||||||
|          libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__, retries); |         kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __func__, | ||||||
|          retries--; |                   retries); | ||||||
|  |         retries--; | ||||||
|         continue; |         continue; | ||||||
|       } else { |       } | ||||||
|          libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", __FUNCTION__); |       else | ||||||
|          return false; |       { | ||||||
|  |         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) |     if (result < 0) | ||||||
|     { |     { | ||||||
|       libKodi->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__); |       kodi::Log(ADDON_LOG_DEBUG, "%s: recv failed", __func__); | ||||||
|       errormessage(getLastError(), __FUNCTION__); |       errormessage(getLastError(), __func__); | ||||||
|       close(); |       close(); | ||||||
|       return false; |       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]; |   char buf[MAXRECV + 1]; | ||||||
|   int status = 0; |   int status = 0; | ||||||
|  |  | ||||||
|   if ( !is_valid() ) |   if (!is_valid()) | ||||||
|   { |   { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   memset ( buf, 0, MAXRECV + 1 ); |   memset(buf, 0, MAXRECV + 1); | ||||||
|   status = receive( buf, MAXRECV, 0 ); |   status = receive(buf, MAXRECV, 0); | ||||||
|   data = buf; |   data = buf; | ||||||
|  |  | ||||||
|   return status; |   return status; | ||||||
| } | } | ||||||
|  |  | ||||||
| int Socket::receive ( char* data, 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; |   unsigned int receivedsize = 0; | ||||||
|  |  | ||||||
|   if ( !is_valid() ) |   if (!is_valid()) | ||||||
|   { |   { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   while ( (receivedsize <= minpacketsize) && (receivedsize < buffersize) ) |   while ((receivedsize <= minpacketsize) && (receivedsize < buffersize)) | ||||||
|   { |   { | ||||||
|     int status = ::recv(_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; |       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; |   return status; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| bool Socket::connect ( const std::string& host, const unsigned short port ) | bool Socket::connect(const std::string& host, const unsigned short port) | ||||||
| { | { | ||||||
|   close(); |   close(); | ||||||
|  |  | ||||||
|   if ( !setHostname( host ) ) |   if (!setHostname(host)) | ||||||
|   { |   { | ||||||
|     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; |     return false; | ||||||
|   } |   } | ||||||
|   _port = port; |   m_port = port; | ||||||
|  |  | ||||||
|   char strPort[15]; |   char strPort[15]; | ||||||
|   snprintf(strPort, 15, "%hu", port); |   snprintf(strPort, 15, "%hu", port); | ||||||
|  |  | ||||||
|   struct addrinfo hints; |   struct addrinfo hints; | ||||||
|   struct addrinfo* result = NULL; |   struct addrinfo* result = nullptr; | ||||||
|   struct addrinfo *address = NULL; |   struct addrinfo* address = nullptr; | ||||||
|   memset(&hints, 0, sizeof(hints)); |   memset(&hints, 0, sizeof(hints)); | ||||||
|   hints.ai_family = _family; |   hints.ai_family = m_family; | ||||||
|   hints.ai_socktype = _type; |   hints.ai_socktype = m_type; | ||||||
|   hints.ai_protocol = _protocol; |   hints.ai_protocol = m_protocol; | ||||||
|  |  | ||||||
|   int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); |   int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); | ||||||
|   if (retval != 0) |   if (retval != 0) | ||||||
| @@ -412,18 +413,18 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   for (address = result; address != NULL; address = address->ai_next) |   for (address = result; address != nullptr; address = address->ai_next) | ||||||
|   { |   { | ||||||
|     // Create the socket |     // 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"); |       errormessage(getLastError(), "Socket::create"); | ||||||
|       continue; |       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) |     if (status == SOCKET_ERROR) | ||||||
|     { |     { | ||||||
|       close(); |       close(); | ||||||
| @@ -436,9 +437,9 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) | |||||||
|  |  | ||||||
|   freeaddrinfo(result); |   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"); |     errormessage(getLastError(), "Socket::connect"); | ||||||
|     close(); |     close(); | ||||||
|     return false; |     return false; | ||||||
| @@ -449,123 +450,124 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) | |||||||
|  |  | ||||||
| bool Socket::reconnect() | bool Socket::reconnect() | ||||||
| { | { | ||||||
|   if ( is_valid() ) |   if (is_valid()) | ||||||
|   { |   { | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return connect(_hostname, _port); |   return connect(m_hostname, m_port); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool Socket::is_valid() const | bool Socket::is_valid() const | ||||||
| { | { | ||||||
|   return (_sd != INVALID_SOCKET); |   return (m_sd != INVALID_SOCKET); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(TARGET_WINDOWS) | #if defined(TARGET_WINDOWS) | ||||||
| bool Socket::set_non_blocking ( const bool b ) | bool Socket::set_non_blocking(const bool b) | ||||||
| { | { | ||||||
|   u_long iMode; |   u_long iMode; | ||||||
|  |  | ||||||
|   if ( b ) |   if (b) | ||||||
|     iMode = 1;  // enable non_blocking |     iMode = 1; // enable non_blocking | ||||||
|   else |   else | ||||||
|     iMode = 0;  // disable non_blocking |     iMode = 0; // disable non_blocking | ||||||
|  |  | ||||||
|   if (ioctlsocket(_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 false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Socket::errormessage( int errnum, const char* functionname) const | void Socket::errormessage(int errnum, const char* functionname) const | ||||||
| { | { | ||||||
|   const char* errmsg = NULL; |   const char* errmsg = nullptr; | ||||||
|  |  | ||||||
|   switch (errnum) |   switch (errnum) | ||||||
|   { |   { | ||||||
|   case WSANOTINITIALISED: |     case WSANOTINITIALISED: | ||||||
|     errmsg = "A successful WSAStartup call must occur before using this function."; |       errmsg = "A successful WSAStartup call must occur before using this function."; | ||||||
|     break; |       break; | ||||||
|   case WSAENETDOWN: |     case WSAENETDOWN: | ||||||
|     errmsg = "The network subsystem or the associated service provider has failed"; |       errmsg = "The network subsystem or the associated service provider has failed"; | ||||||
|     break; |       break; | ||||||
|   case WSA_NOT_ENOUGH_MEMORY: |     case WSA_NOT_ENOUGH_MEMORY: | ||||||
|     errmsg = "Insufficient memory available"; |       errmsg = "Insufficient memory available"; | ||||||
|     break; |       break; | ||||||
|   case WSA_INVALID_PARAMETER: |     case WSA_INVALID_PARAMETER: | ||||||
|     errmsg = "One or more parameters are invalid"; |       errmsg = "One or more parameters are invalid"; | ||||||
|     break; |       break; | ||||||
|   case WSA_OPERATION_ABORTED: |     case WSA_OPERATION_ABORTED: | ||||||
|     errmsg = "Overlapped operation aborted"; |       errmsg = "Overlapped operation aborted"; | ||||||
|     break; |       break; | ||||||
|   case WSAEINTR: |     case WSAEINTR: | ||||||
|     errmsg = "Interrupted function call"; |       errmsg = "Interrupted function call"; | ||||||
|     break; |       break; | ||||||
|   case WSAEBADF: |     case WSAEBADF: | ||||||
|     errmsg = "File handle is not valid"; |       errmsg = "File handle is not valid"; | ||||||
|     break; |       break; | ||||||
|   case WSAEACCES: |     case WSAEACCES: | ||||||
|     errmsg = "Permission denied"; |       errmsg = "Permission denied"; | ||||||
|     break; |       break; | ||||||
|   case WSAEFAULT: |     case WSAEFAULT: | ||||||
|     errmsg = "Bad address"; |       errmsg = "Bad address"; | ||||||
|     break; |       break; | ||||||
|   case WSAEINVAL: |     case WSAEINVAL: | ||||||
|     errmsg = "Invalid argument"; |       errmsg = "Invalid argument"; | ||||||
|     break; |       break; | ||||||
|   case WSAENOTSOCK: |     case WSAENOTSOCK: | ||||||
|     errmsg = "Socket operation on nonsocket"; |       errmsg = "Socket operation on nonsocket"; | ||||||
|     break; |       break; | ||||||
|   case WSAEDESTADDRREQ: |     case WSAEDESTADDRREQ: | ||||||
|     errmsg = "Destination address required"; |       errmsg = "Destination address required"; | ||||||
|     break; |       break; | ||||||
|   case WSAEMSGSIZE: |     case WSAEMSGSIZE: | ||||||
|     errmsg = "Message too long"; |       errmsg = "Message too long"; | ||||||
|     break; |       break; | ||||||
|   case WSAEPROTOTYPE: |     case WSAEPROTOTYPE: | ||||||
|     errmsg = "Protocol wrong type for socket"; |       errmsg = "Protocol wrong type for socket"; | ||||||
|     break; |       break; | ||||||
|   case WSAENOPROTOOPT: |     case WSAENOPROTOOPT: | ||||||
|     errmsg = "Bad protocol option"; |       errmsg = "Bad protocol option"; | ||||||
|     break; |       break; | ||||||
|   case WSAEPFNOSUPPORT: |     case WSAEPFNOSUPPORT: | ||||||
|     errmsg = "Protocol family not supported"; |       errmsg = "Protocol family not supported"; | ||||||
|     break; |       break; | ||||||
|   case WSAEAFNOSUPPORT: |     case WSAEAFNOSUPPORT: | ||||||
|     errmsg = "Address family not supported by protocol family"; |       errmsg = "Address family not supported by protocol family"; | ||||||
|     break; |       break; | ||||||
|   case WSAEADDRINUSE: |     case WSAEADDRINUSE: | ||||||
|     errmsg = "Address already in use"; |       errmsg = "Address already in use"; | ||||||
|     break; |       break; | ||||||
|   case WSAECONNRESET: |     case WSAECONNRESET: | ||||||
|     errmsg = "Connection reset by peer"; |       errmsg = "Connection reset by peer"; | ||||||
|     break; |       break; | ||||||
|   case WSAHOST_NOT_FOUND: |     case WSAHOST_NOT_FOUND: | ||||||
|     errmsg = "Authoritative answer host not found"; |       errmsg = "Authoritative answer host not found"; | ||||||
|     break; |       break; | ||||||
|   case WSATRY_AGAIN: |     case WSATRY_AGAIN: | ||||||
|     errmsg = "Nonauthoritative host not found, or server failure"; |       errmsg = "Nonauthoritative host not found, or server failure"; | ||||||
|     break; |       break; | ||||||
|   case WSAEISCONN: |     case WSAEISCONN: | ||||||
|     errmsg = "Socket is already connected"; |       errmsg = "Socket is already connected"; | ||||||
|     break; |       break; | ||||||
|   case WSAETIMEDOUT: |     case WSAETIMEDOUT: | ||||||
|     errmsg = "Connection timed out"; |       errmsg = "Connection timed out"; | ||||||
|     break; |       break; | ||||||
|   case WSAECONNREFUSED: |     case WSAECONNREFUSED: | ||||||
|     errmsg = "Connection refused"; |       errmsg = "Connection refused"; | ||||||
|     break; |       break; | ||||||
|   case WSANO_DATA: |     case WSANO_DATA: | ||||||
|     errmsg = "Valid name, no data record of requested type"; |       errmsg = "Valid name, no data record of requested type"; | ||||||
|     break; |       break; | ||||||
|   default: |     default: | ||||||
|     errmsg = "WSA Error"; |       errmsg = "WSA Error"; | ||||||
|   } |   } | ||||||
|   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 | int Socket::getLastError() const | ||||||
| @@ -579,15 +581,15 @@ bool Socket::osInit() | |||||||
| { | { | ||||||
|   win_usage_count++; |   win_usage_count++; | ||||||
|   // initialize winsock: |   // initialize winsock: | ||||||
|   if (WSAStartup(MAKEWORD(2,2),&_wsaData) != 0) |   if (WSAStartup(MAKEWORD(2, 2), &m_wsaData) != 0) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   WORD wVersionRequested = MAKEWORD(2,2); |   WORD wVersionRequested = MAKEWORD(2, 2); | ||||||
|  |  | ||||||
|   // check version |   // check version | ||||||
|   if (_wsaData.wVersion != wVersionRequested) |   if (m_wsaData.wVersion != wVersionRequested) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| @@ -598,42 +600,42 @@ bool Socket::osInit() | |||||||
| void Socket::osCleanup() | void Socket::osCleanup() | ||||||
| { | { | ||||||
|   win_usage_count--; |   win_usage_count--; | ||||||
|   if(win_usage_count == 0) |   if (win_usage_count == 0) | ||||||
|   { |   { | ||||||
|     WSACleanup(); |     WSACleanup(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||||
| bool Socket::set_non_blocking ( const bool b ) | bool Socket::set_non_blocking(const bool b) | ||||||
| { | { | ||||||
|   int opts; |   int opts; | ||||||
|  |  | ||||||
|   opts = fcntl(_sd, F_GETFL); |   opts = fcntl(m_sd, F_GETFL); | ||||||
|  |  | ||||||
|   if ( opts < 0 ) |   if (opts < 0) | ||||||
|   { |   { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if ( b ) |   if (b) | ||||||
|     opts = ( opts | O_NONBLOCK ); |     opts = (opts | O_NONBLOCK); | ||||||
|   else |   else | ||||||
|     opts = ( opts & ~O_NONBLOCK ); |     opts = (opts & ~O_NONBLOCK); | ||||||
|  |  | ||||||
|   if(fcntl (_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 false; | ||||||
|   } |   } | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Socket::errormessage( int errnum, const char* functionname) const | void Socket::errormessage(int errnum, const char* functionname) const | ||||||
| { | { | ||||||
|   const char* errmsg = NULL; |   const char* errmsg = nullptr; | ||||||
|  |  | ||||||
|   switch ( errnum ) |   switch (errnum) | ||||||
|   { |   { | ||||||
|     case EAGAIN: //same as EWOULDBLOCK |     case EAGAIN: //same as EWOULDBLOCK | ||||||
|       errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block"; |       errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block"; | ||||||
| @@ -660,7 +662,8 @@ void Socket::errormessage( int errnum, const char* functionname) const | |||||||
|       errmsg = "ENOTSOCK: The argument is not a valid socket"; |       errmsg = "ENOTSOCK: The argument is not a valid socket"; | ||||||
|       break; |       break; | ||||||
|     case EMSGSIZE: |     case EMSGSIZE: | ||||||
|       errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the 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; |       break; | ||||||
|     case ENOBUFS: |     case ENOBUFS: | ||||||
|       errmsg = "ENOBUFS: The output queue for a network interface was full"; |       errmsg = "ENOBUFS: The output queue for a network interface was full"; | ||||||
| @@ -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"; |       errmsg = "EPIPE: The local end has been shut down on a connection oriented socket"; | ||||||
|       break; |       break; | ||||||
|     case EPROTONOSUPPORT: |     case EPROTONOSUPPORT: | ||||||
|       errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported within this domain"; |       errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported " | ||||||
|  |                "within this domain"; | ||||||
|       break; |       break; | ||||||
|     case EAFNOSUPPORT: |     case EAFNOSUPPORT: | ||||||
|       errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family"; |       errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family"; | ||||||
| @@ -684,13 +688,16 @@ void Socket::errormessage( int errnum, const char* functionname) const | |||||||
|       errmsg = "EMFILE: Process file table overflow"; |       errmsg = "EMFILE: Process file table overflow"; | ||||||
|       break; |       break; | ||||||
|     case EACCES: |     case EACCES: | ||||||
|       errmsg = "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; |       break; | ||||||
|     case ECONNREFUSED: |     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; |       break; | ||||||
|     case ENOTCONN: |     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; |       break; | ||||||
|     //case E: |     //case E: | ||||||
|     //	errmsg = ""; |     //	errmsg = ""; | ||||||
| @@ -699,7 +706,7 @@ void Socket::errormessage( int errnum, const char* functionname) const | |||||||
|       break; |       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 | int Socket::getLastError() const | ||||||
|   | |||||||
							
								
								
									
										428
									
								
								src/Socket.h
									
									
									
									
									
								
							
							
						
						
									
										428
									
								
								src/Socket.h
									
									
									
									
									
								
							| @@ -1,83 +1,73 @@ | |||||||
| /* | /* | ||||||
|  *      Copyright (C) 2005-2011 Team XBMC |  *  Copyright (C) 2005-2020 Team Kodi | ||||||
|  *      http://www.xbmc.org |  *  https://kodi.tv | ||||||
|  * |  | ||||||
|  *  This Program is free software; you can redistribute it and/or modify |  | ||||||
|  *  it under the terms of the GNU General Public License as published by |  | ||||||
|  *  the Free Software Foundation; either version 2, or (at your option) |  | ||||||
|  *  any later version. |  | ||||||
|  * |  | ||||||
|  *  This Program is distributed in the hope that it will be useful, |  | ||||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |  | ||||||
|  *  GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  *  You should have received a copy of the GNU General Public License |  | ||||||
|  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  * | ||||||
|  |  *  SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  *  See LICENSE.md for more information. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| //Include platform specific datatypes, header files, defines and constants: | //Include platform specific datatypes, header files, defines and constants: | ||||||
| #if defined TARGET_WINDOWS | #if defined TARGET_WINDOWS | ||||||
|   #define WIN32_LEAN_AND_MEAN           // Enable LEAN_AND_MEAN support | #define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support | ||||||
|   #pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" | #pragma warning(disable : 4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" | ||||||
|   #include <winsock2.h> | #include <WS2tcpip.h> | ||||||
|   #include <WS2tcpip.h> | #include <winsock2.h> | ||||||
|   #pragma warning(default:4005) | #pragma warning(default : 4005) | ||||||
|   #include <windows.h> | #include <windows.h> | ||||||
|  |  | ||||||
|   #ifndef NI_MAXHOST | #ifndef NI_MAXHOST | ||||||
|     #define NI_MAXHOST 1025 | #define NI_MAXHOST 1025 | ||||||
|   #endif | #endif | ||||||
|  |  | ||||||
|   #ifndef socklen_t | #ifndef socklen_t | ||||||
|     typedef int socklen_t; | typedef int socklen_t; | ||||||
|   #endif | #endif | ||||||
|   #ifndef ipaddr_t | #ifndef ipaddr_t | ||||||
|     typedef unsigned long ipaddr_t; | typedef unsigned long ipaddr_t; | ||||||
|   #endif | #endif | ||||||
|   #ifndef port_t | #ifndef port_t | ||||||
|     typedef unsigned short port_t; | typedef unsigned short port_t; | ||||||
|   #endif | #endif | ||||||
|   #ifndef sa_family_t | #ifndef sa_family_t | ||||||
|     #define sa_family_t ADDRESS_FAMILY | #define sa_family_t ADDRESS_FAMILY | ||||||
|   #endif | #endif | ||||||
| #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||||
| #ifdef SOCKADDR_IN | #ifdef SOCKADDR_IN | ||||||
| #undef SOCKADDR_IN | #undef SOCKADDR_IN | ||||||
| #endif | #endif | ||||||
|   #include <sys/types.h>     /* for socket,connect */ | #include <arpa/inet.h> /* for inet_pton */ | ||||||
|   #include <sys/socket.h>    /* for socket,connect */ | #include <errno.h> | ||||||
|   #include <sys/un.h>        /* for Unix socket */ | #include <fcntl.h> | ||||||
|   #include <arpa/inet.h>     /* for inet_pton */ | #include <netdb.h> /* for gethostbyname */ | ||||||
|   #include <netdb.h>         /* for gethostbyname */ | #include <netinet/in.h> /* for htons */ | ||||||
|   #include <netinet/in.h>    /* for htons */ | #include <sys/socket.h> /* for socket,connect */ | ||||||
|   #include <unistd.h>        /* for read, write, close */ | #include <sys/types.h> /* for socket,connect */ | ||||||
|   #include <errno.h> | #include <sys/un.h> /* for Unix socket */ | ||||||
|   #include <fcntl.h> | #include <unistd.h> /* for read, write, close */ | ||||||
|  |  | ||||||
|   typedef int SOCKET; | typedef int SOCKET; | ||||||
|   typedef sockaddr SOCKADDR; | typedef sockaddr SOCKADDR; | ||||||
|   typedef sockaddr_in SOCKADDR_IN; | typedef sockaddr_in SOCKADDR_IN; | ||||||
|   #ifndef INVALID_SOCKET | #ifndef INVALID_SOCKET | ||||||
|   #define INVALID_SOCKET (-1) | #define INVALID_SOCKET (-1) | ||||||
|   #endif | #endif | ||||||
|   #define SOCKET_ERROR (-1) | #define SOCKET_ERROR (-1) | ||||||
|  |  | ||||||
|   #define closesocket(sd) ::close(sd) | #define closesocket(sd) ::close(sd) | ||||||
| #else | #else | ||||||
|   #error Platform specific socket support is not yet available on this platform! | #error Platform specific socket support is not yet available on this platform! | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| namespace OCTO | namespace OCTO | ||||||
| { | { | ||||||
|  |  | ||||||
| #define MAXCONNECTIONS 1  ///< Maximum number of pending connections before "Connection refused" | #define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused" | ||||||
| #define MAXRECV 1500      ///< Maximum packet size | #define MAXRECV 1500 ///< Maximum packet size | ||||||
|  |  | ||||||
| enum SocketFamily | enum SocketFamily | ||||||
| { | { | ||||||
| @@ -88,10 +78,10 @@ enum SocketFamily | |||||||
|  |  | ||||||
| enum SocketDomain | enum SocketDomain | ||||||
| { | { | ||||||
|   #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD | ||||||
|     pf_unix  = PF_UNIX, |   pf_unix = PF_UNIX, | ||||||
|     pf_local = PF_LOCAL, |   pf_local = PF_LOCAL, | ||||||
|   #endif | #endif | ||||||
|   pf_inet = PF_INET |   pf_inet = PF_INET | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -109,197 +99,187 @@ enum SocketProtocol | |||||||
|  |  | ||||||
| class Socket | class Socket | ||||||
| { | { | ||||||
|   public: | public: | ||||||
|  |   /*! | ||||||
|  |    * An unconnected socket may be created directly on the local | ||||||
|  |    * machine. The socket type (SOCK_STREAM, SOCK_DGRAM) and | ||||||
|  |    * protocol may also be specified. | ||||||
|  |    * If the socket cannot be created, an exception is thrown. | ||||||
|  |    * | ||||||
|  |    * \param family Socket family (IPv4 or IPv6) | ||||||
|  |    * \param domain The domain parameter specifies a communications domain within which communication will take place; | ||||||
|  |    * this selects the protocol family which should be used. | ||||||
|  |    * \param type base type and protocol family of the socket. | ||||||
|  |    * \param protocol specific protocol to apply. | ||||||
|  |    */ | ||||||
|  |   Socket(const enum SocketFamily family, | ||||||
|  |          const enum SocketDomain domain, | ||||||
|  |          const enum SocketType type, | ||||||
|  |          const enum SocketProtocol protocol = tcp); | ||||||
|  |   Socket(void); | ||||||
|  |   virtual ~Socket(); | ||||||
|  |  | ||||||
|     /*! |   //Socket settings | ||||||
|      * An unconnected socket may be created directly on the local |  | ||||||
|      * machine. The socket type (SOCK_STREAM, SOCK_DGRAM) and |  | ||||||
|      * protocol may also be specified. |  | ||||||
|      * If the socket cannot be created, an exception is thrown. |  | ||||||
|      * |  | ||||||
|      * \param family Socket family (IPv4 or IPv6) |  | ||||||
|      * \param domain The domain parameter specifies a communications domain within which communication will take place; |  | ||||||
|      * this selects the protocol family which should be used. |  | ||||||
|      * \param type base type and protocol family of the socket. |  | ||||||
|      * \param protocol specific protocol to apply. |  | ||||||
|      */ |  | ||||||
|     Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol = tcp); |  | ||||||
|     Socket(void); |  | ||||||
|     virtual ~Socket(); |  | ||||||
|  |  | ||||||
|     //Socket settings |   /*! | ||||||
|  |    * Socket setFamily | ||||||
|  |    * \param family    Can be af_inet or af_inet6. Default: af_inet | ||||||
|  |    */ | ||||||
|  |   void setFamily(const enum SocketFamily family) { m_family = family; }; | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket setFamily |    * Socket setDomain | ||||||
|      * \param family    Can be af_inet or af_inet6. Default: af_inet |    * \param domain    Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet | ||||||
|      */ |    */ | ||||||
|     void setFamily(const enum SocketFamily family) |   void setDomain(const enum SocketDomain domain) { m_domain = domain; }; | ||||||
|     { |  | ||||||
|       _family = family; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket setDomain |    * Socket setType | ||||||
|      * \param domain    Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet |    * \param type    Can be sock_stream or sock_dgram. Default: sock_stream. | ||||||
|      */ |    */ | ||||||
|     void setDomain(const enum SocketDomain domain) |   void setType(const enum SocketType type) { m_type = type; }; | ||||||
|     { |  | ||||||
|       _domain = domain; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket setType |    * Socket setProtocol | ||||||
|      * \param type    Can be sock_stream or sock_dgram. Default: sock_stream. |    * \param protocol    Can be tcp or udp. Default: tcp. | ||||||
|      */ |    */ | ||||||
|     void setType(const enum SocketType type) |   void setProtocol(const enum SocketProtocol protocol) { m_protocol = protocol; }; | ||||||
|     { |  | ||||||
|       _type = type; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket setProtocol |    * Socket setPort | ||||||
|      * \param protocol    Can be tcp or udp. Default: tcp. |    * \param port    port number for socket communication | ||||||
|      */ |    */ | ||||||
|     void setProtocol(const enum SocketProtocol protocol) |   void setPort(const unsigned short port) { m_sockaddr.sin_port = htons(port); }; | ||||||
|     { |  | ||||||
|       _protocol = protocol; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /*! |   bool setHostname(const std::string& host); | ||||||
|      * Socket setPort |  | ||||||
|      * \param port    port number for socket communication |  | ||||||
|      */ |  | ||||||
|     void setPort (const unsigned short port) |  | ||||||
|     { |  | ||||||
|       _sockaddr.sin_port = htons ( port ); |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     bool setHostname ( const std::string& host ); |   // Server initialization | ||||||
|  |  | ||||||
|     // Server initialization |   /*! | ||||||
|  |    * Socket create | ||||||
|  |    * Create a new socket | ||||||
|  |    * \return     True if succesful | ||||||
|  |    */ | ||||||
|  |   bool create(); | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket create |    * Socket close | ||||||
|      * Create a new socket |    * Close the socket | ||||||
|      * \return     True if succesful |    * \return     True if succesful | ||||||
|      */ |    */ | ||||||
|     bool create(); |   bool close(); | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket close |    * Socket bind | ||||||
|      * Close the socket |    */ | ||||||
|      * \return     True if succesful |   bool bind(const unsigned short port); | ||||||
|      */ |   bool listen() const; | ||||||
|     bool close(); |   bool accept(Socket& socket) const; | ||||||
|  |  | ||||||
|     /*! |   // Client initialization | ||||||
|      * Socket bind |   bool connect(const std::string& host, const unsigned short port); | ||||||
|      */ |  | ||||||
|     bool bind ( const unsigned short port ); |  | ||||||
|     bool listen() const; |  | ||||||
|     bool accept ( Socket& socket ) const; |  | ||||||
|  |  | ||||||
|     // Client initialization |   bool reconnect(); | ||||||
|     bool connect ( const std::string& host, const unsigned short port ); |  | ||||||
|  |  | ||||||
|     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 |    * Socket send function | ||||||
|      * |    * | ||||||
|      * \param data    Reference to a std::string with the data to transmit |    * \param data    Pointer to a character array of size 'size' with the data to transmit | ||||||
|      * \return    Number of bytes send or -1 in case of an error |    * \param size    Length of the data to transmit | ||||||
|      */ |    * \return    Number of bytes send or -1 in case of an error | ||||||
|     int send ( const std::string& data ); |    */ | ||||||
|  |   int send(const char* data, const unsigned int size); | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket send function |    * Socket sendto function | ||||||
|      * |    * | ||||||
|      * \param data    Pointer to a character array of size 'size' with the data to transmit |    * \param data    Reference to a std::string with the data to transmit | ||||||
|      * \param size    Length of the data to transmit |    * \param size    Length of the data to transmit | ||||||
|      * \return    Number of bytes send or -1 in case of an error |    * \param sendcompletebuffer    If 'true': do not return until the complete buffer is transmitted | ||||||
|      */ |    * \return    Number of bytes send or -1 in case of an error | ||||||
|     int send ( const char* data, const unsigned int size ); |    */ | ||||||
|  |   int sendto(const char* data, unsigned int size, bool sendcompletebuffer = false); | ||||||
|  |   // Data Receive | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket sendto function |    * Socket receive function | ||||||
|      * |    * | ||||||
|      * \param data    Reference to a std::string with the data to transmit |    * \param data    Reference to a std::string for storage of the received data. | ||||||
|      * \param size    Length of the data to transmit |    * \param minpacketsize    The minimum number of bytes that should be received before returning from this function | ||||||
|      * \param sendcompletebuffer    If 'true': do not return until the complete buffer is transmitted |    * \return    Number of bytes received or SOCKET_ERROR | ||||||
|      * \return    Number of bytes send or -1 in case of an error |    */ | ||||||
|      */ |   int receive(std::string& data, unsigned int minpacketsize) const; | ||||||
|     int sendto ( const char* data, unsigned int size, bool sendcompletebuffer = false); |  | ||||||
|     // Data Receive |  | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket receive function |    * Socket receive function | ||||||
|      * |    * | ||||||
|      * \param data    Reference to a std::string for storage of the received data. |    * \param data    Reference to a std::string for storage of the received data. | ||||||
|      * \param minpacketsize    The minimum number of bytes that should be received before returning from this function |    * \return    Number of bytes received or SOCKET_ERROR | ||||||
|      * \return    Number of bytes received or SOCKET_ERROR |    */ | ||||||
|      */ |   int receive(std::string& data) const; | ||||||
|     int receive ( std::string& data, unsigned int minpacketsize ) const; |  | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket receive function |    * Socket receive function | ||||||
|      * |    * | ||||||
|      * \param data    Reference to a std::string for storage of the received data. |    * \param data    Pointer to a character array of size buffersize. Used to store the received data. | ||||||
|      * \return    Number of bytes received or SOCKET_ERROR |    * \param buffersize    Size of the 'data' buffer | ||||||
|      */ |    * \param minpacketsize    Specifies the minimum number of bytes that need to be received before returning | ||||||
|     int receive ( std::string& data ) const; |    * \return    Number of bytes received or SOCKET_ERROR | ||||||
|  |    */ | ||||||
|  |   int receive(char* data, const unsigned int buffersize, const unsigned int minpacketsize) const; | ||||||
|  |  | ||||||
|     /*! |   /*! | ||||||
|      * Socket receive function |    * Socket recvfrom function | ||||||
|      * |    * | ||||||
|      * \param data    Pointer to a character array of size buffersize. Used to store the received data. |    * \param data    Pointer to a character array of size buffersize. Used to store the received data. | ||||||
|      * \param buffersize    Size of the 'data' buffer |    * \param buffersize    Size of the 'data' buffer | ||||||
|      * \param minpacketsize    Specifies the minimum number of bytes that need to be received before returning |    * \param from        Optional: pointer to a sockaddr struct that will get the address from which the data is received | ||||||
|      * \return    Number of bytes received or SOCKET_ERROR |    * \param fromlen    Optional, only required if 'from' is given: length of from struct | ||||||
|      */ |    * \return    Number of bytes received or SOCKET_ERROR | ||||||
|     int receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const; |    */ | ||||||
|  |   int recvfrom(char* data, | ||||||
|  |                const int buffersize, | ||||||
|  |                struct sockaddr* from = nullptr, | ||||||
|  |                socklen_t* fromlen = nullptr) const; | ||||||
|  |  | ||||||
|     /*! |   bool set_non_blocking(const bool); | ||||||
|      * Socket recvfrom function |  | ||||||
|      * |  | ||||||
|      * \param data    Pointer to a character array of size buffersize. Used to store the received data. |  | ||||||
|      * \param buffersize    Size of the 'data' buffer |  | ||||||
|      * \param from        Optional: pointer to a sockaddr struct that will get the address from which the data is received |  | ||||||
|      * \param fromlen    Optional, only required if 'from' is given: length of from struct |  | ||||||
|      * \return    Number of bytes received or SOCKET_ERROR |  | ||||||
|      */ |  | ||||||
|     int recvfrom ( char* data, const int buffersize, struct sockaddr* from = NULL, socklen_t* fromlen = NULL) const; |  | ||||||
|  |  | ||||||
|     bool 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 | #ifdef TARGET_WINDOWS | ||||||
|     SOCKADDR_IN _sockaddr;              ///< Socket Address |   WSADATA m_wsaData; ///< Windows Socket data | ||||||
|     //struct addrinfo* _addrinfo;         ///< Socket address info |   static int | ||||||
|     std::string _hostname;              ///< Hostname |       win_usage_count; ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used | ||||||
|     unsigned short _port;               ///< Port number | #endif | ||||||
|  |  | ||||||
|     enum SocketFamily _family;          ///< Socket Address Family |   void errormessage(int errornum, const char* functionname = nullptr) const; | ||||||
|     enum SocketProtocol _protocol;      ///< Socket Protocol |   int getLastError(void) const; | ||||||
|     enum SocketType _type;              ///< Socket Type |   bool osInit(); | ||||||
|     enum SocketDomain _domain;          ///< Socket domain |   void osCleanup(); | ||||||
|  |  | ||||||
|     #ifdef TARGET_WINDOWS |  | ||||||
|       WSADATA _wsaData;                 ///< Windows Socket data |  | ||||||
|       static int win_usage_count;       ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used |  | ||||||
|     #endif |  | ||||||
|  |  | ||||||
|     void errormessage( int errornum, const char* functionname = NULL) const; |  | ||||||
|     int getLastError(void) const; |  | ||||||
|     bool osInit(); |  | ||||||
|     void osCleanup(); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } //namespace OCTO | } //namespace OCTO | ||||||
|   | |||||||
							
								
								
									
										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, const PVR_CHANNEL& channel, time_t iStart, time_t iEnd) |  | ||||||
| { |  | ||||||
| 	return data->getEPG(handle, channel, 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; } |  | ||||||
|  |  | ||||||
| /* Various helper functions */ |  | ||||||
| bool IsTimeshifting(void) { return false; } |  | ||||||
| bool CanPauseStream() { return false; } |  | ||||||
| bool CanSeekStream() { return false; } |  | ||||||
|  |  | ||||||
| /* Callbacks */ |  | ||||||
| void PauseStream(bool bPaused) {} |  | ||||||
| bool SeekTime(double time, bool backwards, double *startpts) { return false; } |  | ||||||
| void SetSpeed(int speed) {} |  | ||||||
| PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; } |  | ||||||
|  |  | ||||||
| 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 "rtsp_client.hpp" | ||||||
|  |  | ||||||
|  | #include "Socket.h" | ||||||
|  |  | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <cctype> | #include <cctype> | ||||||
| #include <iterator> |  | ||||||
| #include "Socket.h" |  | ||||||
| #include "client.h" |  | ||||||
| #include <p8-platform/util/util.h> |  | ||||||
| #include <kodi/libXBMC_addon.h> |  | ||||||
| #include <cstring> | #include <cstring> | ||||||
|  | #include <iterator> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  |  | ||||||
| #if defined(_WIN32) || defined(_WIN64) | #if defined(_WIN32) || defined(_WIN64) | ||||||
| #define strtok_r strtok_s | #define strtok_r strtok_s | ||||||
| #define strncasecmp _strnicmp | #define strncasecmp _strnicmp | ||||||
|  |  | ||||||
| int vasprintf(char **sptr, char *fmt, va_list argv) { | int vasprintf(char** sptr, char* fmt, va_list argv) | ||||||
| 	int wanted = vsnprintf(*sptr = NULL, 0, fmt, argv); | { | ||||||
| 	if((wanted < 0) || ((*sptr = (char *)malloc(1 + wanted)) == NULL)) |   int wanted = vsnprintf(*sptr = nullptr, 0, fmt, argv); | ||||||
| 		return -1; |   if ((wanted < 0) || ((*sptr = (char*)malloc(1 + wanted)) == nullptr)) | ||||||
| 	return vsprintf(*sptr, fmt, argv); |     return -1; | ||||||
|  |   return vsprintf(*sptr, fmt, argv); | ||||||
| } | } | ||||||
|  |  | ||||||
| int asprintf(char **sptr, char *fmt, ...) { | int asprintf(char** sptr, char* fmt, ...) | ||||||
| 	int retval; | { | ||||||
| 	va_list argv; |   int retval; | ||||||
| 	va_start(argv, fmt); |   va_list argv; | ||||||
| 	retval = vasprintf(sptr, fmt, argv); |   va_start(argv, fmt); | ||||||
| 	va_end(argv); |   retval = vasprintf(sptr, fmt, argv); | ||||||
| 	return retval; |   va_end(argv); | ||||||
|  |   return retval; | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -40,441 +49,501 @@ int asprintf(char **sptr, char *fmt, ...) { | |||||||
| #define RTCP_BUFFER_SIZE 1024 | #define RTCP_BUFFER_SIZE 1024 | ||||||
|  |  | ||||||
| using namespace std; | using namespace std; | ||||||
| using namespace ADDON; |  | ||||||
| using namespace OCTO; | using namespace OCTO; | ||||||
|  |  | ||||||
| enum rtsp_state { | enum rtsp_state | ||||||
| 	RTSP_IDLE, | { | ||||||
| 	RTSP_DESCRIBE, |   RTSP_IDLE, | ||||||
| 	RTSP_SETUP, |   RTSP_DESCRIBE, | ||||||
| 	RTSP_PLAY, |   RTSP_SETUP, | ||||||
| 	RTSP_RUNNING |   RTSP_PLAY, | ||||||
|  |   RTSP_RUNNING | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum rtsp_result { | enum rtsp_result | ||||||
| 	RTSP_RESULT_OK = 200, | { | ||||||
|  |   RTSP_RESULT_OK = 200, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct rtsp_client { | struct rtsp_client | ||||||
| 	char *content_base; | { | ||||||
| 	char *control; |   char* content_base; | ||||||
| 	char session_id[64]; |   char* control; | ||||||
| 	uint16_t stream_id; |   char session_id[64]; | ||||||
| 	int keepalive_interval; |   uint16_t stream_id; | ||||||
|  |   int keepalive_interval; | ||||||
|  |  | ||||||
| 	char udp_address[UDP_ADDRESS_LEN]; |   char udp_address[UDP_ADDRESS_LEN]; | ||||||
| 	uint16_t udp_port; |   uint16_t udp_port; | ||||||
|  |  | ||||||
| 	Socket tcp_sock; |   Socket tcp_sock; | ||||||
| 	Socket udp_sock; |   Socket udp_sock; | ||||||
| 	Socket rtcp_sock; |   Socket rtcp_sock; | ||||||
|  |  | ||||||
| 	enum rtsp_state state; |   enum rtsp_state state; | ||||||
| 	int cseq; |   int cseq; | ||||||
|  |  | ||||||
| 	size_t fifo_size; |   size_t fifo_size; | ||||||
| 	uint16_t last_seq_nr; |   uint16_t last_seq_nr; | ||||||
|  |  | ||||||
| 	string name; |   string name; | ||||||
| 	int level; |   int level; | ||||||
| 	int quality; |   int quality; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct url { | struct url | ||||||
| 	string protocol; | { | ||||||
| 	string host; |   string protocol; | ||||||
| 	int port; |   string host; | ||||||
| 	string path; |   int port; | ||||||
|  |   string path; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct rtcp_app { | struct rtcp_app | ||||||
| 	uint8_t subtype; | { | ||||||
| 	uint8_t pt; |   uint8_t subtype; | ||||||
| 	uint16_t len; |   uint8_t pt; | ||||||
| 	uint32_t ssrc; |   uint16_t len; | ||||||
| 	char name[4]; |   uint32_t ssrc; | ||||||
| 	uint16_t identifier; |   char name[4]; | ||||||
| 	uint16_t string_len; |   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 url parse_url(const std::string& str) | ||||||
| 	static const string prot_end = "://"; | { | ||||||
| 	static const string host_end = "/"; |   static const string prot_end = "://"; | ||||||
| 	url result; |   static const string host_end = "/"; | ||||||
|  |   url result; | ||||||
|  |  | ||||||
| 	string::const_iterator begin = str.begin(); |   string::const_iterator begin = str.begin(); | ||||||
| 	string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); |   string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); | ||||||
| 	result.protocol.reserve(distance(begin, end)); |   result.protocol.reserve(distance(begin, end)); | ||||||
| 	transform(begin, end, back_inserter(result.protocol), ::tolower); |   transform(begin, end, back_inserter(result.protocol), ::tolower); | ||||||
| 	advance(end, prot_end.size()); |   advance(end, prot_end.size()); | ||||||
| 	begin = end; |   begin = end; | ||||||
|  |  | ||||||
| 	end = search(begin, str.end(), host_end.begin(), host_end.end()); |   end = search(begin, str.end(), host_end.begin(), host_end.end()); | ||||||
| 	result.host.reserve(distance(begin, end)); |   result.host.reserve(distance(begin, end)); | ||||||
| 	transform(begin, end, back_inserter(result.host), ::tolower); |   transform(begin, end, back_inserter(result.host), ::tolower); | ||||||
| 	advance(end, host_end.size()); |   advance(end, host_end.size()); | ||||||
| 	begin = end; |   begin = end; | ||||||
|  |  | ||||||
| 	result.port = RTSP_DEFAULT_PORT; |   result.port = RTSP_DEFAULT_PORT; | ||||||
|  |  | ||||||
| 	result.path.reserve(distance(begin, str.end())); |   result.path.reserve(distance(begin, str.end())); | ||||||
| 	transform(begin, str.end(), back_inserter(result.path), ::tolower); |   transform(begin, str.end(), back_inserter(result.path), ::tolower); | ||||||
|  |  | ||||||
| 	return result; |   return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| void split_string(const string& s, char delim, vector<string>& elems) { | void split_string(const string& s, char delim, vector<string>& elems) | ||||||
| 	stringstream ss; | { | ||||||
| 	ss.str(s); |   stringstream ss; | ||||||
|  |   ss.str(s); | ||||||
|  |  | ||||||
| 	string item; |   string item; | ||||||
| 	while(getline(ss, item, delim)) { |   while (getline(ss, item, delim)) | ||||||
| 		elems.push_back(item); |   { | ||||||
| 	} |     elems.push_back(item); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int tcp_sock_read_line(string &line) { | static int tcp_sock_read_line(string& line) | ||||||
| 	static string buf; | { | ||||||
|  |   static string buf; | ||||||
|  |  | ||||||
| 	while(true) { |   while (true) | ||||||
| 		string::size_type pos = buf.find("\r\n"); |   { | ||||||
| 		if(pos != string::npos) { |     string::size_type pos = buf.find("\r\n"); | ||||||
| 			line = buf.substr(0, pos); |     if (pos != string::npos) | ||||||
| 			buf.erase(0, pos + 2); |     { | ||||||
| 			return 0; |       line = buf.substr(0, pos); | ||||||
| 		} |       buf.erase(0, pos + 2); | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
| 		char tmp_buf[2048]; |     char tmp_buf[2048]; | ||||||
| 		int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); |     int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); | ||||||
| 		if(size <= 0) { |     if (size <= 0) | ||||||
| 			return 1; |     { | ||||||
| 		} |       return 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
| 		buf.append(&tmp_buf[0], &tmp_buf[size]); |     buf.append(&tmp_buf[0], &tmp_buf[size]); | ||||||
| 	} |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| static string compose_url(const url& u) | static string compose_url(const url& u) | ||||||
| { | { | ||||||
| 	stringstream res; |   stringstream res; | ||||||
| 	res << u.protocol << "://" << u.host; |   res << u.protocol << "://" << u.host; | ||||||
| 	if (u.port > 0) |   if (u.port > 0) | ||||||
| 		res << ":" << u.port; |     res << ":" << u.port; | ||||||
| 	res << "/" << u.path; |   res << "/" << u.path; | ||||||
|  |  | ||||||
| 	return res.str(); |   return res.str(); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void parse_session(char *request_line, char *session, unsigned max, int *timeout) { | static void parse_session(char* request_line, char* session, unsigned max, int* timeout) | ||||||
| 	char *state; |  | ||||||
| 	char *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) |  | ||||||
| { | { | ||||||
| 	int p = atoi(str); |   char* state; | ||||||
| 	if (p < 0 || p > UINT16_MAX) |   char* tok; | ||||||
| 		return -1; |  | ||||||
|  |  | ||||||
| 	*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) { | static int parse_port(char* str, uint16_t* port) | ||||||
| 	char *state; | { | ||||||
| 	char *tok; |   int p = atoi(str); | ||||||
| 	int err; |   if (p < 0 || p > UINT16_MAX) | ||||||
|  |     return -1; | ||||||
|  |  | ||||||
| 	tok = strtok_r(request_line, ";", &state); |   *port = p; | ||||||
| 	if (tok == NULL || strncmp(tok, "RTP/AVP", 7) != 0) |  | ||||||
| 		return -1; |  | ||||||
|  |  | ||||||
| 	tok = strtok_r(NULL, ";", &state); |   return 0; | ||||||
| 	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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #define skip_whitespace(x) while(*x == ' ') x++ | static int parse_transport(char* request_line) | ||||||
| static enum rtsp_result rtsp_handle() { | { | ||||||
| 	uint8_t buffer[512]; |   char* state; | ||||||
| 	int rtsp_result = 0; |   char* tok; | ||||||
| 	bool have_header = false; |   int err; | ||||||
| 	size_t content_length = 0; |  | ||||||
| 	size_t read = 0; |  | ||||||
| 	char *in, *val; |  | ||||||
| 	string in_str; |  | ||||||
|  |  | ||||||
| 	/* Parse header */ |   tok = strtok_r(request_line, ";", &state); | ||||||
| 	while (!have_header) { |   if (tok == nullptr || strncmp(tok, "RTP/AVP", 7) != 0) | ||||||
| 		if (tcp_sock_read_line(in_str) < 0) |     return -1; | ||||||
| 			break; |  | ||||||
| 		in = const_cast<char *>(in_str.c_str()); |  | ||||||
|  |  | ||||||
| 		if (strncmp(in, "RTSP/1.0 ", 9) == 0) { |   tok = strtok_r(nullptr, ";", &state); | ||||||
| 			rtsp_result = atoi(in + 9); |   if (tok == nullptr || strncmp(tok, "multicast", 9) != 0) | ||||||
| 		} else if (strncmp(in, "Content-Base:", 13) == 0) { |     return 0; | ||||||
| 			free(rtsp->content_base); |  | ||||||
|  |  | ||||||
| 			val = in + 13; |   while ((tok = strtok_r(nullptr, ";", &state)) != nullptr) | ||||||
| 			skip_whitespace(val); |   { | ||||||
|  |     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); |       memset(port, 0x00, 6); | ||||||
| 		} else if (strncmp(in, "Content-Length:", 15) == 0) { |       strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5)); | ||||||
| 			val = in + 16; |       if ((end = strstr(port, "-")) != nullptr) | ||||||
| 			skip_whitespace(val); |         *end = '\0'; | ||||||
|  |       err = parse_port(port, &rtsp->udp_port); | ||||||
|  |       if (err) | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
| 			content_length = atoi(val); |   return 0; | ||||||
| 		} else if (strncmp("Session:", in, 8) == 0) { | } | ||||||
| 			val = in + 8; |  | ||||||
| 			skip_whitespace(val); |  | ||||||
|  |  | ||||||
| 			parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval); | #define skip_whitespace(x) \ | ||||||
| 		} else if (strncmp("Transport:", in, 10) == 0) { |   while (*x == ' ') \ | ||||||
| 			val = in + 10; |   x++ | ||||||
| 			skip_whitespace(val); | 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) { |   /* Parse header */ | ||||||
| 				rtsp_result = -1; |   while (!have_header) | ||||||
| 				break; |   { | ||||||
| 			} |     if (tcp_sock_read_line(in_str) < 0) | ||||||
| 		} else if (strncmp("com.ses.streamID:", in, 17) == 0) { |       break; | ||||||
| 			val = in + 17; |     in = const_cast<char*>(in_str.c_str()); | ||||||
| 			skip_whitespace(val); |  | ||||||
|  |  | ||||||
| 			rtsp->stream_id = atoi(val); |     if (strncmp(in, "RTSP/1.0 ", 9) == 0) | ||||||
| 		} else if (in[0] == '\0') { |     { | ||||||
| 			have_header = true; |       rtsp_result = atoi(in + 9); | ||||||
| 		} |     } | ||||||
| 	} |     else if (strncmp(in, "Content-Base:", 13) == 0) | ||||||
|  |     { | ||||||
|  |       free(rtsp->content_base); | ||||||
|  |  | ||||||
| 	/* Discard further content */ |       val = in + 13; | ||||||
| 	while (content_length > 0 && |       skip_whitespace(val); | ||||||
| 			(read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer), min(sizeof(buffer), content_length)))) |  | ||||||
| 		content_length -= read; |  | ||||||
|  |  | ||||||
| 	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) | bool rtsp_open(const string& name, const string& url_str) | ||||||
| { | { | ||||||
| 	string setup_url_str; |   string setup_url_str; | ||||||
| 	const char *psz_setup_url; |   const char* psz_setup_url; | ||||||
| 	stringstream setup_ss; |   stringstream setup_ss; | ||||||
| 	stringstream play_ss; |   stringstream play_ss; | ||||||
| 	url setup_url; |   url setup_url; | ||||||
|  |  | ||||||
| 	rtsp_close(); |   rtsp_close(); | ||||||
| 	rtsp = new rtsp_client(); |   rtsp = new rtsp_client(); | ||||||
| 	if (rtsp == NULL) |   if (rtsp == nullptr) | ||||||
| 		return false; |     return false; | ||||||
|  |  | ||||||
| 	rtsp->name = name; |   rtsp->name = name; | ||||||
| 	rtsp->level = 0; |   rtsp->level = 0; | ||||||
| 	rtsp->quality = 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); |   url dst = parse_url(url_str); | ||||||
| 	libKodi->Log(LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); |   kodi::Log(ADDON_LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); | ||||||
|  |  | ||||||
| 	if(!rtsp->tcp_sock.connect(dst.host, dst.port)) { |   if (!rtsp->tcp_sock.connect(dst.host, dst.port)) | ||||||
| 		libKodi->Log(LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(), dst.port); |   { | ||||||
| 		goto error; |     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(), |   if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), dst.port) < 0) | ||||||
| 				dst.port) < 0) { |   { | ||||||
| 		rtsp->content_base = NULL; |     rtsp->content_base = nullptr; | ||||||
| 		goto error; |     goto error; | ||||||
| 	} |   } | ||||||
|  |  | ||||||
| 	rtsp->last_seq_nr = 0; |   rtsp->last_seq_nr = 0; | ||||||
| 	rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN); |   rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN); | ||||||
|  |  | ||||||
| 	setup_url = dst; |   setup_url = dst; | ||||||
|  |  | ||||||
| 	// reverse the satip protocol trick, as SAT>IP believes to be RTSP |   // reverse the satip protocol trick, as SAT>IP believes to be RTSP | ||||||
| 	if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) { |   if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) | ||||||
| 		setup_url.protocol = "rtsp"; |   { | ||||||
| 	} |     setup_url.protocol = "rtsp"; | ||||||
|  |   } | ||||||
|  |  | ||||||
| 	setup_url_str = compose_url(setup_url); |   setup_url_str = compose_url(setup_url); | ||||||
| 	psz_setup_url = setup_url_str.c_str(); |   psz_setup_url = setup_url_str.c_str(); | ||||||
|  |  | ||||||
| 	// TODO: Find available port |   // TODO: Find available port | ||||||
| 	rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); |   rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | ||||||
| 	rtsp->udp_port = 6785; |   rtsp->udp_port = 6785; | ||||||
| 	if(!rtsp->udp_sock.bind(rtsp->udp_port)) { |   if (!rtsp->udp_sock.bind(rtsp->udp_port)) | ||||||
| 		goto error; |   { | ||||||
| 	} |     goto error; | ||||||
|  |   } | ||||||
|  |  | ||||||
| 	setup_ss << "SETUP " << setup_url_str<< " RTSP/1.0\r\n"; |   setup_ss << "SETUP " << setup_url_str << " RTSP/1.0\r\n"; | ||||||
| 	setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; |   setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||||
| 	setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-" << (rtsp->udp_port + 1) << "\r\n\r\n"; |   setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-" | ||||||
| 	rtsp->tcp_sock.send(setup_ss.str()); |            << (rtsp->udp_port + 1) << "\r\n\r\n"; | ||||||
|  |   rtsp->tcp_sock.send(setup_ss.str()); | ||||||
|  |  | ||||||
| 	if (rtsp_handle() != RTSP_RESULT_OK) { |   if (rtsp_handle() != RTSP_RESULT_OK) | ||||||
| 		libKodi->Log(LOG_ERROR, "Failed to setup RTSP session"); |   { | ||||||
| 		goto error; |     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) { |   if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0) | ||||||
| 		rtsp->control = NULL; |   { | ||||||
| 		goto error; |     rtsp->control = nullptr; | ||||||
| 	} |     goto error; | ||||||
|  |   } | ||||||
|  |  | ||||||
| 	play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; |   play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; | ||||||
| 	play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; |   play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||||
| 	play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; |   play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | ||||||
| 	rtsp->tcp_sock.send(play_ss.str()); |   rtsp->tcp_sock.send(play_ss.str()); | ||||||
|  |  | ||||||
| 	if (rtsp_handle() != RTSP_RESULT_OK) { |   if (rtsp_handle() != RTSP_RESULT_OK) | ||||||
| 		libKodi->Log(LOG_ERROR, "Failed to play RTSP session"); |   { | ||||||
| 		goto error; |     kodi::Log(ADDON_LOG_ERROR, "Failed to play RTSP session"); | ||||||
| 	} |     goto error; | ||||||
|  |   } | ||||||
|  |  | ||||||
| 	rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); |   rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); | ||||||
| 	if(!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) { |   if (!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) | ||||||
| 		goto error; |   { | ||||||
| 	} |     goto error; | ||||||
| 	if(!rtsp->rtcp_sock.set_non_blocking(true)) { |   } | ||||||
| 		goto error; |   if (!rtsp->rtcp_sock.set_non_blocking(true)) | ||||||
| 	} |   { | ||||||
|  |     goto error; | ||||||
|  |   } | ||||||
|  |  | ||||||
| 	return true; |   return true; | ||||||
|  |  | ||||||
| error: | error: | ||||||
| 	rtsp_close(); |   rtsp_close(); | ||||||
| 	return false; |   return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void parse_rtcp(const char *buf, int size) { | static void parse_rtcp(const char* buf, int size) | ||||||
| 	int offset = 0; | { | ||||||
| 	while(size > 4) { |   int offset = 0; | ||||||
| 		const rtcp_app *app = reinterpret_cast<const rtcp_app *>(buf + offset); |   while (size > 4) | ||||||
| 		uint16_t len = 4 * (ntohs(app->len) + 1); |   { | ||||||
|  |     const rtcp_app* app = reinterpret_cast<const rtcp_app*>(buf + offset); | ||||||
|  |     uint16_t len = 4 * (ntohs(app->len) + 1); | ||||||
|  |  | ||||||
| 		if((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0)) { |     if ((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0)) | ||||||
| 			size -= len; |     { | ||||||
| 			offset += len; |       size -= len; | ||||||
| 			continue; |       offset += len; | ||||||
| 		} |       continue; | ||||||
|  |     } | ||||||
|  |  | ||||||
| 		uint16_t string_len = ntohs(app->string_len); |     uint16_t string_len = ntohs(app->string_len); | ||||||
| 		string app_data(&buf[offset + sizeof(rtcp_app)], string_len); |     string app_data(&buf[offset + sizeof(rtcp_app)], string_len); | ||||||
|  |  | ||||||
| 		vector<string> elems; |     vector<string> elems; | ||||||
| 		split_string(app_data, ';', elems); |     split_string(app_data, ';', elems); | ||||||
| 		if(elems.size() != 4) { |     if (elems.size() != 4) | ||||||
| 			return; |     { | ||||||
| 		} |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
| 		vector<string> tuner; |     vector<string> tuner; | ||||||
| 		split_string(elems[2], ',', tuner); |     split_string(elems[2], ',', tuner); | ||||||
| 		if(tuner.size() < 4) { |     if (tuner.size() < 4) | ||||||
| 			return; |     { | ||||||
| 		} |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
| 		rtsp->level = atoi(tuner[1].c_str()); |     rtsp->level = atoi(tuner[1].c_str()); | ||||||
| 		rtsp->quality = atoi(tuner[3].c_str()); |     rtsp->quality = atoi(tuner[3].c_str()); | ||||||
|  |  | ||||||
| 		return; |     return; | ||||||
| 	} |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| int rtsp_read(void *buf, unsigned buf_size) { | int rtsp_read(void* buf, unsigned buf_size) | ||||||
| 	sockaddr addr; | { | ||||||
| 	socklen_t addr_len = sizeof(addr); |   sockaddr addr; | ||||||
| 	int ret = rtsp->udp_sock.recvfrom((char *)buf, buf_size, (sockaddr *)&addr, &addr_len); |   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]; |   char rtcp_buf[RTCP_BUFFER_SIZE]; | ||||||
| 	int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr *)&addr, &addr_len); |   int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr*)&addr, &addr_len); | ||||||
| 	parse_rtcp(rtcp_buf, rtcp_len); |   parse_rtcp(rtcp_buf, rtcp_len); | ||||||
|  |  | ||||||
| 	// TODO: check ip |   // TODO: check ip | ||||||
|  |  | ||||||
| 	return ret; |   return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void rtsp_teardown() { | static void rtsp_teardown() | ||||||
| 	if(!rtsp->tcp_sock.is_valid()) { | { | ||||||
| 		return; |   if (!rtsp->tcp_sock.is_valid()) | ||||||
| 	} |   { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
| 	if (rtsp->session_id[0] > 0) { |   if (rtsp->session_id[0] > 0) | ||||||
| 		char *msg; |   { | ||||||
| 		int len; |     char* msg; | ||||||
| 		stringstream ss; |     int len; | ||||||
|  |     stringstream ss; | ||||||
|  |  | ||||||
| 		rtsp->udp_sock.close(); |     rtsp->udp_sock.close(); | ||||||
|  |  | ||||||
| 		ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; |     ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; | ||||||
| 		ss << "CSeq: " << rtsp->cseq++ << "\r\n"; |     ss << "CSeq: " << rtsp->cseq++ << "\r\n"; | ||||||
| 		ss << "Session: " << rtsp->session_id << "\r\n\r\n"; |     ss << "Session: " << rtsp->session_id << "\r\n\r\n"; | ||||||
| 		rtsp->tcp_sock.send(ss.str()); |     rtsp->tcp_sock.send(ss.str()); | ||||||
|  |  | ||||||
| 		if (rtsp_handle() != RTSP_RESULT_OK) { |     if (rtsp_handle() != RTSP_RESULT_OK) | ||||||
| 			libKodi->Log(LOG_ERROR, "Failed to teardown RTSP session"); |     { | ||||||
| 			return; |       kodi::Log(ADDON_LOG_ERROR, "Failed to teardown RTSP session"); | ||||||
| 		} |       return; | ||||||
| 	} |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void rtsp_close() | void rtsp_close() | ||||||
| { | { | ||||||
| 	if(rtsp) { |   if (rtsp) | ||||||
| 		rtsp_teardown(); |   { | ||||||
| 		rtsp->tcp_sock.close(); |     rtsp_teardown(); | ||||||
| 		rtsp->udp_sock.close(); |     rtsp->tcp_sock.close(); | ||||||
| 		rtsp->rtcp_sock.close(); |     rtsp->udp_sock.close(); | ||||||
| 		delete rtsp; |     rtsp->rtcp_sock.close(); | ||||||
| 		rtsp = NULL; |     delete rtsp; | ||||||
| 	} |     rtsp = nullptr; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void rtsp_fill_signal_status(PVR_SIGNAL_STATUS& signal_status) { | void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status) | ||||||
| 	if(rtsp) { | { | ||||||
| 		strncpy(signal_status.strServiceName, rtsp->name.c_str(), PVR_ADDON_NAME_STRING_LENGTH - 1); |   if (rtsp) | ||||||
| 		signal_status.iSNR = 0x1111 * rtsp->quality; |   { | ||||||
| 		signal_status.iSignal = 0x101 * rtsp->level; |     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 <string> | ||||||
| #include <kodi/xbmc_pvr_types.h> |  | ||||||
|  |  | ||||||
| bool rtsp_open(const std::string& name, const std::string& url_str); | bool rtsp_open(const std::string& name, const std::string& url_str); | ||||||
| void rtsp_close(); | void rtsp_close(); | ||||||
| int rtsp_read(void *buf, unsigned buf_size); | int rtsp_read(void* buf, unsigned buf_size); | ||||||
| void rtsp_fill_signal_status(PVR_SIGNAL_STATUS& signal_status); | void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status); | ||||||
|  |  | ||||||
| #endif |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user