mirror of
				https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
				synced 2023-10-10 17:16:51 +00:00 
			
		
		
		
	Added IGMP multicast server
Modified Files: CONTRIBUTORS HISTORY Makefile README po/de_DE.po po/fi_FI.po po/fr_FR.po po/it_IT.po po/ru_RU.po server/component.c server/component.h server/connection.c server/connection.h server/livefilter.c server/server.c server/setup.c server/setup.h server/streamer.c server/streamer.h streamdev/streamdevhosts.conf tools/socket.c tools/socket.h Added Files: patches/vdr-cap_net_raw.diff server/componentIGMP.c server/componentIGMP.h server/connectionIGMP.c server/connectionIGMP.h
This commit is contained in:
		| @@ -108,3 +108,6 @@ owagner | |||||||
|  |  | ||||||
| Joachim K<>nig-Baltes | Joachim K<>nig-Baltes | ||||||
|   for fixing Min/MaxPriority parsing |   for fixing Min/MaxPriority parsing | ||||||
|  |  | ||||||
|  | Artem Makhutov | ||||||
|  |   for suggesting and heavy testing IGMP based multicast streaming | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								HISTORY
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								HISTORY
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| VDR Plugin 'streamdev' Revision History | VDR Plugin 'streamdev' Revision History | ||||||
| --------------------------------------- | --------------------------------------- | ||||||
|  |  | ||||||
|  | - added IGMP based multicast streaming | ||||||
| - ignore trailing blank lines in HTTP requests | - ignore trailing blank lines in HTTP requests | ||||||
| - fixed parsing Min/MaxPriority from config (thanks to Joachim K<>nig-Baltes) | - fixed parsing Min/MaxPriority from config (thanks to Joachim K<>nig-Baltes) | ||||||
| - updated Finnish translation (thanks to Rolf Ahrenberg) | - updated Finnish translation (thanks to Rolf Ahrenberg) | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| # | # | ||||||
| # Makefile for a Video Disk Recorder plugin | # Makefile for a Video Disk Recorder plugin | ||||||
| # | # | ||||||
| # $Id: Makefile,v 1.16 2008/04/08 14:18:15 schmirl Exp $ | # $Id: Makefile,v 1.17 2009/02/13 10:39:20 schmirl Exp $ | ||||||
|  |  | ||||||
| # The official name of this plugin. | # The official name of this plugin. | ||||||
| # This name will be used in the '-P...' option of VDR to load the plugin. | # This name will be used in the '-P...' option of VDR to load the plugin. | ||||||
| @@ -57,11 +57,11 @@ CLIENTOBJS = $(PLUGIN)-client.o \ | |||||||
|  |  | ||||||
| SERVEROBJS = $(PLUGIN)-server.o \ | SERVEROBJS = $(PLUGIN)-server.o \ | ||||||
| 	\ | 	\ | ||||||
| 	server/server.o server/connectionVTP.o server/connectionHTTP.o \ | 	server/server.o server/component.o server/connection.o \ | ||||||
| 	server/componentHTTP.o server/componentVTP.o server/connection.o \ | 	server/componentVTP.o server/componentHTTP.o server/componentIGMP.o \ | ||||||
| 	server/component.o server/suspend.o server/setup.o server/streamer.o \ | 	server/connectionVTP.o server/connectionHTTP.o server/connectionIGMP.o \ | ||||||
| 	server/livestreamer.o server/livefilter.o server/menuHTTP.o \ | 	server/streamer.o server/livestreamer.o server/livefilter.o \ | ||||||
| 	\ | 	server/suspend.o server/setup.o server/menuHTTP.o \ | ||||||
| 	remux/tsremux.o remux/ts2ps.o remux/ts2es.o remux/extern.o | 	remux/tsremux.o remux/ts2ps.o remux/ts2es.o remux/extern.o | ||||||
| 	 | 	 | ||||||
| ifdef DEBUG | ifdef DEBUG | ||||||
|   | |||||||
							
								
								
									
										68
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								README
									
									
									
									
									
								
							| @@ -20,8 +20,9 @@ Contents: | |||||||
| 2.3  Updating from streamdev 0.3.x | 2.3  Updating from streamdev 0.3.x | ||||||
| 3.   Usage | 3.   Usage | ||||||
| 3.1  Usage HTTP server | 3.1  Usage HTTP server | ||||||
| 3.2  Usage VDR-to-VDR server | 3.2  Usage IGMP multicast server | ||||||
| 3.3  Usage VDR-to-VDR client | 3.3  Usage VDR-to-VDR server | ||||||
|  | 3.4  Usage VDR-to-VDR client | ||||||
| 4.   Other useful Plugins | 4.   Other useful Plugins | ||||||
| 4.1  Plugins for VDR-to-VDR clients | 4.1  Plugins for VDR-to-VDR clients | ||||||
| 4.2  Plugins for Server | 4.2  Plugins for Server | ||||||
| @@ -221,7 +222,66 @@ Note the single quotes, as otherwise "-a" will be passed to VDR and not to | |||||||
| streamdev-server. The login ("vdr" in the example above) doesn't have to exist | streamdev-server. The login ("vdr" in the example above) doesn't have to exist | ||||||
| as a system account. | as a system account. | ||||||
|  |  | ||||||
| 3.2 Usage VDR-to-VDR server: | 3.2 Usage IGMP multicast server: | ||||||
|  | -------------------------------- | ||||||
|  |  | ||||||
|  | IGMP based multicast streaming is often used by settop boxes to receive IP TV. | ||||||
|  | Streamdev's multicast server allows you to feed live TV from VDR to such a | ||||||
|  | settop box. VLC is known to work well if you look for a software client. | ||||||
|  |  | ||||||
|  | The advantage of multicasting is that the actual stream is sent out only once, | ||||||
|  | regardless of how many clients want to receive it. The downside is, that you | ||||||
|  | cannot simply multicast across network boundaries. You need multicast routers. | ||||||
|  | For multicast streaming over the public Internet you would even need to register | ||||||
|  | for your own IP range. So don't even think of multicasting via Internet with | ||||||
|  | streamdev! Streamdev will send the stream only to one local ethernet segment and | ||||||
|  | all clients must be connected to this same segment. There must not be a router | ||||||
|  | inbetween. Also note that the client must not run on the streamdev-server | ||||||
|  | machine. | ||||||
|  |  | ||||||
|  | Each channel is offered on a different multicast IP. Channel 1 is available from | ||||||
|  | multicast IP 239.255.0.1, channel 2 from 239.255.0.2 and so on. The upper limit | ||||||
|  | is 239.255.254.255 which corresponds to channel 65279 (239.255.255.0/24 is | ||||||
|  | reserved according to RFC-2365). | ||||||
|  |  | ||||||
|  | Before you can use streamdev's multicast server, you might need to patch VDR. | ||||||
|  | Binding an IGMP socket is a privileged operation, so you must start VDR as root. | ||||||
|  | If you pass the -u option to VDR, it will drop almost all priviledges before | ||||||
|  | streamdev is even loaded. Apply vdr-cap_net_raw.diff to keep VDR from dropping | ||||||
|  | the CAP_NET_RAW capability required to bind the IGMP socket. The patch is part | ||||||
|  | of streamdev's source distribution. Check the patches subdirectory. There's no | ||||||
|  | need to patch VDR if it is kept running as root (not recommended). | ||||||
|  |  | ||||||
|  | The multicast server is disabled by default. Enter the streamdev-server setup | ||||||
|  | menu to enable it and - IMPORTANT - bind the multicast server to the IP of your | ||||||
|  | VDR server's LAN ethernet card. The multicast server will refuse to start with | ||||||
|  | the default bind adresse "0.0.0.0". | ||||||
|  |  | ||||||
|  | Now edit your streamdevhosts.conf. To allow streaming of all channels, it must | ||||||
|  | contain "239.255.0.0/16". Note that you cannot limit connections by client IP | ||||||
|  | here. You can however restrict which channels are allowed to be multicasted. | ||||||
|  | Enter individual multicast IPs instead of "239.255.0.0/16". | ||||||
|  |  | ||||||
|  | By default, the linux kernel will refuse to join more than 20 multicast groups. | ||||||
|  | You might want to increase this up to "number_of_channels + 1". Note that it's | ||||||
|  | "number_of_channels", not "maximum_channel_number". | ||||||
|  |  | ||||||
|  |   #First 100 channels: | ||||||
|  |   bash# sysctl -w sys.net.ipv4.igmp_max_memberships=101 | ||||||
|  |  | ||||||
|  |   #All channels: | ||||||
|  |   bash# COUNT=$(grep -c '^[^:]' PATH_TO_YOUR/channels.conf) | ||||||
|  |   bash# sysctl -w sys.net.ipv4.igmp_max_memberships=$COUNT | ||||||
|  |  | ||||||
|  | A multicast server never knows how many clients are actually receiving a stream. | ||||||
|  | If a client signals that it leaves a multicast group, the server has to query | ||||||
|  | for other listeners before it can stop the stream. This may delay zapping from | ||||||
|  | one transponder to an other. The client will probably requests the new channel | ||||||
|  | before the previous stream has been stopped. If there's no free DVB card, VDR | ||||||
|  | won't be able to fulfill the request until a DVB card becomes available and the | ||||||
|  | client resends the request. | ||||||
|  |  | ||||||
|  | 3.3 Usage VDR-to-VDR server: | ||||||
| ---------------------------- | ---------------------------- | ||||||
|  |  | ||||||
| You can activate the VDR-to-VDR server part in the PlugIn's Setup Menu. It is | You can activate the VDR-to-VDR server part in the PlugIn's Setup Menu. It is | ||||||
| @@ -230,7 +290,7 @@ port where you want the server to listen for incoming connections. The server | |||||||
| will be activated when you push the OK button inside the setup menu, so there's | will be activated when you push the OK button inside the setup menu, so there's | ||||||
| no need to restart VDR. | no need to restart VDR. | ||||||
|  |  | ||||||
| 3.3 Usage VDR-to-VDR client: | 3.4 Usage VDR-to-VDR client: | ||||||
| ---------------------------- | ---------------------------- | ||||||
|  |  | ||||||
| Streamdev-client adds a "Suspend Server" item to VDR's mainmenu. With the | Streamdev-client adds a "Suspend Server" item to VDR's mainmenu. With the | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								patches/vdr-cap_net_raw.diff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								patches/vdr-cap_net_raw.diff
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | --- vdr.c.orig	2009-02-13 09:45:55.000000000 +0100 | ||||||
|  | +++ vdr.c	2009-02-13 09:46:24.000000000 +0100 | ||||||
|  | @@ -115,7 +115,7 @@ | ||||||
|  |  static bool SetCapSysTime(void) | ||||||
|  |  { | ||||||
|  |    // drop all capabilities except cap_sys_time | ||||||
|  | -  cap_t caps = cap_from_text("= cap_sys_time=ep"); | ||||||
|  | +  cap_t caps = cap_from_text("= cap_sys_time,cap_net_raw=ep"); | ||||||
|  |    if (!caps) { | ||||||
|  |       fprintf(stderr, "vdr: cap_from_text failed: %s\n", strerror(errno)); | ||||||
|  |       return false; | ||||||
							
								
								
									
										14
									
								
								po/de_DE.po
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								po/de_DE.po
									
									
									
									
									
								
							| @@ -7,7 +7,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: streamdev 0.5.0\n" | "Project-Id-Version: streamdev 0.5.0\n" | ||||||
| "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | ||||||
| "POT-Creation-Date: 2009-01-31 13:34+0200\n" | "POT-Creation-Date: 2009-02-13 11:53+0100\n" | ||||||
| "PO-Revision-Date: 2008-03-30 02:11+0200\n" | "PO-Revision-Date: 2008-03-30 02:11+0200\n" | ||||||
| "Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n" | "Last-Translator: Frank Schmirler <vdrdev@schmirler.de>\n" | ||||||
| "Language-Team: <vdr@linuxtv.org>\n" | "Language-Team: <vdr@linuxtv.org>\n" | ||||||
| @@ -96,6 +96,18 @@ msgstr "Port des HTTP Servers" | |||||||
| msgid "HTTP Streamtype" | msgid "HTTP Streamtype" | ||||||
| msgstr "HTTP Streamtyp" | msgstr "HTTP Streamtyp" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streaming Server" | ||||||
|  | msgstr "Multicast Streaming Server" | ||||||
|  |  | ||||||
|  | msgid "Start IGMP Server" | ||||||
|  | msgstr "IGMP Server starten" | ||||||
|  |  | ||||||
|  | msgid "Multicast Client Port" | ||||||
|  | msgstr "Port des Multicast Clients" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streamtype" | ||||||
|  | msgstr "Multicast Streamtyp" | ||||||
|  |  | ||||||
| msgid "Offer suspend mode" | msgid "Offer suspend mode" | ||||||
| msgstr "Pausieren anbieten" | msgstr "Pausieren anbieten" | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								po/fi_FI.po
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								po/fi_FI.po
									
									
									
									
									
								
							| @@ -7,7 +7,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: streamdev 0.5.0\n" | "Project-Id-Version: streamdev 0.5.0\n" | ||||||
| "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | ||||||
| "POT-Creation-Date: 2009-01-31 13:34+0200\n" | "POT-Creation-Date: 2009-02-13 11:53+0100\n" | ||||||
| "PO-Revision-Date: 2008-03-30 02:11+0200\n" | "PO-Revision-Date: 2008-03-30 02:11+0200\n" | ||||||
| "Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n" | "Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n" | ||||||
| "Language-Team: <vdr@linuxtv.org>\n" | "Language-Team: <vdr@linuxtv.org>\n" | ||||||
| @@ -96,6 +96,18 @@ msgstr "HTTP-palvelimen portti" | |||||||
| msgid "HTTP Streamtype" | msgid "HTTP Streamtype" | ||||||
| msgstr "HTTP-l<>hetysmuoto" | msgstr "HTTP-l<>hetysmuoto" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streaming Server" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Start IGMP Server" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Multicast Client Port" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streamtype" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| msgid "Offer suspend mode" | msgid "Offer suspend mode" | ||||||
| msgstr "tyrkyt<79>" | msgstr "tyrkyt<79>" | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								po/fr_FR.po
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								po/fr_FR.po
									
									
									
									
									
								
							| @@ -7,7 +7,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: streamdev 0.5.0\n" | "Project-Id-Version: streamdev 0.5.0\n" | ||||||
| "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | ||||||
| "POT-Creation-Date: 2009-01-31 13:34+0200\n" | "POT-Creation-Date: 2009-02-13 11:53+0100\n" | ||||||
| "PO-Revision-Date: 2008-03-30 02:11+0200\n" | "PO-Revision-Date: 2008-03-30 02:11+0200\n" | ||||||
| "Last-Translator: micky979 <micky979@free.fr>\n" | "Last-Translator: micky979 <micky979@free.fr>\n" | ||||||
| "Language-Team: <vdr@linuxtv.org>\n" | "Language-Team: <vdr@linuxtv.org>\n" | ||||||
| @@ -96,6 +96,18 @@ msgstr "Port du serveur HTTP" | |||||||
| msgid "HTTP Streamtype" | msgid "HTTP Streamtype" | ||||||
| msgstr "Type de Streaming HTTP" | msgstr "Type de Streaming HTTP" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streaming Server" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Start IGMP Server" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Multicast Client Port" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streamtype" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| msgid "Offer suspend mode" | msgid "Offer suspend mode" | ||||||
| msgstr "Offrir le mode suspendre" | msgstr "Offrir le mode suspendre" | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								po/it_IT.po
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								po/it_IT.po
									
									
									
									
									
								
							| @@ -9,7 +9,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: streamdev 0.5.0\n" | "Project-Id-Version: streamdev 0.5.0\n" | ||||||
| "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | ||||||
| "POT-Creation-Date: 2008-04-10 01:16+0200\n" | "POT-Creation-Date: 2009-02-13 11:53+0100\n" | ||||||
| "PO-Revision-Date: 2008-04-13 23:42+0100\n" | "PO-Revision-Date: 2008-04-13 23:42+0100\n" | ||||||
| "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" | "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" | ||||||
| "Language-Team:  <vdr@linuxtv.org>\n" | "Language-Team:  <vdr@linuxtv.org>\n" | ||||||
| @@ -98,6 +98,18 @@ msgstr "Porta Server HTTP" | |||||||
| msgid "HTTP Streamtype" | msgid "HTTP Streamtype" | ||||||
| msgstr "Tipo flusso HTTP" | msgstr "Tipo flusso HTTP" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streaming Server" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Start IGMP Server" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Multicast Client Port" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streamtype" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| msgid "Offer suspend mode" | msgid "Offer suspend mode" | ||||||
| msgstr "Offri mod. sospensione" | msgstr "Offri mod. sospensione" | ||||||
|  |  | ||||||
| @@ -106,4 +118,3 @@ msgstr "Sempre sospeso" | |||||||
|  |  | ||||||
| msgid "Never suspended" | msgid "Never suspended" | ||||||
| msgstr "Mai sospeso" | msgstr "Mai sospeso" | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								po/ru_RU.po
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								po/ru_RU.po
									
									
									
									
									
								
							| @@ -7,7 +7,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: streamdev 0.5.0\n" | "Project-Id-Version: streamdev 0.5.0\n" | ||||||
| "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | "Report-Msgid-Bugs-To: <http://www.vdr-developer.org/mantisbt/>\n" | ||||||
| "POT-Creation-Date: 2008-06-26 15:14+0200\n" | "POT-Creation-Date: 2009-02-13 11:53+0100\n" | ||||||
| "PO-Revision-Date: 2008-06-26 15:36+0100\n" | "PO-Revision-Date: 2008-06-26 15:36+0100\n" | ||||||
| "Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n" | "Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n" | ||||||
| "Language-Team: <vdr@linuxtv.org>\n" | "Language-Team: <vdr@linuxtv.org>\n" | ||||||
| @@ -96,6 +96,18 @@ msgstr "HTTP  | |||||||
| msgid "HTTP Streamtype" | msgid "HTTP Streamtype" | ||||||
| msgstr "<22><><EFBFBD> HTTP <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" | msgstr "<22><><EFBFBD> HTTP <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streaming Server" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Start IGMP Server" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Multicast Client Port" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | msgid "Multicast Streamtype" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| msgid "Offer suspend mode" | msgid "Offer suspend mode" | ||||||
| msgstr "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" | msgstr "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,13 +1,14 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: component.c,v 1.3 2005/05/09 20:22:29 lordjaxom Exp $ |  *  $Id: component.c,v 1.4 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| #include "server/component.h" | #include "server/component.h" | ||||||
| #include "server/connection.h" | #include "server/connection.h" | ||||||
|  |  | ||||||
| cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp, | cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp, | ||||||
|                                    uint ListenPort): |                                    uint ListenPort, int Type, int IpProto): | ||||||
| 		m_Protocol(Protocol), | 		m_Protocol(Protocol), | ||||||
|  | 		m_Listen(Type, IpProto), | ||||||
| 		m_ListenIp(ListenIp), | 		m_ListenIp(ListenIp), | ||||||
| 		m_ListenPort(ListenPort) | 		m_ListenPort(ListenPort) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: component.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ |  *  $Id: component.h,v 1.3 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| #ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H | #ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H | ||||||
| @@ -17,8 +17,8 @@ class cServerConnection; | |||||||
|  |  | ||||||
| class cServerComponent: public cListObject { | class cServerComponent: public cListObject { | ||||||
| private: | private: | ||||||
| 	cTBSocket m_Listen; |  | ||||||
| 	const char *m_Protocol; | 	const char *m_Protocol; | ||||||
|  | 	cTBSocket m_Listen; | ||||||
| 	const char *m_ListenIp; | 	const char *m_ListenIp; | ||||||
| 	uint m_ListenPort; | 	uint m_ListenPort; | ||||||
|  |  | ||||||
| @@ -27,7 +27,7 @@ protected: | |||||||
| 	virtual cServerConnection *NewClient(void) = 0; | 	virtual cServerConnection *NewClient(void) = 0; | ||||||
|  |  | ||||||
| public: | public: | ||||||
| 	cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort); | 	cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort, int Type = SOCK_STREAM, int IpProto = 0); | ||||||
| 	virtual ~cServerComponent(); | 	virtual ~cServerComponent(); | ||||||
|  |  | ||||||
| 	/* Starts listening on the specified Port, override if you want to do things | 	/* Starts listening on the specified Port, override if you want to do things | ||||||
|   | |||||||
							
								
								
									
										447
									
								
								server/componentIGMP.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										447
									
								
								server/componentIGMP.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,447 @@ | |||||||
|  | /* | ||||||
|  |  *  $Id: componentIGMP.c,v 1.1 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  |  */ | ||||||
|  | #include <netinet/ip.h> | ||||||
|  | #include <netinet/igmp.h> | ||||||
|  |   | ||||||
|  | #include "server/componentIGMP.h" | ||||||
|  | #include "server/connectionIGMP.h" | ||||||
|  | #include "server/setup.h" | ||||||
|  |  | ||||||
|  | #ifndef IGMP_ALL_HOSTS | ||||||
|  | #define IGMP_ALL_HOSTS htonl(0xE0000001L) | ||||||
|  | #endif | ||||||
|  | #ifndef IGMP_ALL_ROUTER | ||||||
|  | #define IGMP_ALL_ROUTER htonl(0xE0000002L) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // IGMP parameters according to RFC2236. All time values in seconds. | ||||||
|  | #define IGMP_ROBUSTNESS 2 | ||||||
|  | #define IGMP_QUERY_INTERVAL 125 | ||||||
|  | #define IGMP_QUERY_RESPONSE_INTERVAL 10 | ||||||
|  | #define IGMP_GROUP_MEMBERSHIP_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL) | ||||||
|  | #define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL / 2) | ||||||
|  | #define IGMP_STARTUP_QUERY_INTERVAL (IGMP_QUERY_INTERVAL / 4) | ||||||
|  | #define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS | ||||||
|  | // This value is 1/10 sec. RFC default is 10. Reduced to minimum to free unused channels ASAP | ||||||
|  | #define IGMP_LAST_MEMBER_QUERY_INTERVAL_TS 1 | ||||||
|  | #define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS | ||||||
|  |  | ||||||
|  | // operations on struct timeval | ||||||
|  | #define TV_CMP(a, cmp, b) (a.tv_sec == b.tv_sec ? a.tv_usec cmp b.tv_usec : a.tv_sec cmp b.tv_sec) | ||||||
|  | #define TV_SET(tv) (tv.tv_sec || tv.tv_usec) | ||||||
|  | #define TV_CLR(tv) memset(&tv, 0, sizeof(tv)) | ||||||
|  | #define TV_CPY(dst, src) memcpy(&dst, &src, sizeof(dst)) | ||||||
|  | #define TV_ADD(dst, ts) dst.tv_sec += ts / 10; dst.tv_usec += (ts % 10) * 100000; if (dst.tv_usec >= 1000000) { dst.tv_usec -= 1000000; dst.tv_sec++; } | ||||||
|  |  | ||||||
|  | class cMulticastGroup: public cListObject | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	cConnectionIGMP *connection; | ||||||
|  | 	in_addr_t group; | ||||||
|  | 	in_addr_t reporter; | ||||||
|  | 	struct timeval timeout; | ||||||
|  | 	struct timeval v1timer; | ||||||
|  | 	struct timeval retransmit; | ||||||
|  |  | ||||||
|  | 	cMulticastGroup(in_addr_t Group); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | cMulticastGroup::cMulticastGroup(in_addr_t Group) : | ||||||
|  | 		connection(NULL), | ||||||
|  | 		group(Group), | ||||||
|  | 		reporter(0) | ||||||
|  | { | ||||||
|  | 	TV_CLR(timeout); | ||||||
|  | 	TV_CLR(v1timer); | ||||||
|  | 	TV_CLR(retransmit); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void logIGMP(uint8_t type, struct in_addr Src, struct in_addr Dst, struct in_addr Grp) | ||||||
|  | { | ||||||
|  | 	const char* msg; | ||||||
|  | 	switch (type) { | ||||||
|  | 		case IGMP_MEMBERSHIP_QUERY:	msg = "membership query"; break; | ||||||
|  | 		case IGMP_V1_MEMBERSHIP_REPORT:	msg = "V1 membership report"; break; | ||||||
|  | 		case IGMP_V2_MEMBERSHIP_REPORT:	msg = "V2 membership report"; break; | ||||||
|  | 		case IGMP_V2_LEAVE_GROUP:	msg = "leave group"; break; | ||||||
|  | 		default:			msg = "unknown"; break; | ||||||
|  | 	} | ||||||
|  | 	char* s = strdup(inet_ntoa(Src)); | ||||||
|  | 	char* d = strdup(inet_ntoa(Dst)); | ||||||
|  | 	dsyslog("streamdev-server IGMP: Received %s from %s (dst %s) for %s", msg, s, d, inet_ntoa(Grp)); | ||||||
|  | 	free(s); | ||||||
|  | 	free(d); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Taken from http://tools.ietf.org/html/rfc1071 */ | ||||||
|  | uint16_t inetChecksum(uint16_t *addr, int count) | ||||||
|  | { | ||||||
|  | 	uint32_t sum = 0; | ||||||
|  | 	while (count > 1) { | ||||||
|  | 		sum += *addr++; | ||||||
|  | 		count -= 2; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |         if( count > 0 ) | ||||||
|  | 		sum += * (uint8_t *) addr; | ||||||
|  |  | ||||||
|  | 	while (sum>>16) | ||||||
|  | 		sum = (sum & 0xffff) + (sum >> 16); | ||||||
|  |  | ||||||
|  | 	return ~sum; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cComponentIGMP::cComponentIGMP(void): | ||||||
|  | 		cServerComponent("IGMP", "0.0.0.0", 0, SOCK_RAW, IPPROTO_IGMP), | ||||||
|  | 		cThread("IGMP timeout handler"), | ||||||
|  | 		m_BindIp(inet_addr(StreamdevServerSetup.IGMPBindIP)), | ||||||
|  | 		m_MaxChannelNumber(0), | ||||||
|  | 		m_StartupQueryCount(IGMP_STARTUP_QUERY_COUNT), | ||||||
|  | 		m_Querier(true) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cComponentIGMP::~cComponentIGMP(void) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const | ||||||
|  | { | ||||||
|  | 	cMulticastGroup *group = m_Groups.First(); | ||||||
|  | 	while (group && group->group != Group) | ||||||
|  | 		group = m_Groups.Next(group); | ||||||
|  | 	return group; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool cComponentIGMP::Initialize(void) | ||||||
|  | { | ||||||
|  | 	if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER)) | ||||||
|  | 	{ | ||||||
|  | 		for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) | ||||||
|  | 		{ | ||||||
|  | 			if (channel->GroupSep()) | ||||||
|  | 				continue; | ||||||
|  | 			int num = channel->Number(); | ||||||
|  | 			if (!IGMPMembership(htonl(MULTICAST_PRIV_MIN + num))) | ||||||
|  | 				break; | ||||||
|  | 			m_MaxChannelNumber = num; | ||||||
|  | 		} | ||||||
|  | 		if (m_MaxChannelNumber == 0) | ||||||
|  | 		{ | ||||||
|  | 			IGMPMembership(IGMP_ALL_ROUTER, false); | ||||||
|  | 			esyslog("streamdev-server IGMP: no multicast group joined"); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			Start(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return m_MaxChannelNumber > 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::Destruct(void) | ||||||
|  | { | ||||||
|  | 	if (m_MaxChannelNumber > 0) | ||||||
|  | 	{ | ||||||
|  | 		Cancel(3); | ||||||
|  | 		for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) | ||||||
|  | 		{ | ||||||
|  | 			if (channel->GroupSep()) | ||||||
|  | 				continue; | ||||||
|  | 			int num = channel->Number(); | ||||||
|  | 			if (num > m_MaxChannelNumber) | ||||||
|  | 				break; | ||||||
|  | 			IGMPMembership(htonl(MULTICAST_PRIV_MIN + num), false); | ||||||
|  | 		} | ||||||
|  | 		IGMPMembership(IGMP_ALL_ROUTER, false); | ||||||
|  | 	} | ||||||
|  | 	m_MaxChannelNumber = 0; | ||||||
|  | 	cServerComponent::Destruct(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cServerConnection *cComponentIGMP::NewClient(void) | ||||||
|  | { | ||||||
|  | 	return new cConnectionIGMP("IGMP", StreamdevServerSetup.IGMPClientPort, (eStreamType) StreamdevServerSetup.IGMPStreamType); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cServerConnection* cComponentIGMP::Accept(void) | ||||||
|  | { | ||||||
|  | 	ssize_t recv_len; | ||||||
|  | 	int ip_hdrlen, ip_datalen; | ||||||
|  | 	struct ip *ip; | ||||||
|  | 	struct igmp *igmp; | ||||||
|  |  | ||||||
|  | 	while ((recv_len = ::recvfrom(Socket(), m_ReadBuffer, sizeof(m_ReadBuffer), 0, NULL, NULL)) < 0 && errno == EINTR) | ||||||
|  | 		errno = 0; | ||||||
|  |  | ||||||
|  | 	if (recv_len < 0) { | ||||||
|  | 		esyslog("streamdev-server IGMP: read failed: %m"); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	else if (recv_len < (ssize_t) sizeof(struct ip)) { | ||||||
|  | 		esyslog("streamdev-server IGMP: IP packet too short"); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ip = (struct ip*) m_ReadBuffer; | ||||||
|  |  | ||||||
|  | 	// filter out my own packets | ||||||
|  | 	if (ip->ip_src.s_addr == m_BindIp) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	ip_hdrlen = ip->ip_hl << 2; | ||||||
|  | #ifdef __FreeBSD__ | ||||||
|  | 	ip_datalen = ip->ip_len; | ||||||
|  | #else | ||||||
|  | 	ip_datalen = ntohs(ip->ip_len) - ip_hdrlen; | ||||||
|  | #endif | ||||||
|  | 	if (ip->ip_p != IPPROTO_IGMP) { | ||||||
|  | 		esyslog("streamdev-server IGMP: Unexpected protocol %hhu", ip->ip_p); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	if (recv_len < ip_hdrlen + IGMP_MINLEN) { | ||||||
|  | 		esyslog("streamdev-server IGMP: packet too short"); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	igmp = (struct igmp*) (m_ReadBuffer + ip_hdrlen); | ||||||
|  | 	uint16_t chksum = igmp->igmp_cksum; | ||||||
|  | 	igmp->igmp_cksum = 0; | ||||||
|  | 	if (chksum != inetChecksum((uint16_t *)igmp, ip_datalen)) | ||||||
|  | 	{ | ||||||
|  | 		esyslog("INVALID CHECKSUM %d %d %d %d 0x%x 0x%x", ntohs(ip->ip_len), ip_hdrlen, ip_datalen, recv_len, chksum, inetChecksum((uint16_t *)igmp, ip_datalen)); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	logIGMP(igmp->igmp_type, ip->ip_src, ip->ip_dst, igmp->igmp_group); | ||||||
|  | 	return ProcessMessage(igmp, igmp->igmp_group.s_addr, ip->ip_src.s_addr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cServerConnection* cComponentIGMP::ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender) | ||||||
|  | { | ||||||
|  | 	cServerConnection* conn = NULL; | ||||||
|  | 	cMulticastGroup* group; | ||||||
|  | 	LOCK_THREAD; | ||||||
|  | 	switch (Igmp->igmp_type) { | ||||||
|  | 		case IGMP_MEMBERSHIP_QUERY: | ||||||
|  | 			if (ntohl(Sender) < ntohl(m_BindIp)) | ||||||
|  | 				IGMPStartOtherQuerierPresentTimer(); | ||||||
|  | 			break; | ||||||
|  | 		case IGMP_V1_MEMBERSHIP_REPORT: | ||||||
|  | 		case IGMP_V2_MEMBERSHIP_REPORT: | ||||||
|  | 			group = FindGroup(Group); | ||||||
|  | 			if (!group) { | ||||||
|  | 				group = new cMulticastGroup(Group); | ||||||
|  | 				m_Groups.Add(group); | ||||||
|  | 			} | ||||||
|  | 			if (!group->connection) { | ||||||
|  | 				IGMPStartMulticast(group); | ||||||
|  | 				conn = group->connection; | ||||||
|  | 			} | ||||||
|  | 			IGMPStartTimer(group, Sender); | ||||||
|  | 			if (Igmp->igmp_type == IGMP_V1_MEMBERSHIP_REPORT) | ||||||
|  | 				IGMPStartV1HostTimer(group); | ||||||
|  | 			break; | ||||||
|  | 		case IGMP_V2_LEAVE_GROUP: | ||||||
|  | 			group = FindGroup(Group); | ||||||
|  | 			if (group && !TV_SET(group->v1timer)) { | ||||||
|  | 				if (group->reporter == Sender) { | ||||||
|  | 					IGMPStartTimerAfterLeave(group, m_Querier ? IGMP_LAST_MEMBER_QUERY_INTERVAL_TS : Igmp->igmp_code); | ||||||
|  | 					if (m_Querier) | ||||||
|  | 						IGMPSendGroupQuery(group); | ||||||
|  | 					IGMPStartRetransmitTimer(group); | ||||||
|  | 				} | ||||||
|  | 				m_CondWait.Signal(); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 	return conn; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::Action() | ||||||
|  | { | ||||||
|  | 	while (Running()) { | ||||||
|  | 		struct timeval now; | ||||||
|  | 		struct timeval next; | ||||||
|  |  | ||||||
|  | 		gettimeofday(&now, NULL); | ||||||
|  | 		TV_CPY(next, now); | ||||||
|  | 		next.tv_sec += IGMP_QUERY_INTERVAL; | ||||||
|  |  | ||||||
|  | 		cMulticastGroup *del = NULL; | ||||||
|  | 		{ | ||||||
|  | 			LOCK_THREAD; | ||||||
|  | 			if (TV_CMP(m_GeneralQueryTimer, <, now)) { | ||||||
|  | 				dsyslog("General Query"); | ||||||
|  | 				IGMPSendGeneralQuery(); | ||||||
|  | 				IGMPStartGeneralQueryTimer(); | ||||||
|  | 			} | ||||||
|  | 			if (TV_CMP(next, >, m_GeneralQueryTimer)) | ||||||
|  | 				TV_CPY(next, m_GeneralQueryTimer); | ||||||
|  |  | ||||||
|  | 			for (cMulticastGroup *group = m_Groups.First(); group; group = m_Groups.Next(group)) { | ||||||
|  | 				if (TV_CMP(group->timeout, <, now)) { | ||||||
|  | 					IGMPStopMulticast(group); | ||||||
|  | 					IGMPClearRetransmitTimer(group); | ||||||
|  | 					if (del) | ||||||
|  | 						m_Groups.Del(del); | ||||||
|  | 					del = group; | ||||||
|  | 				} | ||||||
|  | 				else if (m_Querier && TV_SET(group->retransmit) && TV_CMP(group->retransmit, <, now)) { | ||||||
|  | 					IGMPSendGroupQuery(group); | ||||||
|  | 					IGMPStartRetransmitTimer(group); | ||||||
|  | 					if (TV_CMP(next, >, group->retransmit)) | ||||||
|  | 						TV_CPY(next, group->retransmit); | ||||||
|  | 				} | ||||||
|  | 				else if (TV_SET(group->v1timer) && TV_CMP(group->v1timer, <, now)) { | ||||||
|  | 					TV_CLR(group->v1timer); | ||||||
|  | 				} | ||||||
|  | 				else { | ||||||
|  | 					if (TV_CMP(next, >, group->timeout)) | ||||||
|  | 						TV_CPY(next, group->timeout); | ||||||
|  | 					if (TV_SET(group->retransmit) && TV_CMP(next, >, group->retransmit)) | ||||||
|  | 						TV_CPY(next, group->retransmit); | ||||||
|  | 					if (TV_SET(group->v1timer) && TV_CMP(next, >, group->v1timer)) | ||||||
|  | 						TV_CPY(next, group->v1timer); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if (del) | ||||||
|  | 				m_Groups.Del(del); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		int sleep = (next.tv_sec - now.tv_sec) * 1000; | ||||||
|  | 		sleep += (next.tv_usec - now.tv_usec) / 1000; | ||||||
|  | 		if (next.tv_usec < now.tv_usec) | ||||||
|  | 			sleep += 1000; | ||||||
|  | 		dsyslog("Sleeping %d ms", sleep); | ||||||
|  | 		m_CondWait.Wait(sleep); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool cComponentIGMP::IGMPMembership(in_addr_t Group, bool Add) | ||||||
|  | { | ||||||
|  | 	struct ip_mreqn mreq; | ||||||
|  | 	mreq.imr_multiaddr.s_addr = Group; | ||||||
|  | 	mreq.imr_address.s_addr = INADDR_ANY; | ||||||
|  | 	mreq.imr_ifindex = 0; | ||||||
|  | 	if (setsockopt(Socket(), IPPROTO_IP, Add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) | ||||||
|  | 	{ | ||||||
|  | 		esyslog("streamdev-server IGMP: unable to %s %s: %m", Add ? "join" : "leave", inet_ntoa(mreq.imr_multiaddr)); | ||||||
|  | 		if (errno == ENOBUFS) | ||||||
|  | 			esyslog("consider increasing sys.net.ipv4.igmp_max_memberships"); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPSendQuery(in_addr_t Group, int Timeout) | ||||||
|  | { | ||||||
|  | 	struct sockaddr_in dst; | ||||||
|  | 	struct igmp query; | ||||||
|  |  | ||||||
|  | 	dst.sin_family = AF_INET; | ||||||
|  | 	dst.sin_port = IPPROTO_IGMP; | ||||||
|  | 	dst.sin_addr.s_addr = Group; | ||||||
|  | 	query.igmp_type = IGMP_MEMBERSHIP_QUERY; | ||||||
|  | 	query.igmp_code = Timeout * 10; | ||||||
|  | 	query.igmp_cksum = 0; | ||||||
|  | 	query.igmp_group.s_addr = (Group == IGMP_ALL_HOSTS) ? 0 : Group; | ||||||
|  | 	query.igmp_cksum = inetChecksum((uint16_t *) &query, sizeof(query)); | ||||||
|  |  | ||||||
|  | 	for (int i = 0; i < 5 && ::sendto(Socket(), &query, sizeof(query), 0, (sockaddr*)&dst, sizeof(dst)) == -1; i++) { | ||||||
|  | 		if (errno != EAGAIN && errno != EWOULDBLOCK) { | ||||||
|  | 			esyslog("streamdev-server IGMP: unable to query group %s: %m", inet_ntoa(dst.sin_addr)); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		cCondWait::SleepMs(10); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Querier state actions | ||||||
|  | void cComponentIGMP::IGMPStartGeneralQueryTimer() | ||||||
|  | { | ||||||
|  | 	m_Querier = true; | ||||||
|  | 	if (m_StartupQueryCount) { | ||||||
|  | 		gettimeofday(&m_GeneralQueryTimer, NULL); | ||||||
|  | 		m_GeneralQueryTimer.tv_sec += IGMP_STARTUP_QUERY_INTERVAL; | ||||||
|  | 		m_StartupQueryCount--; | ||||||
|  | 	} | ||||||
|  | 	else { | ||||||
|  | 		gettimeofday(&m_GeneralQueryTimer, NULL); | ||||||
|  | 		m_GeneralQueryTimer.tv_sec += IGMP_QUERY_INTERVAL; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPStartOtherQuerierPresentTimer() | ||||||
|  | { | ||||||
|  | 	m_Querier = false; | ||||||
|  | 	m_StartupQueryCount = 0; | ||||||
|  | 	gettimeofday(&m_GeneralQueryTimer, NULL); | ||||||
|  | 	m_GeneralQueryTimer.tv_sec += IGMP_OTHER_QUERIER_PRESENT_INTERVAL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPSendGeneralQuery() | ||||||
|  | { | ||||||
|  | 	IGMPSendQuery(IGMP_ALL_HOSTS, IGMP_QUERY_RESPONSE_INTERVAL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Group state actions | ||||||
|  | void cComponentIGMP::IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member) | ||||||
|  | { | ||||||
|  | 	gettimeofday(&Group->timeout, NULL); | ||||||
|  | 	Group->timeout.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL; | ||||||
|  | 	TV_CLR(Group->retransmit); | ||||||
|  | 	Group->reporter = Member; | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPStartV1HostTimer(cMulticastGroup* Group) | ||||||
|  | { | ||||||
|  | 	gettimeofday(&Group->v1timer, NULL); | ||||||
|  | 	Group->v1timer.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTimeTs) | ||||||
|  | { | ||||||
|  | 	//Group->Update(time(NULL) + MaxResponseTime * IGMP_LAST_MEMBER_QUERY_COUNT / 10); | ||||||
|  | 	MaxResponseTimeTs *= IGMP_LAST_MEMBER_QUERY_COUNT; | ||||||
|  | 	gettimeofday(&Group->timeout, NULL); | ||||||
|  | 	TV_ADD(Group->timeout, MaxResponseTimeTs); | ||||||
|  | 	TV_CLR(Group->retransmit); | ||||||
|  | 	Group->reporter = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPStartRetransmitTimer(cMulticastGroup* Group) | ||||||
|  | { | ||||||
|  | 	gettimeofday(&Group->retransmit, NULL); | ||||||
|  | 	TV_ADD(Group->retransmit, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPClearRetransmitTimer(cMulticastGroup* Group) | ||||||
|  | { | ||||||
|  | 	TV_CLR(Group->retransmit); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPSendGroupQuery(cMulticastGroup* Group) | ||||||
|  | { | ||||||
|  | 	IGMPSendQuery(Group->group, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group) | ||||||
|  | { | ||||||
|  | 	in_addr_t g = ntohl(Group->group); | ||||||
|  | 	if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) { | ||||||
|  | 		cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN); | ||||||
|  | 		Group->connection = (cConnectionIGMP*) NewClient(); | ||||||
|  | 		if (!Group->connection->Start(channel, Group->group)) { | ||||||
|  | 			DELETENULL(Group->connection); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group) | ||||||
|  | { | ||||||
|  | 	if (Group->connection) | ||||||
|  | 		Group->connection->Stop(); | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								server/componentIGMP.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								server/componentIGMP.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | /* | ||||||
|  |  *  $Id: componentIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  |  */ | ||||||
|  |   | ||||||
|  | #ifndef VDR_STREAMDEV_IGMPSERVER_H | ||||||
|  | #define VDR_STREAMDEV_IGMPSERVER_H | ||||||
|  |  | ||||||
|  | #include <sys/time.h> | ||||||
|  | #include <time.h> | ||||||
|  | #include <vdr/thread.h> | ||||||
|  | #include "server/component.h" | ||||||
|  |  | ||||||
|  | class cConnectionIGMP; | ||||||
|  | class cMulticastGroup; | ||||||
|  |  | ||||||
|  | class cComponentIGMP: public cServerComponent, public cThread { | ||||||
|  | private: | ||||||
|  | 	char m_ReadBuffer[2048]; | ||||||
|  | 	cList<cMulticastGroup> m_Groups; | ||||||
|  | 	in_addr_t m_BindIp; | ||||||
|  | 	int m_MaxChannelNumber; | ||||||
|  | 	struct timeval m_GeneralQueryTimer; | ||||||
|  | 	int m_StartupQueryCount; | ||||||
|  | 	bool m_Querier; | ||||||
|  | 	cCondWait m_CondWait; | ||||||
|  |  | ||||||
|  | 	cMulticastGroup* FindGroup(in_addr_t Group) const; | ||||||
|  |  | ||||||
|  | 	/* Add or remove local host to multicast group */ | ||||||
|  | 	bool IGMPMembership(in_addr_t Group, bool Add = true); | ||||||
|  | 	void IGMPSendQuery(in_addr_t Group, int Timeout); | ||||||
|  |  | ||||||
|  | 	cServerConnection* ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender); | ||||||
|  |  | ||||||
|  | 	void IGMPStartGeneralQueryTimer(); | ||||||
|  | 	void IGMPStartOtherQuerierPresentTimer(); | ||||||
|  | 	void IGMPSendGeneralQuery(); | ||||||
|  |  | ||||||
|  | 	void IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member); | ||||||
|  | 	void IGMPStartV1HostTimer(cMulticastGroup* Group); | ||||||
|  | 	void IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTime); | ||||||
|  | 	void IGMPStartRetransmitTimer(cMulticastGroup* Group); | ||||||
|  | 	void IGMPClearRetransmitTimer(cMulticastGroup* Group); | ||||||
|  | 	void IGMPSendGroupQuery(cMulticastGroup* Group); | ||||||
|  | 	void IGMPStartMulticast(cMulticastGroup* Group); | ||||||
|  | 	void IGMPStopMulticast(cMulticastGroup* Group); | ||||||
|  |  | ||||||
|  | 	virtual void Action(); | ||||||
|  |  | ||||||
|  | protected: | ||||||
|  | 	virtual cServerConnection *NewClient(void); | ||||||
|  |  | ||||||
|  | public: | ||||||
|  | 	virtual bool Initialize(void); | ||||||
|  | 	virtual void Destruct(void); | ||||||
|  | 	virtual cServerConnection* Accept(void); | ||||||
|  |  | ||||||
|  | 	cComponentIGMP(void); | ||||||
|  | 	~cComponentIGMP(void); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif // VDR_STREAMDEV_IGMPSERVER_H | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: connection.c,v 1.11 2008/04/08 14:18:18 schmirl Exp $ |  *  $Id: connection.c,v 1.12 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| #include "server/connection.h" | #include "server/connection.h" | ||||||
| @@ -12,7 +12,8 @@ | |||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  |  | ||||||
| cServerConnection::cServerConnection(const char *Protocol): | cServerConnection::cServerConnection(const char *Protocol, int Type): | ||||||
|  | 		cTBSocket(Type), | ||||||
| 		m_Protocol(Protocol), | 		m_Protocol(Protocol), | ||||||
| 		m_DeferClose(false), | 		m_DeferClose(false), | ||||||
| 		m_Pending(false), | 		m_Pending(false), | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: connection.h,v 1.6 2008/10/14 11:05:47 schmirl Exp $ |  *  $Id: connection.h,v 1.7 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| #ifndef VDR_STREAMDEV_SERVER_CONNECTION_H | #ifndef VDR_STREAMDEV_SERVER_CONNECTION_H | ||||||
| @@ -44,7 +44,7 @@ protected: | |||||||
| public: | public: | ||||||
| 	/* If you derive, specify a short string such as HTTP for Protocol, which | 	/* If you derive, specify a short string such as HTTP for Protocol, which | ||||||
| 	   will be displayed in error messages */ | 	   will be displayed in error messages */ | ||||||
| 	cServerConnection(const char *Protocol); | 	cServerConnection(const char *Protocol, int Type = SOCK_STREAM); | ||||||
| 	virtual ~cServerConnection(); | 	virtual ~cServerConnection(); | ||||||
|  |  | ||||||
| 	/* If true, any client IP will be accepted */ | 	/* If true, any client IP will be accepted */ | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								server/connectionIGMP.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								server/connectionIGMP.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | /* | ||||||
|  |  *  $Id: connectionIGMP.c,v 1.1 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <ctype.h> | ||||||
|  |   | ||||||
|  | #include "server/connectionIGMP.h" | ||||||
|  | #include "server/server.h" | ||||||
|  | #include "server/setup.h" | ||||||
|  | #include <vdr/channels.h> | ||||||
|  |  | ||||||
|  | cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) : | ||||||
|  | 		cServerConnection(Name, SOCK_DGRAM), | ||||||
|  | 		m_LiveStreamer(NULL), | ||||||
|  | 		m_ClientPort(ClientPort), | ||||||
|  | 		m_StreamType(StreamType) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cConnectionIGMP::~cConnectionIGMP()  | ||||||
|  | { | ||||||
|  | 	delete m_LiveStreamer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool cConnectionIGMP::Start(cChannel *Channel, in_addr_t Dst) | ||||||
|  | { | ||||||
|  | 	if (Channel != NULL) { | ||||||
|  | 		cDevice *device = GetDevice(Channel, 0); | ||||||
|  | 		if (device != NULL) { | ||||||
|  | 			device->SwitchChannel(Channel, false); | ||||||
|  | 			struct in_addr ip; | ||||||
|  | 			ip.s_addr = Dst; | ||||||
|  | 			if (Connect(inet_ntoa(ip), m_ClientPort)) { | ||||||
|  | 				m_LiveStreamer = new cStreamdevLiveStreamer(0); | ||||||
|  | 				if (m_LiveStreamer->SetChannel(Channel, m_StreamType)) { | ||||||
|  | 					m_LiveStreamer->SetDevice(device); | ||||||
|  | 					if (!SetDSCP()) | ||||||
|  | 						LOG_ERROR_STR("unable to set DSCP sockopt"); | ||||||
|  | 					Dprintf("streamer start\n"); | ||||||
|  | 					m_LiveStreamer->Start(this); | ||||||
|  | 					return true; | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 					esyslog("streamdev-server IGMP: SetDevice failed"); | ||||||
|  | 				DELETENULL(m_LiveStreamer); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				esyslog("streamdev-server IGMP: Connect failed: %m"); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			esyslog("streamdev-server IGMP: GetDevice failed"); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		esyslog("streamdev-server IGMP: Channel not found"); | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void cConnectionIGMP::Stop() | ||||||
|  | { | ||||||
|  | 	if (m_LiveStreamer) { | ||||||
|  | 		m_LiveStreamer->Stop(); | ||||||
|  | 		DELETENULL(m_LiveStreamer); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								server/connectionIGMP.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								server/connectionIGMP.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | /* | ||||||
|  |  *  $Id: connectionIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  |  */ | ||||||
|  |   | ||||||
|  | #ifndef VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H | ||||||
|  | #define VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H | ||||||
|  |  | ||||||
|  | #include "connection.h" | ||||||
|  | #include "server/livestreamer.h" | ||||||
|  |  | ||||||
|  | #include <tools/select.h> | ||||||
|  |  | ||||||
|  | #define MULTICAST_PRIV_MIN ((uint32_t) 0xefff0000) | ||||||
|  | #define MULTICAST_PRIV_MAX ((uint32_t) 0xeffffeff) | ||||||
|  |  | ||||||
|  | class cStreamdevLiveStreamer; | ||||||
|  |  | ||||||
|  | class cConnectionIGMP: public cServerConnection { | ||||||
|  | private: | ||||||
|  | 	cStreamdevLiveStreamer           *m_LiveStreamer; | ||||||
|  | 	int                               m_ClientPort; | ||||||
|  | 	eStreamType                       m_StreamType; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  | 	cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType); | ||||||
|  | 	virtual ~cConnectionIGMP(); | ||||||
|  |  | ||||||
|  | 	bool Start(cChannel *Channel, in_addr_t Dst); | ||||||
|  | 	void Stop(); | ||||||
|  |  | ||||||
|  | 	/* Not used here */ | ||||||
|  | 	virtual bool Command(char *Cmd) { return false; } | ||||||
|  |  | ||||||
|  | 	virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); } | ||||||
|  | 	virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); } | ||||||
|  |  | ||||||
|  | 	virtual bool Abort(void) const; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | inline bool cConnectionIGMP::Abort(void) const | ||||||
|  | { | ||||||
|  | 	return !m_LiveStreamer || m_LiveStreamer->Abort(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H | ||||||
| @@ -1,14 +1,11 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: livefilter.c,v 1.5 2008/04/07 14:27:31 schmirl Exp $ |  *  $Id: livefilter.c,v 1.6 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "server/livefilter.h" | #include "server/livefilter.h" | ||||||
| #include "server/streamer.h" | #include "server/streamer.h" | ||||||
| #include "common.h" | #include "common.h" | ||||||
|  |  | ||||||
| #ifndef TS_SIZE |  | ||||||
| #    define TS_SIZE          188 |  | ||||||
| #endif |  | ||||||
| #ifndef TS_SYNC_BYTE | #ifndef TS_SYNC_BYTE | ||||||
| #    define TS_SYNC_BYTE     0x47 | #    define TS_SYNC_BYTE     0x47 | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: server.c,v 1.9 2008/10/31 12:19:57 schmirl Exp $ |  *  $Id: server.c,v 1.10 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "server/server.h" | #include "server/server.h" | ||||||
| #include "server/componentVTP.h" | #include "server/componentVTP.h" | ||||||
| #include "server/componentHTTP.h" | #include "server/componentHTTP.h" | ||||||
|  | #include "server/componentIGMP.h" | ||||||
| #include "server/setup.h" | #include "server/setup.h" | ||||||
|  |  | ||||||
| #include <vdr/tools.h> | #include <vdr/tools.h> | ||||||
| @@ -36,6 +37,12 @@ void cStreamdevServer::Initialize(void) | |||||||
| 	if (m_Instance == NULL) { | 	if (m_Instance == NULL) { | ||||||
| 		if (StreamdevServerSetup.StartVTPServer)  Register(new cComponentVTP); | 		if (StreamdevServerSetup.StartVTPServer)  Register(new cComponentVTP); | ||||||
| 		if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP); | 		if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP); | ||||||
|  | 		if (StreamdevServerSetup.StartIGMPServer) { | ||||||
|  | 			if (strcmp(StreamdevServerSetup.IGMPBindIP, "0.0.0.0") == 0) | ||||||
|  | 				esyslog("streamdev-server: Not starting IGMP. IGMP must be bound to a local IP"); | ||||||
|  | 			else | ||||||
|  | 				Register(new cComponentIGMP); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		m_Instance = new cStreamdevServer; | 		m_Instance = new cStreamdevServer; | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: setup.c,v 1.5 2009/01/16 11:35:44 schmirl Exp $ |  *  $Id: setup.c,v 1.6 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| #include <vdr/menuitems.h> | #include <vdr/menuitems.h> | ||||||
| @@ -16,10 +16,14 @@ cStreamdevServerSetup::cStreamdevServerSetup(void) { | |||||||
| 	StartHTTPServer = true; | 	StartHTTPServer = true; | ||||||
| 	HTTPServerPort  = 3000; | 	HTTPServerPort  = 3000; | ||||||
| 	HTTPStreamType  = stTS; | 	HTTPStreamType  = stTS; | ||||||
|  | 	StartIGMPServer = false; | ||||||
|  | 	IGMPClientPort  = 1234; | ||||||
|  | 	IGMPStreamType  = stTS; | ||||||
| 	SuspendMode     = smAlways; | 	SuspendMode     = smAlways; | ||||||
| 	AllowSuspend    = false; | 	AllowSuspend    = false; | ||||||
| 	strcpy(VTPBindIP, "0.0.0.0"); | 	strcpy(VTPBindIP, "0.0.0.0"); | ||||||
| 	strcpy(HTTPBindIP, "0.0.0.0"); | 	strcpy(HTTPBindIP, "0.0.0.0"); | ||||||
|  | 	strcpy(IGMPBindIP, "0.0.0.0"); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) { | bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) { | ||||||
| @@ -31,6 +35,10 @@ bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) { | |||||||
| 	else if (strcmp(Name, "HTTPServerPort") == 0)  HTTPServerPort  = atoi(Value); | 	else if (strcmp(Name, "HTTPServerPort") == 0)  HTTPServerPort  = atoi(Value); | ||||||
| 	else if (strcmp(Name, "HTTPStreamType") == 0)  HTTPStreamType  = atoi(Value); | 	else if (strcmp(Name, "HTTPStreamType") == 0)  HTTPStreamType  = atoi(Value); | ||||||
| 	else if (strcmp(Name, "HTTPBindIP") == 0)      strcpy(HTTPBindIP, Value); | 	else if (strcmp(Name, "HTTPBindIP") == 0)      strcpy(HTTPBindIP, Value); | ||||||
|  | 	else if (strcmp(Name, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value); | ||||||
|  | 	else if (strcmp(Name, "IGMPClientPort") == 0)  IGMPClientPort  = atoi(Value); | ||||||
|  | 	else if (strcmp(Name, "IGMPStreamType") == 0)  IGMPStreamType  = atoi(Value); | ||||||
|  | 	else if (strcmp(Name, "IGMPBindIP") == 0)      strcpy(IGMPBindIP, Value); | ||||||
| 	else if (strcmp(Name, "SuspendMode") == 0)     SuspendMode     = atoi(Value); | 	else if (strcmp(Name, "SuspendMode") == 0)     SuspendMode     = atoi(Value); | ||||||
| 	else if (strcmp(Name, "AllowSuspend") == 0)    AllowSuspend    = atoi(Value); | 	else if (strcmp(Name, "AllowSuspend") == 0)    AllowSuspend    = atoi(Value); | ||||||
| 	else return false; | 	else return false; | ||||||
| @@ -55,7 +63,11 @@ cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) { | |||||||
| 	AddShortEdit(tr("HTTP Server Port"),          m_NewSetup.HTTPServerPort); | 	AddShortEdit(tr("HTTP Server Port"),          m_NewSetup.HTTPServerPort); | ||||||
| 	AddTypeEdit (tr("HTTP Streamtype"),           m_NewSetup.HTTPStreamType); | 	AddTypeEdit (tr("HTTP Streamtype"),           m_NewSetup.HTTPStreamType); | ||||||
| 	AddIpEdit   (tr("Bind to IP"),                m_NewSetup.HTTPBindIP); | 	AddIpEdit   (tr("Bind to IP"),                m_NewSetup.HTTPBindIP); | ||||||
| 	 | 	AddCategory (tr("Multicast Streaming Server")); | ||||||
|  | 	AddBoolEdit (tr("Start IGMP Server"),         m_NewSetup.StartIGMPServer); | ||||||
|  | 	AddShortEdit(tr("Multicast Client Port"),     m_NewSetup.IGMPClientPort); | ||||||
|  | 	AddTypeEdit (tr("Multicast Streamtype"),      m_NewSetup.IGMPStreamType); | ||||||
|  | 	AddIpEdit   (tr("Bind to IP"),                m_NewSetup.IGMPBindIP); | ||||||
| 	SetCurrent(Get(1)); | 	SetCurrent(Get(1)); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -69,7 +81,10 @@ void cStreamdevServerMenuSetupPage::Store(void) { | |||||||
| 			|| strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0 | 			|| strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0 | ||||||
| 			|| m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer | 			|| m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer | ||||||
| 			|| m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort | 			|| m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort | ||||||
| 			|| strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0) { | 			|| strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0 | ||||||
|  | 			|| m_NewSetup.StartIGMPServer != StreamdevServerSetup.StartIGMPServer | ||||||
|  | 			|| m_NewSetup.IGMPClientPort != StreamdevServerSetup.IGMPClientPort | ||||||
|  | 			|| strcmp(m_NewSetup.IGMPBindIP, StreamdevServerSetup.IGMPBindIP) != 0) { | ||||||
| 		restart = true; | 		restart = true; | ||||||
| 		cStreamdevServer::Destruct(); | 		cStreamdevServer::Destruct(); | ||||||
| 	} | 	} | ||||||
| @@ -82,6 +97,10 @@ void cStreamdevServerMenuSetupPage::Store(void) { | |||||||
| 	SetupStore("HTTPServerPort",  m_NewSetup.HTTPServerPort); | 	SetupStore("HTTPServerPort",  m_NewSetup.HTTPServerPort); | ||||||
| 	SetupStore("HTTPStreamType",  m_NewSetup.HTTPStreamType); | 	SetupStore("HTTPStreamType",  m_NewSetup.HTTPStreamType); | ||||||
| 	SetupStore("HTTPBindIP",      m_NewSetup.HTTPBindIP); | 	SetupStore("HTTPBindIP",      m_NewSetup.HTTPBindIP); | ||||||
|  | 	SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer); | ||||||
|  | 	SetupStore("IGMPClientPort",  m_NewSetup.IGMPClientPort); | ||||||
|  | 	SetupStore("IGMPStreamType",  m_NewSetup.IGMPStreamType); | ||||||
|  | 	SetupStore("IGMPBindIP",      m_NewSetup.IGMPBindIP); | ||||||
| 	SetupStore("SuspendMode",     m_NewSetup.SuspendMode); | 	SetupStore("SuspendMode",     m_NewSetup.SuspendMode); | ||||||
| 	SetupStore("AllowSuspend",    m_NewSetup.AllowSuspend); | 	SetupStore("AllowSuspend",    m_NewSetup.AllowSuspend); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: setup.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $ |  *  $Id: setup.h,v 1.2 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| #ifndef VDR_STREAMDEV_SETUPSERVER_H | #ifndef VDR_STREAMDEV_SETUPSERVER_H | ||||||
| @@ -20,6 +20,10 @@ struct cStreamdevServerSetup { | |||||||
| 	int HTTPServerPort; | 	int HTTPServerPort; | ||||||
| 	int HTTPStreamType; | 	int HTTPStreamType; | ||||||
| 	char HTTPBindIP[20]; | 	char HTTPBindIP[20]; | ||||||
|  | 	int StartIGMPServer; | ||||||
|  | 	int IGMPClientPort; | ||||||
|  | 	int IGMPStreamType; | ||||||
|  | 	char IGMPBindIP[20]; | ||||||
| 	int SuspendMode; | 	int SuspendMode; | ||||||
| 	int AllowSuspend; | 	int AllowSuspend; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: streamer.c,v 1.17 2008/10/22 11:59:32 schmirl Exp $ |  *  $Id: streamer.c,v 1.18 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| #include <vdr/ringbuffer.h> | #include <vdr/ringbuffer.h> | ||||||
| @@ -55,16 +55,33 @@ void cStreamdevWriter::Action(void) | |||||||
|  |  | ||||||
| 			if (sel.CanWrite(*m_Socket)) { | 			if (sel.CanWrite(*m_Socket)) { | ||||||
| 				int written; | 				int written; | ||||||
| 				if ((written = m_Socket->Write(block + offset, count)) == -1) { | 				int pkgsize = count; | ||||||
| 					esyslog("ERROR: streamdev-server: couldn't send data: %m"); | 				// SOCK_DGRAM indicates multicast | ||||||
|  | 				if (m_Socket->Type() == SOCK_DGRAM) { | ||||||
|  | 					// don't fragment multicast packets | ||||||
|  | 					// max. payload on standard local ethernet is 1416 to 1456 bytes | ||||||
|  | 					// and some STBs expect complete TS packets | ||||||
|  | 					// so let's always limit to 7 * TS_SIZE = 1316 | ||||||
|  | 					if (pkgsize > 7 * TS_SIZE) | ||||||
|  | 						pkgsize = 7 * TS_SIZE; | ||||||
|  | 					else | ||||||
|  | 						pkgsize -= pkgsize % TS_SIZE; | ||||||
|  | 				} | ||||||
|  | 				if ((written = m_Socket->Write(block + offset, pkgsize)) == -1) { | ||||||
|  | 					esyslog("ERROR: streamdev-server: couldn't send %d bytes: %m", pkgsize); | ||||||
| 					break; | 					break; | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | 				// statistics | ||||||
| 				if (count > max) | 				if (count > max) | ||||||
| 					max = count; | 					max = count; | ||||||
|  |  | ||||||
| 				offset += written; | 				offset += written; | ||||||
| 				count -= written; | 				count -= written; | ||||||
| 				if (count == 0) { |  | ||||||
|  | 				// less than one TS packet left: | ||||||
|  | 				// delete what we've written so far and get next chunk | ||||||
|  | 				if (count < TS_SIZE) { | ||||||
| 					m_Streamer->Del(offset); | 					m_Streamer->Del(offset); | ||||||
| 					block = NULL; | 					block = NULL; | ||||||
| 				} | 				} | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  *  $Id: streamer.h,v 1.9 2008/10/22 11:59:32 schmirl Exp $ |  *  $Id: streamer.h,v 1.10 2009/02/13 10:39:22 schmirl Exp $ | ||||||
|  */ |  */ | ||||||
|   |   | ||||||
| #ifndef VDR_STREAMDEV_STREAMER_H | #ifndef VDR_STREAMDEV_STREAMER_H | ||||||
| @@ -12,6 +12,10 @@ | |||||||
| class cTBSocket; | class cTBSocket; | ||||||
| class cStreamdevStreamer; | class cStreamdevStreamer; | ||||||
|  |  | ||||||
|  | #ifndef TS_SIZE | ||||||
|  | #define TS_SIZE 188 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define STREAMERBUFSIZE MEGABYTE(4) | #define STREAMERBUFSIZE MEGABYTE(4) | ||||||
| #define WRITERBUFSIZE KILOBYTE(256) | #define WRITERBUFSIZE KILOBYTE(256) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,4 +10,5 @@ | |||||||
| 127.0.0.1             # always accept localhost | 127.0.0.1             # always accept localhost | ||||||
| #192.168.100.0/24     # any host on the local net | #192.168.100.0/24     # any host on the local net | ||||||
| #204.152.189.113      # a specific host | #204.152.189.113      # a specific host | ||||||
|  | #239.255.0.0/16       # uncomment for IGMP multicast streaming | ||||||
| #0.0.0.0/0            # any host on any net (USE THIS WITH CARE!) | #0.0.0.0/0            # any host on any net (USE THIS WITH CARE!) | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "tools/socket.h" | #include "tools/socket.h" | ||||||
|  |  | ||||||
|  | #include <vdr/tools.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| @@ -15,10 +16,11 @@ | |||||||
| // actual DSCP value used | // actual DSCP value used | ||||||
| #define STREAMDEV_DSCP DSCP_AF41 | #define STREAMDEV_DSCP DSCP_AF41 | ||||||
|  |  | ||||||
| cTBSocket::cTBSocket(int Type) { | cTBSocket::cTBSocket(int Type, int Protocol) { | ||||||
| 	memset(&m_LocalAddr, 0, sizeof(m_LocalAddr)); | 	memset(&m_LocalAddr, 0, sizeof(m_LocalAddr)); | ||||||
| 	memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr)); | 	memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr)); | ||||||
| 	m_Type = Type; | 	m_Type = Type; | ||||||
|  | 	m_Protocol = Protocol; | ||||||
| } | } | ||||||
|  |  | ||||||
| cTBSocket::~cTBSocket() { | cTBSocket::~cTBSocket() { | ||||||
| @@ -31,7 +33,7 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) { | |||||||
|  |  | ||||||
| 	if (IsOpen()) Close(); | 	if (IsOpen()) Close(); | ||||||
| 		 | 		 | ||||||
| 	if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1) | 	if ((socket = ::socket(PF_INET, m_Type, m_Protocol)) == -1) | ||||||
| 		return false; | 		return false; | ||||||
|  |  | ||||||
| 	m_LocalAddr.sin_family = AF_INET; | 	m_LocalAddr.sin_family = AF_INET; | ||||||
| @@ -52,11 +54,13 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) { | |||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if (m_Type == SOCK_STREAM) { | ||||||
| 		len = sizeof(struct sockaddr_in); | 		len = sizeof(struct sockaddr_in); | ||||||
| 		if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) { | 		if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) { | ||||||
| 			::close(socket); | 			::close(socket); | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 	 | 	 | ||||||
| 	len = sizeof(struct sockaddr_in); | 	len = sizeof(struct sockaddr_in); | ||||||
| 	if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1) { | 	if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1) { | ||||||
| @@ -64,7 +68,11 @@ bool cTBSocket::Connect(const std::string &Host, unsigned int Port) { | |||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return cTBSource::Open(socket); | 	if (!cTBSource::Open(socket)) { | ||||||
|  | 		::close(socket); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) { | bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) { | ||||||
| @@ -74,7 +82,7 @@ bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) { | |||||||
|  |  | ||||||
| 	if (IsOpen()) Close(); | 	if (IsOpen()) Close(); | ||||||
| 	 | 	 | ||||||
| 	if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1) | 	if ((socket = ::socket(PF_INET, m_Type, m_Protocol)) == -1) | ||||||
| 		return false; | 		return false; | ||||||
|  |  | ||||||
| 	val = 1; | 	val = 1; | ||||||
|   | |||||||
| @@ -18,9 +18,10 @@ private: | |||||||
| 	struct sockaddr_in m_RemoteAddr; | 	struct sockaddr_in m_RemoteAddr; | ||||||
|  |  | ||||||
| 	int m_Type; | 	int m_Type; | ||||||
|  | 	int m_Protocol; | ||||||
|  |  | ||||||
| public: | public: | ||||||
| 	cTBSocket(int Type = SOCK_STREAM); | 	cTBSocket(int Type = SOCK_STREAM, int Protocol = 0); | ||||||
| 	virtual ~cTBSocket(); | 	virtual ~cTBSocket(); | ||||||
| 	 | 	 | ||||||
| 	/* See cTBSource::SysRead()  | 	/* See cTBSource::SysRead()  | ||||||
| @@ -97,15 +98,22 @@ public: | |||||||
| }; | }; | ||||||
|  |  | ||||||
| inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const { | inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const { | ||||||
| 	if (m_Type == SOCK_DGRAM) { | 	if (m_Type == SOCK_STREAM) | ||||||
|  | 		return ::recv(*this, Buffer, Length, 0); | ||||||
|  | 	else { | ||||||
| 		socklen_t len = sizeof(m_RemoteAddr); | 		socklen_t len = sizeof(m_RemoteAddr); | ||||||
| 		return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len); | 		return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len); | ||||||
| 	} else | 	} | ||||||
| 		return ::recv(*this, Buffer, Length, 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const { | inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const { | ||||||
| 	return ::send(*this, Buffer, Length, 0); | 	return ::send(*this, Buffer, Length, 0); | ||||||
|  | 	if (m_Type == SOCK_STREAM) | ||||||
|  | 		return ::send(*this, Buffer, Length, 0); | ||||||
|  | 	else { | ||||||
|  | 		socklen_t len = sizeof(m_RemoteAddr); | ||||||
|  | 		return ::sendto(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, len); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| #endif // TOOLBOX_SOCKET_H | #endif // TOOLBOX_SOCKET_H | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user