diff --git a/configure.ac b/configure.ac index 2bb9081b..e8eb294e 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.61]) -AC_INIT([MuMuDVB], [1.6.1b_20100101], [mumudvb@braice.net]) +AC_INIT([MuMuDVB], [1.6.1b_20110605_rtsp], [mumudvb@braice.net]) AC_CONFIG_SRCDIR([src/mumudvb.c]) AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([src/config.h]) @@ -30,7 +30,7 @@ AM_ICONV AC_CHECK_LIB([dvbapi], [dvbdemux_set_section_filter]) AC_CHECK_LIB([ucsi], [atsc_text_segment_decode],[atsc_long_names="yes"], [atsc_long_names="no"]) #AC_CHECK_LIB([dvben50221], [en50221_stdcam_create],,,[ucsi,pthread]) -AC_CHECK_LIB([pthread], [pthread_create], [pthread="yes"], [pthread="no"]) +AC_CHECK_LIB([pthread], [pthread_create]) dnl @@ -41,15 +41,16 @@ AC_ARG_ENABLE(cam_support, if test "${enable_cam_support}" = "yes" then - AC_CHECK_LIB([pthread], [pthread_create],[], [enable_cam_support="no"]) AC_CHECK_LIB([ucsi], [atsc_text_segment_decode],[], [enable_cam_support="no"]) AC_CHECK_LIB([dvben50221], [en50221_tl_create],[], [enable_cam_support="no"]) if test "${enable_cam_support}" != "no" then AC_DEFINE(ENABLE_CAM_SUPPORT, 1, Define if you want the CAM support) else - AC_MSG_WARN([libpthread, libucsi and libdvben50221 are needed for CAM support]) + AC_MSG_WARN([libucsi and libdvben50221 are needed for CAM support]) fi +else + enable_cam_support="no" fi AM_CONDITIONAL(BUILD_CAMSUPPORT, [test "${enable_cam_support}" != "no"]) @@ -61,7 +62,8 @@ AC_ARG_ENABLE(transcoding, if test "${enable_transcoding}" = "yes" then - AC_CHECK_LIB([pthread], [pthread_create],[], [enable_transcoding="no"]) + AC_CHECK_LIB([avformat], [url_split],[AC_DEFINE(HAVE_URL_SPLIT, 1, Define if the function url_split exists)], []) + AC_CHECK_LIB([avformat], [ff_url_split],[AC_DEFINE(HAVE_FF_URL_SPLIT, 1, Define if the function ff_url_split exists)], []) AC_CHECK_HEADERS([libavcodec/avcodec.h ffmpeg/avcodec.h]) AC_CHECK_HEADERS([libavformat/avformat.h ffmpeg/avformat.h]) AC_CHECK_HEADERS([libswscale/swscale.h ffmpeg/swscale.h]) @@ -72,8 +74,10 @@ then then AC_DEFINE(ENABLE_TRANSCODING, 1, Define if you want the transcoding support) else - AC_MSG_WARN([libpthread, libavcodev, libavformat and libswscale are needed for transcoding support]) + AC_MSG_WARN([libavcodev, libavformat and libswscale are needed for transcoding support]) fi +else + enable_transcoding="no" fi AM_CONDITIONAL(BUILD_TRANSCODING, [test "${enable_transcoding}" != "no"]) @@ -157,12 +161,6 @@ else echo "Build with ATSC long names support: no" fi -if test "${pthread}" = "yes" ; then - echo "Builded with threads support: yes" -else - echo "Builded with threads support: no" -fi - echo "" echo "Debugging" echo "" diff --git a/doc/configuration_examples/rtsp.conf b/doc/configuration_examples/rtsp.conf new file mode 100644 index 00000000..edc631a4 --- /dev/null +++ b/doc/configuration_examples/rtsp.conf @@ -0,0 +1,9 @@ +freq=12168 +pol=v +srate=27500 +autoconfiguration=full +unicast=1 +unicast_rtsp=1 +port_rtsp=554 +multicast=0 +port_http=8090 diff --git a/src/Makefile.am b/src/Makefile.am index 1c9bab39..0a40b13b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,10 +4,11 @@ AM_LDFLAGS = bin_PROGRAMS = mumudvb mumudvb_SOURCES = autoconf.c crc32.c dvb.h log.c log.h multicast.c mumudvb.h network.h rewrite.h \ - rtp.h sap.h ts.h tune.h unicast_http.h autoconf.h dvb.c errors.h \ + rtp.h sap.h ts.h tune.h unicast_http.h autoconf.h dvb.c errors.h unicast_common.h \ mumudvb.c mumudvb_common.c network.c pat_rewrite.c rewrite.c sdt_rewrite.c \ rtp.c sap.c ts.c tune.c unicast_http.c unicast_queue.c autoconf_sdt.c autoconf_atsc.c \ - autoconf_pmt.c autoconf_nit.c unicast_clients.c + autoconf_pmt.c autoconf_nit.c unicast_clients.c unicast_common.c unicast_common.h \ + unicast_rtsp.c unicast_rtsp.h unicast.h if BUILD_CAMSUPPORT mumudvb_SOURCES += $(SOURCES_camsupport) diff --git a/src/autoconf.c b/src/autoconf.c index 8b4a281d..f8dd6e21 100644 --- a/src/autoconf.c +++ b/src/autoconf.c @@ -74,6 +74,7 @@ #include "autoconf.h" #include "rtp.h" #include "log.h" +#include "unicast.h" extern int Interrupted; extern int server_id; @@ -847,7 +848,7 @@ void autoconf_definite_end(int card, mumudvb_chan_and_pids_t *chan_and_pids, int { log_message(MSG_INFO,"Autoconfiguration done\n"); - log_streamed_channels(chan_and_pids->number_of_channels, chan_and_pids->channels, multicast, unicast_vars->unicast, unicast_vars->portOut, unicast_vars->ipOut); + log_streamed_channels(chan_and_pids->number_of_channels, chan_and_pids->channels, multicast, unicast_vars->unicast, unicast_vars->http_portOut, unicast_vars->ipOut); /**@todo : make an option to generate it or not ?*/ char filename_gen_conf[256]; diff --git a/src/autoconf.h b/src/autoconf.h index 3a44cd42..68df60ef 100644 --- a/src/autoconf.h +++ b/src/autoconf.h @@ -30,7 +30,7 @@ #define _AUTOCONF_H #include "mumudvb.h" -#include "unicast_http.h" +#include "unicast.h" #include "ts.h" #include "tune.h" diff --git a/src/mumudvb.c b/src/mumudvb.c index 23e1375c..90c865bf 100644 --- a/src/mumudvb.c +++ b/src/mumudvb.c @@ -121,7 +121,7 @@ #include "autoconf.h" #include "sap.h" #include "rewrite.h" -#include "unicast_http.h" +#include "unicast.h" #include "rtp.h" #include "log.h" #ifdef ENABLE_TRANSCODING @@ -294,8 +294,11 @@ rewrite_parameters_t rewrite_vars={ unicast_parameters_t unicast_vars={ .unicast=0, .ipOut="0.0.0.0", - .portOut=4242, - .portOut_str=NULL, + .http_portOut=4242, + .http_portOut_str=NULL, + .unicast_rtsp_enable=0, + .rtsp_portOut=554, + .rtsp_portOut_str=NULL, .consecutive_errors_timeout=UNICAST_CONSECUTIVE_ERROR_TIMEOUT, .max_clients=-1, .queue_max_size=UNICAST_DEFAULT_QUEUE_MAX, @@ -768,17 +771,29 @@ int //If we specified a string for the unicast port out, we parse it - if(unicast_vars.portOut_str!=NULL) + if(unicast_vars.http_portOut_str!=NULL) { int len; - len=strlen(unicast_vars.portOut_str)+1; + len=strlen(unicast_vars.http_portOut_str)+1; char number[10]; sprintf(number,"%d",tuneparams.card); - unicast_vars.portOut_str=mumu_string_replace(unicast_vars.portOut_str,&len,1,"%card",number); + unicast_vars.http_portOut_str=mumu_string_replace(unicast_vars.http_portOut_str,&len,1,"%card",number); sprintf(number,"%d",server_id); - unicast_vars.portOut_str=mumu_string_replace(unicast_vars.portOut_str,&len,1,"%server",number); - unicast_vars.portOut=string_comput(unicast_vars.portOut_str); - log_message( MSG_DEBUG, "Unicast: computed unicast master port : %d\n",unicast_vars.portOut); + unicast_vars.http_portOut_str=mumu_string_replace(unicast_vars.http_portOut_str,&len,1,"%server",number); + unicast_vars.http_portOut=string_comput(unicast_vars.http_portOut_str); + log_message( MSG_DEBUG, "Unicast: computed unicast master port : %d\n",unicast_vars.http_portOut); + } + if(unicast_vars.unicast_rtsp_enable && unicast_vars.rtsp_portOut_str!=NULL) + { + int len; + len=strlen(unicast_vars.rtsp_portOut_str)+1; + char number[10]; + sprintf(number,"%d",tuneparams.card); + unicast_vars.rtsp_portOut_str=mumu_string_replace(unicast_vars.rtsp_portOut_str,&len,1,"%card",number); + sprintf(number,"%d",server_id); + unicast_vars.rtsp_portOut_str=mumu_string_replace(unicast_vars.rtsp_portOut_str,&len,1,"%server",number); + unicast_vars.rtsp_portOut=string_comput(unicast_vars.rtsp_portOut_str); + log_message( MSG_DEBUG, "Unicast: computed unicast RTSP port : %d\n",unicast_vars.rtsp_portOut); } /******************************************************/ //end of config file reading @@ -843,10 +858,10 @@ int } } #endif - if(multicast_vars.rtp_header) + if(multicast_vars.rtp_header && !unicast_vars.unicast_rtsp_enable) { multicast_vars.rtp_header=0; - log_message( MSG_INFO, "NO Multicast, RTP Header is desactivated.\n"); + log_message( MSG_INFO, "NO Multicast, NO RTSP, RTP Header is desactivated.\n"); } if(sap_vars.sap==OPTION_ON) { @@ -1069,7 +1084,7 @@ int /*****************************************************/ //Initialisation of the channels for RTP - if(multicast_vars.rtp_header) + if(multicast_vars.rtp_header || unicast_vars.unicast_rtsp_enable) for (curr_channel = 0; curr_channel < chan_and_pids.number_of_channels; curr_channel++) init_rtp_header(&chan_and_pids.channels[curr_channel]); @@ -1206,8 +1221,8 @@ int //We open the socket for the http unicast if needed and we update the poll structure if(unicast_vars.unicast) { - log_message(MSG_INFO,"Unicast : We open the Master http socket for address %s:%d\n",unicast_vars.ipOut, unicast_vars.portOut); - unicast_create_listening_socket(UNICAST_MASTER, -1, unicast_vars.ipOut, unicast_vars.portOut, &unicast_vars.sIn, &unicast_vars.socketIn, &fds, &unicast_vars); + log_message(MSG_INFO,"Unicast : We open the Master HTTP socket for address %s:%d\n",unicast_vars.ipOut, unicast_vars.http_portOut); + unicast_create_listening_socket(UNICAST_MASTER_HTTP, -1, unicast_vars.ipOut, unicast_vars.http_portOut, &unicast_vars.http_sIn, &unicast_vars.http_socketIn, &fds, &unicast_vars); /** open the unicast listening connections fo the channels */ for (curr_channel = 0; curr_channel < chan_and_pids.number_of_channels; curr_channel++) if(chan_and_pids.channels[curr_channel].unicast_port) @@ -1215,6 +1230,11 @@ int log_message(MSG_INFO,"Unicast : We open the channel %d http socket address %s:%d\n",curr_channel, unicast_vars.ipOut, chan_and_pids.channels[curr_channel].unicast_port); unicast_create_listening_socket(UNICAST_LISTEN_CHANNEL, curr_channel, unicast_vars.ipOut,chan_and_pids.channels[curr_channel].unicast_port , &chan_and_pids.channels[curr_channel].sIn, &chan_and_pids.channels[curr_channel].socketIn, &fds, &unicast_vars); } + if(unicast_vars.unicast_rtsp_enable) + { + log_message(MSG_INFO,"Unicast : We open the Master RTSP socket for address %s:%d\n",unicast_vars.ipOut, unicast_vars.rtsp_portOut); + unicast_create_listening_socket(UNICAST_MASTER_RTSP, -1, unicast_vars.ipOut, unicast_vars.rtsp_portOut, &unicast_vars.rtsp_sIn, &unicast_vars.rtsp_socketIn, &fds, &unicast_vars); + } } @@ -1231,7 +1251,7 @@ int /*****************************************************/ if(autoconf_vars.autoconfiguration!=AUTOCONF_MODE_FULL) - log_streamed_channels(chan_and_pids.number_of_channels, chan_and_pids.channels, multicast_vars.multicast, unicast_vars.unicast, unicast_vars.portOut, unicast_vars.ipOut); + log_streamed_channels(chan_and_pids.number_of_channels, chan_and_pids.channels, multicast_vars.multicast, unicast_vars.unicast, unicast_vars.http_portOut, unicast_vars.ipOut); if(autoconf_vars.autoconfiguration) log_message(MSG_INFO,"Autoconfiguration Start\n"); @@ -1513,8 +1533,8 @@ int chan_and_pids.channels[curr_channel].nb_bytes += TS_PACKET_SIZE; //The buffer is full, we send it - if ((!multicast_vars.rtp_header && ((chan_and_pids.channels[curr_channel].nb_bytes + TS_PACKET_SIZE) > MAX_UDP_SIZE)) - ||(multicast_vars.rtp_header && ((chan_and_pids.channels[curr_channel].nb_bytes + RTP_HEADER_LEN + TS_PACKET_SIZE) > MAX_UDP_SIZE))) + if ((!(multicast_vars.rtp_header ||unicast_vars.unicast_rtsp_enable) && ((chan_and_pids.channels[curr_channel].nb_bytes + TS_PACKET_SIZE) > MAX_UDP_SIZE)) + ||((multicast_vars.rtp_header ||unicast_vars.unicast_rtsp_enable) && ((chan_and_pids.channels[curr_channel].nb_bytes + RTP_HEADER_LEN + TS_PACKET_SIZE) > MAX_UDP_SIZE))) { //For bandwith measurement (traffic) chan_and_pids.channels[curr_channel].sent_data+=chan_and_pids.channels[curr_channel].nb_bytes; @@ -1546,14 +1566,15 @@ int (NULL != chan_and_pids.channels[curr_channel].transcode_options.streaming_type && STREAMING_TYPE_MPEGTS != *chan_and_pids.channels[curr_channel].transcode_options.streaming_type)) #endif + if( multicast_vars.rtp_header || unicast_vars.unicast_rtsp_enable) + rtp_update_sequence_number(&chan_and_pids.channels[curr_channel]); /********** MULTICAST *************/ - //if the multicast TTL is set to 0 we don't send the multicast packets + //if the multicast TTL is set to 0 we don't send the multicast packets if(multicast_vars.multicast) { if(multicast_vars.rtp_header) { /****** RTP *******/ - rtp_update_sequence_number(&chan_and_pids.channels[curr_channel]); sendudp (chan_and_pids.channels[curr_channel].socketOut, &chan_and_pids.channels[curr_channel].sOut, chan_and_pids.channels[curr_channel].buf_with_rtp_header, diff --git a/src/network.c b/src/network.c index f04aa0c1..ad3dc7b9 100644 --- a/src/network.c +++ b/src/network.c @@ -106,6 +106,55 @@ makesocket (char *szAddr, unsigned short port, int TTL, return iSocket; } + +/** @brief create a sender socket. + * + * Create a socket for sending data, the socket is udp, with the option REUSE_ADDR set to 1 + */ +int +makeUDPsocket (char *szAddr, unsigned short port, + struct sockaddr_in *sSockAddr) +{ + int iRet, iLoop = 1; + struct sockaddr_in sin; + + int iSocket = socket (AF_INET, SOCK_DGRAM, 0); + + if (iSocket < 0) + { + log_message( MSG_WARN, "socket() failed : %s\n",strerror(errno)); + Interrupted=ERROR_NETWORK<<8; + } + + sSockAddr->sin_family = sin.sin_family = AF_INET; + sSockAddr->sin_port = sin.sin_port = htons (port); + iRet=inet_aton (szAddr,&sSockAddr->sin_addr); + if (iRet == 0) + { + log_message( MSG_ERROR,"inet_aton failed : %s\n", strerror(errno)); + Interrupted=ERROR_NETWORK<<8; + } + //We connect the socket + connect(iSocket,(struct sockaddr *) sSockAddr, sizeof(struct sockaddr_in)); + if (iRet == 0) + { + log_message( MSG_ERROR,"connect failed : %s\n", strerror(errno)); + Interrupted=ERROR_NETWORK<<8; + } + + + /* + iRet = setsockopt (iSocket, SOL_SOCKET, SO_REUSEADDR, &iLoop, sizeof (int)); + if (iRet < 0) + { + log_message( MSG_ERROR,"setsockopt SO_REUSEADDR failed : %s\n",strerror(errno)); + Interrupted=ERROR_NETWORK<<8; + } + */ + return iSocket; +} + + /** @brief create a receiver socket, i.e. join the multicast group. *@todo document */ diff --git a/src/network.h b/src/network.h index 9c7bdf11..68102705 100644 --- a/src/network.h +++ b/src/network.h @@ -48,6 +48,8 @@ int makeclientsocket (char *szAddr, unsigned short port, int TTL, struct sockadd int sendudp (int fd, struct sockaddr_in *sSockAddr, unsigned char *data, int len); int makesocket (char *szAddr, unsigned short port, int TTL, struct sockaddr_in *sSockAddr); int makeTCPclientsocket (char *szAddr, unsigned short port, struct sockaddr_in *sSockAddr); +int makeUDPsocket (char *szAddr, unsigned short port, struct sockaddr_in *sSockAddr); + #endif diff --git a/src/unicast.h b/src/unicast.h new file mode 100644 index 00000000..16af6507 --- /dev/null +++ b/src/unicast.h @@ -0,0 +1,184 @@ +/* + * mumudvb - UDP-ize a DVB transport stream. + * + * (C) 2009 Brice DUBOST + * + * The latest version can be found at http://mumudvb.braice.net + * + * Copyright notice: + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/**@file + * @brief HTML unicast headers + */ + +#ifndef _UNICAST_H +#define _UNICAST_H + +#include "mumudvb.h" +#include "unicast_queue.h" + +#define RTSP_PORT 554 +#define CLOSE_CONNECTION -2 + +/** @brief The different fd/socket types */ +enum + { + UNICAST_MASTER_HTTP=1, + UNICAST_MASTER_RTSP, + UNICAST_LISTEN_CHANNEL, + UNICAST_CLIENT_HTTP, + UNICAST_CLIENT_RTSP, + }; + +/** @brief The different client types */ +enum + { + CLIENT_HTTP, + CLIENT_RTSP, + }; + + +#define RECV_BUFFER_MULTIPLE 100 +/**@brief the timeout for disconnecting a client with only consecutive errors*/ +#define UNICAST_CONSECUTIVE_ERROR_TIMEOUT 5 + + + +/** @brief A client connected to the unicast connection. + * + *There is two chained list of client : a global one wich contain all the clients. Another one in each channel wich contain the associated clients. + */ +typedef struct unicast_client_t{ + /** The type of client */ + int client_type; + /**HTTP/Control socket*/ + struct sockaddr_in SocketAddr; + /**HTTP/Contol socket*/ + int Socket; + + /** Socket closed (RTSP no keep alive)*/ + int Control_socket_closed; + /**RTSP ports*/ + int rtsp_client_port; + int rtsp_server_port; + char client_ip[20]; + /**RTSP socket*/ + int rtsp_Socket; + /**RTSP socket*/ + struct sockaddr_in rtsp_SocketAddr; + /**Session number*/ + char session[16]; + + /**Reception buffer*/ + char *buffer; + /**Size of the buffer*/ + int buffersize; + /**Position in the buffer*/ + int bufferpos; + /**Is there consecutive errors ?*/ + int consecutive_errors; + /**When the first consecutive error happeard*/ + long first_error_time; + /**Channel : -1 if not associated yet*/ + int channel; + /**Future channel : we will set the channel when we will receive the get*/ + int askedChannel; + /**Next client*/ + struct unicast_client_t *next; + /**Previous client*/ + struct unicast_client_t *prev; + /**Next client in the channel*/ + struct unicast_client_t *chan_next; + /**Previous client in the channel*/ + struct unicast_client_t *chan_prev; + /** The packets in queue*/ + unicast_queue_header_t queue; + /** The latest write error for this client*/ + int last_write_error; +}unicast_client_t; + + +/** @brief The information on the unicast file descriptors/sockets + * There is three kind of descriptors : + * The master connection : this connection will interpret the HTTP path asked, to give the channel, the channel list or debugging information + * Client connections : This is the connections for connected clients + * Channel listening connections : When a client connect to one of these sockets, the associated channel will be given directly without interpreting the PATH + * + * The numbering of this socket information is the same as the file descriptors numbering + */ +typedef struct unicast_fd_info_t{ + /**The fd/socket type*/ + int type; + /** The channel if it's a channel socket*/ + int channel; + /** The client if it's a client socket*/ + unicast_client_t *client; +}unicast_fd_info_t; + + +/** @brief The parameters for unicast +*/ +typedef struct unicast_parameters_t{ + /** Do we activate unicast ?*/ + int unicast; + /**The "HTTP" ip address*/ + char ipOut[20]; + /** The "HTTP" port*/ + int http_portOut; + /** The "HTTP" port string version before parsing*/ + char *http_portOut_str; + /** The HTTP input socket*/ + struct sockaddr_in http_sIn; + /** The HTTP input socket*/ + int http_socketIn; + /** RTSP enable */ + int unicast_rtsp_enable; + /** The "RTSP" port*/ + int rtsp_portOut; + /** The "RTSP" port string version before parsing*/ + char *rtsp_portOut_str; + /** The RTSP input socket*/ + struct sockaddr_in rtsp_sIn; + /** The RTSP input socket*/ + int rtsp_socketIn; + /** The clients, contains all the clients, associated to a channel or not*/ + unicast_client_t *clients; + /** The number of connected clients*/ + int client_number; + /** The maximum number of simultaneous clients allowed*/ + int max_clients; + /** The timeout before disconnecting a client wich does only errors*/ + int consecutive_errors_timeout; + /** The information on the file descriptors : ie the type of FD, the client associated if it's a client fd, the channel if it's a channel fd */ + unicast_fd_info_t *fd_info; + /** The maximim size of the queue */ + int queue_max_size; +}unicast_parameters_t; + + +int read_unicast_configuration(unicast_parameters_t *unicast_vars, mumudvb_channel_t *current_channel, int ip_ok, char *substring); + +int unicast_create_listening_socket(int socket_type, int socket_channel, char *ipOut, int port, struct sockaddr_in *sIn, int *socketIn, fds_t *fds, unicast_parameters_t *unicast_vars); + +int unicast_handle_fd_event(unicast_parameters_t *unicast_vars, fds_t *fds, mumudvb_channel_t *channels, int number_of_channels); + +void unicast_freeing(unicast_parameters_t *unicast_vars, mumudvb_channel_t *channels); + +void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *channels, fds_t *fds, unicast_parameters_t *unicast_vars); + +#endif diff --git a/src/unicast_clients.c b/src/unicast_clients.c index ea77c799..7b7c6a48 100644 --- a/src/unicast_clients.c +++ b/src/unicast_clients.c @@ -51,7 +51,8 @@ #include "mumudvb.h" #include "errors.h" #include "log.h" - +#include "unicast.h" +#include "unicast_common.h" /** @brief Add a client to the chained list of clients @@ -61,7 +62,7 @@ * @param SocketAddr The socket address * @param Socket The socket number */ -unicast_client_t *unicast_add_client(unicast_parameters_t *unicast_vars, struct sockaddr_in SocketAddr, int Socket) +unicast_client_t *unicast_add_client(unicast_parameters_t *unicast_vars, struct sockaddr_in SocketAddr, int Socket, int client_type) { unicast_client_t *client; @@ -109,7 +110,9 @@ unicast_client_t *unicast_add_client(unicast_parameters_t *unicast_vars, struct client->queue.packets_in_queue=0; client->queue.first=NULL; client->queue.last=NULL; - + client->client_type=client_type; + client->rtsp_Socket=0; + client->Control_socket_closed=0; unicast_vars->client_number++; return client; @@ -130,12 +133,18 @@ int unicast_del_client(unicast_parameters_t *unicast_vars, unicast_client_t *cli { unicast_client_t *prev_client,*next_client; - log_message(MSG_DETAIL,"Unicast : We delete the client %s:%d, socket %d\n",inet_ntoa(client->SocketAddr.sin_addr), client->SocketAddr.sin_port, client->Socket); + log_message(MSG_DETAIL,"Unicast : We delete the client %s:%d, socket %d\n",inet_ntoa(client->SocketAddr.sin_addr), ntohs(client->SocketAddr.sin_port), client->Socket); + if(client->client_type==CLIENT_RTSP && client->rtsp_Socket) + log_message(MSG_DETAIL,"Unicast : RTSP %s:%d, Unicast socket %d\n",inet_ntoa(client->rtsp_SocketAddr.sin_addr), ntohs(client->rtsp_SocketAddr.sin_port), client->rtsp_Socket); if (client->Socket >= 0) { close(client->Socket); } + if (client->rtsp_Socket >= 0) + { + close(client->rtsp_Socket); + } prev_client=client->prev; next_client=client->next; @@ -193,13 +202,20 @@ int channel_add_unicast_client(unicast_client_t *client,mumudvb_channel_t *chann unicast_client_t *last_client; int iRet; - log_message(MSG_INFO,"Unicast : We add the client %s:%d to the channel \"%s\"\n",inet_ntoa(client->SocketAddr.sin_addr), client->SocketAddr.sin_port,channel->name); + log_message(MSG_INFO,"Unicast : We add the client %s:%d to the channel \"%s\"\n",inet_ntoa(client->SocketAddr.sin_addr), ntohs(client->SocketAddr.sin_port),channel->name); - iRet=write(client->Socket,HTTP_OK_REPLY, strlen(HTTP_OK_REPLY)); - if(iRet!=strlen(HTTP_OK_REPLY)) + if(client->client_type==CLIENT_HTTP) + { + iRet=write(client->Socket,HTTP_OK_REPLY, strlen(HTTP_OK_REPLY)); + if(iRet!=strlen(HTTP_OK_REPLY)) + { + log_message(MSG_INFO,"Unicast : Error when sending the HTTP reply\n"); + return -1; + } + } + if(client->client_type==CLIENT_RTSP) { - log_message(MSG_INFO,"Unicast : Error when sending the HTTP reply\n"); - return -1; + log_message(MSG_INFO,"Unicast : We add the client (RTP) sin_addr : %s: ntohs(client->rtsp_SocketAddr.sin_port) %d to the channel \"%s\"\n",inet_ntoa(client->rtsp_SocketAddr.sin_addr), ntohs(client->rtsp_SocketAddr.sin_port),channel->name); } client->chan_next=NULL; diff --git a/src/unicast_common.c b/src/unicast_common.c new file mode 100644 index 00000000..8b1183bb --- /dev/null +++ b/src/unicast_common.c @@ -0,0 +1,787 @@ +/* +* MuMuDVB - Stream a DVB transport stream. +* +* (C) 2009-2010 Brice DUBOST +* +* The latest version can be found at http://mumudvb.braice.net +* +* Copyright notice: +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/** @file +* @brief File for HTTP unicast common fonctions +* @author Brice DUBOST +* @date 2009-2010 +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unicast_queue.h" +#include "mumudvb.h" +#include "errors.h" +#include "log.h" +#include "unicast.h" +#include "unicast_common.h" +#include "unicast_http.h" +#include "unicast_rtsp.h" + +extern int Interrupted; + + +/** @brief Read a line of the configuration file to check if there is a unicast parameter +* +* @param unicast_vars the unicast parameters +* @param substring The currrent line +*/ +int read_unicast_configuration(unicast_parameters_t *unicast_vars, mumudvb_channel_t *current_channel, int ip_ok, char *substring) +{ + + char delimiteurs[] = CONFIG_FILE_SEPARATOR; + + if (!strcmp (substring, "ip_http")) + { + substring = strtok (NULL, delimiteurs); + if(strlen(substring)>19) + { + log_message( MSG_ERROR, + "The Ip address %s is too long.\n", substring); + exit(ERROR_CONF); + } + sscanf (substring, "%s\n", unicast_vars->ipOut); + if(unicast_vars->ipOut) + { + if(unicast_vars->unicast==0) + { + log_message( MSG_WARN,"You should use the option \"unicast\" to activate unicast instead of ip_http\n"); + unicast_vars->unicast=1; + log_message( MSG_WARN,"You have enabled the support for HTTP Unicast. This feature is quite youg, please report any bug/comment\n"); + } + } + } + else if (!strcmp (substring, "unicast")) + { + substring = strtok (NULL, delimiteurs); + unicast_vars->unicast = atoi (substring); + if(unicast_vars->unicast) + { + log_message( MSG_WARN, + "You have enabled the support for HTTP Unicast. This feature is quite youg, please report any bug/comment\n"); + } + } + else if (!strcmp (substring, "unicast_consecutive_errors_timeout")) + { + substring = strtok (NULL, delimiteurs); + unicast_vars->consecutive_errors_timeout = atoi (substring); + if(unicast_vars->consecutive_errors_timeout<=0) + log_message( MSG_WARN, + "Warning : You have desactivated the unicast timeout for disconnecting clients, this can lead to an accumulation of zombie clients, this is unadvised, prefer a long timeout\n"); + } + else if (!strcmp (substring, "unicast_max_clients")) + { + substring = strtok (NULL, delimiteurs); + unicast_vars->max_clients = atoi (substring); + } + else if (!strcmp (substring, "unicast_queue_size")) + { + substring = strtok (NULL, delimiteurs); + unicast_vars->queue_max_size = atoi (substring); + } + else if (!strcmp (substring, "port_http")) + { + substring = strtok (NULL, ""); + if((strchr(substring,'*')!=NULL)||(strchr(substring,'+')!=NULL)||(strchr(substring,'%')!=NULL)) + { + unicast_vars->http_portOut_str=malloc(sizeof(char)*(strlen(substring)+1)); + strcpy(unicast_vars->http_portOut_str,substring); + } + else + unicast_vars->http_portOut = atoi (substring); + } + else if (!strcmp (substring, "port_rtsp")) + { + substring = strtok (NULL, ""); + if((strchr(substring,'*')!=NULL)||(strchr(substring,'+')!=NULL)||(strchr(substring,'%')!=NULL)) + { + unicast_vars->rtsp_portOut_str=malloc(sizeof(char)*(strlen(substring)+1)); + strcpy(unicast_vars->rtsp_portOut_str,substring); + } + else + unicast_vars->rtsp_portOut = atoi (substring); + } + else if (!strcmp (substring, "unicast_rtsp")) + { + substring = strtok (NULL, delimiteurs); + unicast_vars->unicast_rtsp_enable = atoi (substring); + if(unicast_vars->unicast_rtsp_enable) + log_message(MSG_DETAIL, "RTSP enabled\n"); + } + else if (!strcmp (substring, "unicast_port")) + { + if ( ip_ok == 0) + { + log_message( MSG_ERROR, + "unicast_port : You have to start a channel first (using ip= or channel_next)\n"); + exit(ERROR_CONF); + } + substring = strtok (NULL, delimiteurs); + current_channel->unicast_port = atoi (substring); + } + else + return 0; //Nothing concerning tuning, we return 0 to explore the other possibilities + + return 1;//We found something for tuning, we tell main to go for the next line + +} + + + +/** @brief Create a listening socket and add it to the list of polling file descriptors if success +* +* +* +*/ +int unicast_create_listening_socket(int socket_type, int socket_channel, char *ipOut, int port, struct sockaddr_in *sIn, int *socketIn, fds_t *fds, unicast_parameters_t *unicast_vars) +{ + *socketIn= makeTCPclientsocket(ipOut, port, sIn); + //We add them to the poll descriptors + if(*socketIn>0) + { + fds->pfdsnum++; + log_message(MSG_DEBUG, "unicast : fds->pfdsnum : %d\n", fds->pfdsnum); + fds->pfds=realloc(fds->pfds,(fds->pfdsnum+1)*sizeof(struct pollfd)); + if (fds->pfds==NULL) + { + log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + return -1; + } + fds->pfds[fds->pfdsnum-1].fd = *socketIn; + fds->pfds[fds->pfdsnum-1].events = POLLIN | POLLPRI; + fds->pfds[fds->pfdsnum-1].revents = 0; + fds->pfds[fds->pfdsnum].fd = 0; + fds->pfds[fds->pfdsnum].events = POLLIN | POLLPRI; + fds->pfds[fds->pfdsnum].revents = 0; + //Information about the descriptor + unicast_vars->fd_info=realloc(unicast_vars->fd_info,(fds->pfdsnum)*sizeof(unicast_fd_info_t)); + if (unicast_vars->fd_info==NULL) + { + log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + return -1; + } + //Master connection + unicast_vars->fd_info[fds->pfdsnum-1].type=socket_type; + unicast_vars->fd_info[fds->pfdsnum-1].channel=socket_channel; + unicast_vars->fd_info[fds->pfdsnum-1].client=NULL; + } + else + { + log_message( MSG_WARN, "Problem creating the socket %s:%d : %s\n",ipOut,port,strerror(errno) ); + return -1; + } + + return 0; + +} + +/** @brief Close an unicast connection and delete the client if asked +* +* @param unicast_vars the unicast parameters +* @param fds The polling file descriptors +* @param Socket The socket of the client we want to disconnect +* @param channels The channel list +*/ +void unicast_close_connection(unicast_parameters_t *unicast_vars, fds_t *fds, int Socket, mumudvb_channel_t *channels, int delete_client) +{ + int actual_fd; + actual_fd=0; + //We find the FD correspondig to this client + while((actual_fdpfdsnum) && (fds->pfds[actual_fd].fd!=Socket)) + actual_fd++; + + if(actual_fd==fds->pfdsnum) + { + log_message(MSG_ERROR,"Unicast : close connection : we did't find the file descriptor this should never happend, please contact\n"); + actual_fd=0; + //We find the FD correspondig to this client + while(actual_fdpfdsnum) + { + log_message(MSG_ERROR,"Unicast : fds->pfds[actual_fd].fd %d Socket %d \n", fds->pfds[actual_fd].fd,Socket); + actual_fd++; + } + return; + } + + + //We delete the client + if(delete_client) + { + log_message(MSG_DEBUG,"Unicast : We close the connection and delete the client\n"); + unicast_del_client(unicast_vars, unicast_vars->fd_info[actual_fd].client, channels); + } + else + { + log_message(MSG_DEBUG,"Unicast : We ONLY close the connection\n"); + close(Socket); + unicast_vars->fd_info[actual_fd].client->Socket=0; + } + + /** @todo : deal with the keep alive, we can close the control connection but keep the client*/ + //We move the last fd to the actual/deleted one, and decrease the number of fds by one + fds->pfds[actual_fd].fd = fds->pfds[fds->pfdsnum-1].fd; + fds->pfds[actual_fd].events = fds->pfds[fds->pfdsnum-1].events; + fds->pfds[actual_fd].revents = fds->pfds[fds->pfdsnum-1].revents; + //we move the file descriptor information + unicast_vars->fd_info[actual_fd] = unicast_vars->fd_info[fds->pfdsnum-1]; + //last one set to 0 for poll() + fds->pfds[fds->pfdsnum-1].fd=0; + fds->pfds[fds->pfdsnum-1].events=POLLIN|POLLPRI; + fds->pfds[fds->pfdsnum-1].revents=0; //We clear it to avoid nasty bugs ... + fds->pfdsnum--; + fds->pfds=realloc(fds->pfds,(fds->pfdsnum+1)*sizeof(struct pollfd)); + if (fds->pfds==NULL) + { + log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + Interrupted=ERROR_MEMORY<<8; + } + unicast_vars->fd_info=realloc(unicast_vars->fd_info,(fds->pfdsnum)*sizeof(unicast_fd_info_t)); + if (unicast_vars->fd_info==NULL) + { + log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + Interrupted=ERROR_MEMORY<<8; + } + log_message(MSG_DEBUG,"Unicast : Number of clients : %d\n", unicast_vars->client_number); + +} + + + + + +/** @brief Handle an "event" on the unicast file descriptors +* If the event is on an already open client connection, it handle the message +* If the event is on the master connection, it accepts the new connection +* If the event is on a channel specific socket, it accepts the new connection and starts streaming +* +*/ +int unicast_handle_fd_event(unicast_parameters_t *unicast_vars, fds_t *fds, mumudvb_channel_t *channels, int number_of_channels) +{ + int iRet; + //We look what happened for which connection + int actual_fd; + + + for(actual_fd=1;actual_fdpfdsnum;actual_fd++) + { + iRet=0; + if((fds->pfds[actual_fd].revents&POLLHUP)&&((unicast_vars->fd_info[actual_fd].type==UNICAST_CLIENT_HTTP)||(unicast_vars->fd_info[actual_fd].type==UNICAST_CLIENT_RTSP))) + { + /** @todo RTSP : no keep alive */ + log_message(MSG_DEBUG,"Unicast : We've got a POLLHUP. Actual_fd %d socket %d we close the connection \n", actual_fd, fds->pfds[actual_fd].fd ); + unicast_close_connection(unicast_vars,fds,fds->pfds[actual_fd].fd,channels,1); // !!!!!!!!!!!!! here dont delete the client if RTSP + //We check if we hage to parse fds->pfds[actual_fd].revents (the last fd moved to the actual one) + if(fds->pfds[actual_fd].revents) + actual_fd--;//Yes, we force the loop to see it + } + if((fds->pfds[actual_fd].revents&POLLIN)||(fds->pfds[actual_fd].revents&POLLPRI)) + { + if((unicast_vars->fd_info[actual_fd].type==UNICAST_MASTER_HTTP)|| + (unicast_vars->fd_info[actual_fd].type==UNICAST_LISTEN_CHANNEL)|| + (unicast_vars->fd_info[actual_fd].type==UNICAST_MASTER_RTSP)) + { + //Event on the master connection or listenin channel + //New connection, we accept the connection + if(unicast_vars->fd_info[actual_fd].type==UNICAST_MASTER_RTSP) + log_message(MSG_DEBUG,"Unicast : New RTSP client\n"); + else + log_message(MSG_DEBUG,"Unicast : New HTTP client\n"); + int tempSocket; + unicast_client_t *tempClient; + //we accept the incoming connection + if(unicast_vars->fd_info[actual_fd].type==UNICAST_MASTER_RTSP) + tempClient=unicast_accept_connection(unicast_vars, fds->pfds[actual_fd].fd,CLIENT_RTSP); + else + tempClient=unicast_accept_connection(unicast_vars, fds->pfds[actual_fd].fd,CLIENT_HTTP); + + if(tempClient!=NULL) + { + tempSocket=tempClient->Socket; + fds->pfdsnum++; + fds->pfds=realloc(fds->pfds,(fds->pfdsnum+1)*sizeof(struct pollfd)); + if (fds->pfds==NULL) + { + log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + Interrupted=ERROR_MEMORY<<8; + return -1; + } + //We poll the new socket + fds->pfds[fds->pfdsnum-1].fd = tempSocket; + fds->pfds[fds->pfdsnum-1].events = POLLIN | POLLPRI | POLLHUP; //We also poll the deconnections + fds->pfds[fds->pfdsnum-1].revents = 0; + fds->pfds[fds->pfdsnum].fd = 0; + fds->pfds[fds->pfdsnum].events = POLLIN | POLLPRI; + fds->pfds[fds->pfdsnum].revents = 0; + + //Information about the descriptor + unicast_vars->fd_info=realloc(unicast_vars->fd_info,(fds->pfdsnum)*sizeof(unicast_fd_info_t)); + if (unicast_vars->fd_info==NULL) + { + log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + Interrupted=ERROR_MEMORY<<8; + return -1; + } + //client connection + if(unicast_vars->fd_info[actual_fd].type==UNICAST_MASTER_RTSP) + unicast_vars->fd_info[fds->pfdsnum-1].type=UNICAST_CLIENT_RTSP; + else + unicast_vars->fd_info[fds->pfdsnum-1].type=UNICAST_CLIENT_HTTP; + unicast_vars->fd_info[fds->pfdsnum-1].channel=-1; + unicast_vars->fd_info[fds->pfdsnum-1].client=tempClient; + + + log_message(MSG_DEBUG,"Unicast : Number of clients : %d\n", unicast_vars->client_number); + + if(unicast_vars->fd_info[actual_fd].type==UNICAST_LISTEN_CHANNEL) + { + //Event on a channel connection, we open a new socket for this client and + //we store the wanted channel for when we will get the GET + log_message(MSG_DEBUG,"Unicast : Connection on a channel socket the client will get the channel %d\n", unicast_vars->fd_info[actual_fd].channel); + tempClient->askedChannel=unicast_vars->fd_info[actual_fd].channel; + } + } + } + else if(unicast_vars->fd_info[actual_fd].type==UNICAST_CLIENT_HTTP) + { + //Event on a client connectio i.e. the client asked something + log_message(MSG_FLOOD,"Unicast : New HTTP message for socket %d\n", fds->pfds[actual_fd].fd); + iRet=unicast_handle_http_message(unicast_vars,unicast_vars->fd_info[actual_fd].client, channels, number_of_channels, fds); + if (iRet==CLOSE_CONNECTION ) //iRet==-2 --> 0 received data or error, we close the connection + { + log_message(MSG_FLOOD,"Unicast : handle_http_message told us to close the socket %d\n", fds->pfds[actual_fd].fd); + unicast_close_connection(unicast_vars,fds,fds->pfds[actual_fd].fd,channels,1); + //We check if we hage to parse fds->pfds[actual_fd].revents (the last fd moved to the actual one) + if(fds->pfds[actual_fd].revents) + actual_fd--;//Yes, we force the loop to see it again + } + } + else if(unicast_vars->fd_info[actual_fd].type==UNICAST_CLIENT_RTSP) + { + //Event on a client connectio i.e. the client asked something + log_message(MSG_FLOOD,"Unicast : New RTSP message for socket %d\n", fds->pfds[actual_fd].fd); + iRet=unicast_handle_rtsp_message(unicast_vars,unicast_vars->fd_info[actual_fd].client, channels, number_of_channels, fds); + if (iRet==CLOSE_CONNECTION ) //iRet==CLOSE_CONNECTION --> 0 received data or error, we close the connection + { + log_message(MSG_FLOOD,"Unicast : handle_rtsp_message told us to close the socket %d\n", fds->pfds[actual_fd].fd); + unicast_close_connection(unicast_vars,fds,fds->pfds[actual_fd].fd,channels,1); + //We check if we hage to parse fds->pfds[actual_fd].revents (the last fd moved to the actual one) + if(fds->pfds[actual_fd].revents) + actual_fd--;//Yes, we force the loop to see it again + } + } + else + { + log_message(MSG_WARN,"Unicast : File descriptor with bad type, please contact\n Debug information : actual_fd %d unicast_vars->fd_info[actual_fd].type %d\n", + actual_fd, unicast_vars->fd_info[actual_fd].type); + } + } + } + return 0; + +} + +/** @brief Accept an incoming connection +* +* +* @param unicast_vars the unicast parameters +* @param socketIn the socket on wich the connection was made +*/ +unicast_client_t *unicast_accept_connection(unicast_parameters_t *unicast_vars, int socketIn, int client_type) +{ + + unsigned int l; + int tempSocket; + unicast_client_t *tempClient; + struct sockaddr_in tempSocketAddrIn; + + l = sizeof(struct sockaddr); + tempSocket = accept(socketIn, (struct sockaddr *) &tempSocketAddrIn, &l); + if (tempSocket < 0 ) + { + log_message(MSG_WARN,"Unicast : Error when accepting the incoming connection : %s\n", strerror(errno)); + return NULL; + } + struct sockaddr_in tempSocketAddr; + l = sizeof(struct sockaddr); + getsockname(tempSocket, (struct sockaddr *) &tempSocketAddr, &l); + log_message(MSG_DETAIL,"Unicast : New connection from %s:%d to %s:%d \n",inet_ntoa(tempSocketAddrIn.sin_addr), ntohs(tempSocketAddrIn.sin_port),inet_ntoa(tempSocketAddr.sin_addr), ntohs(tempSocketAddr.sin_port)); + //Now we set this socket to be non blocking because we poll it + int flags; + flags = fcntl(tempSocket, F_GETFL, 0); + flags |= O_NONBLOCK; + if (fcntl(tempSocket, F_SETFL, flags) < 0) + { + log_message(MSG_ERROR,"Set non blocking failed : %s\n",strerror(errno)); + return NULL; + } + + /* if the maximum number of clients is reached, raise a temporary error*/ + if((unicast_vars->max_clients>0)&&(unicast_vars->client_number>=unicast_vars->max_clients)) + { + int iRet; + log_message(MSG_INFO,"Unicast : Too many clients connected, we raise an error to %s\n", inet_ntoa(tempSocketAddrIn.sin_addr)); + if(client_type==CLIENT_HTTP) + iRet=write(tempSocket,HTTP_503_REPLY, strlen(HTTP_503_REPLY)); //iRet is to make the copiler happy we will close the connection anyways + else + iRet=write(tempSocket,RTSP_503_REPLY, strlen(RTSP_503_REPLY)); + close(tempSocket); + return NULL; + } + + tempClient=unicast_add_client(unicast_vars, tempSocketAddrIn, tempSocket, client_type); + if( tempClient == NULL) + { + //We cannot create the client, we close the socket cleanly + close(tempSocket); + return NULL; + } + strncpy(tempClient->client_ip,inet_ntoa(tempSocketAddrIn.sin_addr),20); + tempClient->client_ip[19]='\0'; + + return tempClient; + +} + +/** @brief Store the new message on the socket +* +*/ +int unicast_new_message(unicast_client_t *client) +{ + int received_len; + /************ auto increasing buffer to receive the message **************/ + if((client->buffersize-client->bufferpos)buffer=realloc(client->buffer,(client->buffersize + RECV_BUFFER_MULTIPLE+1)*sizeof(char)); //the +1 if for the \0 at the end + if(client->buffer==NULL) + { + log_message(MSG_ERROR,"Unicast : Problem with realloc for the client buffer : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + client->buffersize=0; + client->bufferpos=0; + return -1; + } + memset (client->buffer+client->buffersize, 0, RECV_BUFFER_MULTIPLE*sizeof(char)); //We fill the buffer with zeros to be sure + client->buffersize += RECV_BUFFER_MULTIPLE; + } + + received_len=recv(client->Socket, client->buffer+client->bufferpos, RECV_BUFFER_MULTIPLE, 0); + + if(received_len>0) + { + if(client->bufferpos==0) + log_message(MSG_DEBUG,"Unicast : beginning of buffer %c%c%c%c%c\n",client->buffer[0],client->buffer[1],client->buffer[2],client->buffer[3],client->buffer[4]); + client->bufferpos+=received_len; + log_message(MSG_FLOOD,"Unicast : We received %d, buffer len %d new buffer pos %d\n",received_len,client->buffersize, client->bufferpos); + } + + if(received_len==-1) + { + log_message(MSG_ERROR,"Unicast : Problem with recv : %s\n",strerror(errno)); + return -1; + } + if(received_len==0) + { + log_message(MSG_DEBUG,"Unicast : Empty message\n"); + return CLOSE_CONNECTION; //To say to the main program to close the connection + } + + /***************** Now we parse the message to see if something was asked *****************/ + client->buffer[client->buffersize]='\0'; //For avoiding strlen to look too far (other option is to use the gnu extension strnlen) + + return 0; +} + +void unicast_flush_client(unicast_client_t *client) +{ + + if(client->buffer) + { + free(client->buffer); + client->buffersize=0; + client->bufferpos=0; + } + client->buffer=NULL; + +} + +/** @brief get the channel number from the asked path + +*/ +int parse_channel_path(char* buf, int *err404, int number_of_channels, mumudvb_channel_t *channels) +{ + int pos; + char *substring; + int requested_channel; + requested_channel=0; + pos=0; + + //Channel by number + //GET /bynumber/channelnumber + if(strstr(buf ,"/bynumber/")==(buf)) + { + pos+=strlen("/bynumber/"); + substring = strtok (buf+pos, " "); + if(substring == NULL) + *err404=1; + else + { + requested_channel=atoi(substring); + if(requested_channel && requested_channel<=number_of_channels) + log_message(MSG_DEBUG,"Unicast : Channel by number, number %d\n",requested_channel); + else + { + log_message(MSG_INFO,"Unicast : Channel by number, number %d out of range\n",requested_channel); + *err404=1; + requested_channel=0; + } + } + } + //Channel by autoconf_sid_list + //GET /bytsid/tsid + else if(strstr(buf ,"/bysid/")==(buf)) + { + pos+=strlen("/bytsid/"); + substring = strtok (buf+pos, " "); + if(substring == NULL) + *err404=1; + else + { + int requested_sid; + requested_sid=atoi(substring); + for(int current_channel=0; current_channelbuffer_header = malloc(REPLY_SIZE_STEP * sizeof (char)); + if (NULL == reply->buffer_header) + { + free(reply); + log_message(MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + return NULL; + } + reply->length_header = REPLY_SIZE_STEP; + reply->used_header = 0; + reply->buffer_body = malloc(REPLY_SIZE_STEP * sizeof (char)); + if (NULL == reply->buffer_body) + { + free(reply->buffer_header); + free(reply); + log_message(MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + return NULL; + } + reply->length_body = REPLY_SIZE_STEP; + reply->used_body = 0; + reply->type = REPLY_BODY; + return reply; +} + +/** @brief Release the reply structure +* +*/ +int unicast_reply_free(unicast_reply_t *reply) +{ + if (NULL == reply) + return 1; + if ((NULL == reply->buffer_header)&&(NULL == reply->buffer_body)) + return 1; + if(reply->buffer_header != NULL) + free(reply->buffer_header); + if(reply->buffer_body != NULL) + free(reply->buffer_body); + free(reply); + return 0; +} + +/** @brief Write data in a buffer using the same syntax that printf() +* +* auto-realloc buffer if needed +*/ +int unicast_reply_write(unicast_reply_t *reply, const char* msg, ...) +{ + char **buffer; + char *temp_buffer; + int *length; + int *used; + buffer=NULL; + va_list args; + if (NULL == msg) + return -1; + switch(reply->type) + { + case REPLY_HEADER: + buffer=&reply->buffer_header; + length=&reply->length_header; + used=&reply->used_header; + break; + case REPLY_BODY: + buffer=&reply->buffer_body; + length=&reply->length_body; + used=&reply->used_body; + break; + default: + log_message(MSG_WARN,"Unicast : unicast_reply_write with wrong type, please contact\n"); + return -1; + } + va_start(args, msg); + int estimated_len = vsnprintf(NULL, 0, msg, args); /* !! imply gcc -std=c99 */ + //Since vsnprintf put the mess we reinitiate the args + va_end(args); + va_start(args, msg); + while (*length - *used < estimated_len) { + temp_buffer = realloc(*buffer, *length + REPLY_SIZE_STEP); + if(temp_buffer == NULL) + { + log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); + return -1; + } + *buffer=temp_buffer; + *length += REPLY_SIZE_STEP; + } + int real_len = vsnprintf(*buffer+*used, *length - *used, msg, args); + if (real_len != estimated_len) { + log_message(MSG_WARN,"Unicast : Error when writing the HTTP reply\n"); + } + *used += real_len; + va_end(args); + return 0; +} + +/** @brief Dump the filled buffer on the socket adding HTTP header informations +*/ +int unicast_reply_send(unicast_reply_t *reply, int socket, int code, const char* content_type) +{ + //we add the header information + reply->type = REPLY_HEADER; + unicast_reply_write(reply, "HTTP/1.0 "); + switch(code) + { + case 200: + unicast_reply_write(reply, "200 OK\r\n"); + break; + case 404: + unicast_reply_write(reply, "404 Not found\r\n"); + break; + default: + log_message(MSG_ERROR,"reply send with bad code please contact\n"); + return 0; + } + unicast_reply_write(reply, "Server: mumudvb/" VERSION "\r\n"); + unicast_reply_write(reply, "Content-type: %s\r\n", content_type); + unicast_reply_write(reply, "Content-length: %d\r\n", reply->used_body); + unicast_reply_write(reply, "\r\n"); /* end header */ + //we merge the header and the body + reply->buffer_header = realloc(reply->buffer_header, reply->used_header+reply->used_body); + memcpy(&reply->buffer_header[reply->used_header],reply->buffer_body,sizeof(char)*reply->used_body); + reply->used_header+=reply->used_body; + //now we write the data + int size = write(socket, reply->buffer_header, reply->used_header); + return size; +} + + +////////////////////// +// End HTTP Toolbox // +////////////////////// + + + + + + + + + + + + diff --git a/src/unicast_common.h b/src/unicast_common.h new file mode 100644 index 00000000..e3582e6e --- /dev/null +++ b/src/unicast_common.h @@ -0,0 +1,64 @@ +/* + * MuMuDVB -Stream a DVB transport stream. + * + * (C) 2010 Brice DUBOST + * + * The latest version can be found at http://mumudvb.braice.net + * + * Copyright notice: + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/**@file + * @brief HTML unicast headers + */ + +#ifndef _UNICAST_COMMON_H +#define _UNICAST_COMMON_H + +#include "mumudvb.h" +#include "unicast_queue.h" +#include "unicast.h" + +#define REPLY_HEADER 0 +#define REPLY_BODY 1 +#define REPLY_SIZE_STEP 256 + +typedef struct unicast_reply_t { + char* buffer_header; + char* buffer_body; + int length_header; + int length_body; + int used_header; + int used_body; + int type; +}unicast_reply_t; + +unicast_reply_t* unicast_reply_init(); +int unicast_reply_free(unicast_reply_t *reply); +int unicast_reply_write(unicast_reply_t *reply, const char* msg, ...); +int unicast_reply_send(unicast_reply_t *reply, int socket, int code, const char* content_type); + +void unicast_close_connection(unicast_parameters_t *unicast_vars, fds_t *fds, int Socket, mumudvb_channel_t *channels, int delete_client); +unicast_client_t *unicast_accept_connection(unicast_parameters_t *unicast_vars, int socketIn, int client_type); +unicast_client_t *unicast_add_client(unicast_parameters_t *unicast_vars, struct sockaddr_in SocketAddr, int Socket, int client_type); +int unicast_new_message(unicast_client_t *client); +void unicast_flush_client(unicast_client_t *client); + +int parse_channel_path(char* buf, int* err404, int number_of_channels, mumudvb_channel_t* channels); + + +#endif diff --git a/src/unicast_http.c b/src/unicast_http.c index 51b6df20..a9762f96 100644 --- a/src/unicast_http.c +++ b/src/unicast_http.c @@ -62,21 +62,24 @@ Todo list #include "mumudvb.h" #include "errors.h" #include "log.h" +#include "unicast.h" +#include "unicast_common.h" extern int Interrupted; //from unicast_client.c -unicast_client_t *unicast_add_client(unicast_parameters_t *unicast_vars, struct sockaddr_in SocketAddr, int Socket); int channel_add_unicast_client(unicast_client_t *client,mumudvb_channel_t *channel); -unicast_client_t *unicast_accept_connection(unicast_parameters_t *unicast_vars, int socketIn); -void unicast_close_connection(unicast_parameters_t *unicast_vars, fds_t *fds, int Socket, mumudvb_channel_t *channels); + + int unicast_send_streamed_channels_list (int number_of_channels, mumudvb_channel_t *channels, int Socket, char *host); int unicast_send_play_list_unicast (int number_of_channels, mumudvb_channel_t *channels, int Socket, int unicast_portOut, int perport); int +unicast_send_play_list_unicast_rtsp (int number_of_channels, mumudvb_channel_t *channels, int Socket, int unicast_portOut, int rtsp_portOut, int perport); +int unicast_send_play_list_multicast (int number_of_channels, mumudvb_channel_t* channels, int Socket, int vlc); int unicast_send_streamed_channels_list_js (int number_of_channels, mumudvb_channel_t *channels, int Socket); @@ -84,392 +87,15 @@ int unicast_send_signal_power_js (int Socket, fds_t *fds); int unicast_send_channel_traffic_js (int number_of_channels, mumudvb_channel_t *channels, int Socket); +int +unicast_send_channel_clients_js (int number_of_clients, int Socket); -int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t *client, mumudvb_channel_t *channels, int num_of_channels, fds_t *fds); - -#define REPLY_HEADER 0 -#define REPLY_BODY 1 -#define REPLY_SIZE_STEP 256 - -struct unicast_reply { - char* buffer_header; - char* buffer_body; - int length_header; - int length_body; - int used_header; - int used_body; - int type; -}; -static struct unicast_reply* unicast_reply_init(); -static int unicast_reply_free(struct unicast_reply *reply); -static int unicast_reply_write(struct unicast_reply *reply, const char* msg, ...); -static int unicast_reply_send(struct unicast_reply *reply, int socket, int code, const char* content_type); - - -/**@todo : deal with the RTP over http case ie implement RTSP*/ - - -/** @brief Read a line of the configuration file to check if there is a unicast parameter -* -* @param unicast_vars the unicast parameters -* @param substring The currrent line -*/ -int read_unicast_configuration(unicast_parameters_t *unicast_vars, mumudvb_channel_t *current_channel, int ip_ok, char *substring) -{ - - char delimiteurs[] = CONFIG_FILE_SEPARATOR; - - if (!strcmp (substring, "ip_http")) - { - substring = strtok (NULL, delimiteurs); - if(strlen(substring)>19) - { - log_message( MSG_ERROR, - "The Ip address %s is too long.\n", substring); - exit(ERROR_CONF); - } - sscanf (substring, "%s\n", unicast_vars->ipOut); - if(unicast_vars->ipOut) - { - if(unicast_vars->unicast==0) - { - log_message( MSG_WARN,"You should use the option \"unicast\" to activate unicast instead of ip_http\n"); - unicast_vars->unicast=1; - log_message( MSG_WARN,"You have enabled the support for HTTP Unicast. This feature is quite youg, please report any bug/comment\n"); - } - } - } - else if (!strcmp (substring, "unicast")) - { - substring = strtok (NULL, delimiteurs); - unicast_vars->unicast = atoi (substring); - if(unicast_vars->unicast) - { - log_message( MSG_WARN, - "You have enabled the support for HTTP Unicast. This feature is quite youg, please report any bug/comment\n"); - } - } - else if (!strcmp (substring, "unicast_consecutive_errors_timeout")) - { - substring = strtok (NULL, delimiteurs); - unicast_vars->consecutive_errors_timeout = atoi (substring); - if(unicast_vars->consecutive_errors_timeout<=0) - log_message( MSG_WARN, - "Warning : You have desactivated the unicast timeout for disconnecting clients, this can lead to an accumulation of zombie clients, this is unadvised, prefer a long timeout\n"); - } - else if (!strcmp (substring, "unicast_max_clients")) - { - substring = strtok (NULL, delimiteurs); - unicast_vars->max_clients = atoi (substring); - } - else if (!strcmp (substring, "unicast_queue_size")) - { - substring = strtok (NULL, delimiteurs); - unicast_vars->queue_max_size = atoi (substring); - } - else if (!strcmp (substring, "port_http")) - { - substring = strtok (NULL, ""); - if((strchr(substring,'*')!=NULL)||(strchr(substring,'+')!=NULL)||(strchr(substring,'%')!=NULL)) - { - unicast_vars->portOut_str=malloc(sizeof(char)*(strlen(substring)+1)); - strcpy(unicast_vars->portOut_str,substring); - } - else - unicast_vars->portOut = atoi (substring); - } - else if (!strcmp (substring, "unicast_port")) - { - if ( ip_ok == 0) - { - log_message( MSG_ERROR, - "unicast_port : You have to start a channel first (using ip= or channel_next)\n"); - exit(ERROR_CONF); - } - substring = strtok (NULL, delimiteurs); - current_channel->unicast_port = atoi (substring); - } - else - return 0; //Nothing concerning tuning, we return 0 to explore the other possibilities - - return 1;//We found something for tuning, we tell main to go for the next line - -} - - - -/** @brief Create a listening socket and add it to the list of polling file descriptors if success -* -* -* -*/ -int unicast_create_listening_socket(int socket_type, int socket_channel, char *ipOut, int port, struct sockaddr_in *sIn, int *socketIn, fds_t *fds, unicast_parameters_t *unicast_vars) -{ - *socketIn= makeTCPclientsocket(ipOut, port, sIn); - //We add them to the poll descriptors - if(*socketIn>0) - { - fds->pfdsnum++; - log_message(MSG_DEBUG, "unicast : fds->pfdsnum : %d\n", fds->pfdsnum); - fds->pfds=realloc(fds->pfds,(fds->pfdsnum+1)*sizeof(struct pollfd)); - if (fds->pfds==NULL) - { - log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - return -1; - } - fds->pfds[fds->pfdsnum-1].fd = *socketIn; - fds->pfds[fds->pfdsnum-1].events = POLLIN | POLLPRI; - fds->pfds[fds->pfdsnum-1].revents = 0; - fds->pfds[fds->pfdsnum].fd = 0; - fds->pfds[fds->pfdsnum].events = POLLIN | POLLPRI; - fds->pfds[fds->pfdsnum].revents = 0; - //Information about the descriptor - unicast_vars->fd_info=realloc(unicast_vars->fd_info,(fds->pfdsnum)*sizeof(unicast_fd_info_t)); - if (unicast_vars->fd_info==NULL) - { - log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - return -1; - } - //Master connection - unicast_vars->fd_info[fds->pfdsnum-1].type=socket_type; - unicast_vars->fd_info[fds->pfdsnum-1].channel=socket_channel; - unicast_vars->fd_info[fds->pfdsnum-1].client=NULL; - } - else - { - log_message( MSG_WARN, "Problem creating the socket %s:%d : %s\n",ipOut,port,strerror(errno) ); - return -1; - } - - return 0; - -} - -/** @brief Handle an "event" on the unicast file descriptors -* If the event is on an already open client connection, it handle the message -* If the event is on the master connection, it accepts the new connection -* If the event is on a channel specific socket, it accepts the new connection and starts streaming -* -*/ -int unicast_handle_fd_event(unicast_parameters_t *unicast_vars, fds_t *fds, mumudvb_channel_t *channels, int number_of_channels) -{ - int iRet; - //We look what happened for which connection - int actual_fd; - - - for(actual_fd=1;actual_fdpfdsnum;actual_fd++) - { - iRet=0; - if((fds->pfds[actual_fd].revents&POLLHUP)&&(unicast_vars->fd_info[actual_fd].type==UNICAST_CLIENT)) - { - log_message(MSG_DEBUG,"Unicast : We've got a POLLHUP. Actual_fd %d socket %d we close the connection \n", actual_fd, fds->pfds[actual_fd].fd ); - unicast_close_connection(unicast_vars,fds,fds->pfds[actual_fd].fd,channels); - //We check if we hage to parse fds->pfds[actual_fd].revents (the last fd moved to the actual one) - if(fds->pfds[actual_fd].revents) - actual_fd--;//Yes, we force the loop to see it - } - if((fds->pfds[actual_fd].revents&POLLIN)||(fds->pfds[actual_fd].revents&POLLPRI)) - { - if((unicast_vars->fd_info[actual_fd].type==UNICAST_MASTER)|| - (unicast_vars->fd_info[actual_fd].type==UNICAST_LISTEN_CHANNEL)) - { - //Event on the master connection or listenin channel - //New connection, we accept the connection - log_message(MSG_DEBUG,"Unicast : New client\n"); - int tempSocket; - unicast_client_t *tempClient; - //we accept the incoming connection - tempClient=unicast_accept_connection(unicast_vars, fds->pfds[actual_fd].fd); - - if(tempClient!=NULL) - { - tempSocket=tempClient->Socket; - fds->pfdsnum++; - fds->pfds=realloc(fds->pfds,(fds->pfdsnum+1)*sizeof(struct pollfd)); - if (fds->pfds==NULL) - { - log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - Interrupted=ERROR_MEMORY<<8; - return -1; - } - //We poll the new socket - fds->pfds[fds->pfdsnum-1].fd = tempSocket; - fds->pfds[fds->pfdsnum-1].events = POLLIN | POLLPRI | POLLHUP; //We also poll the deconnections - fds->pfds[fds->pfdsnum-1].revents = 0; - fds->pfds[fds->pfdsnum].fd = 0; - fds->pfds[fds->pfdsnum].events = POLLIN | POLLPRI; - fds->pfds[fds->pfdsnum].revents = 0; - - //Information about the descriptor - unicast_vars->fd_info=realloc(unicast_vars->fd_info,(fds->pfdsnum)*sizeof(unicast_fd_info_t)); - if (unicast_vars->fd_info==NULL) - { - log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - Interrupted=ERROR_MEMORY<<8; - return -1; - } - //client connection - unicast_vars->fd_info[fds->pfdsnum-1].type=UNICAST_CLIENT; - unicast_vars->fd_info[fds->pfdsnum-1].channel=-1; - unicast_vars->fd_info[fds->pfdsnum-1].client=tempClient; - - - log_message(MSG_DEBUG,"Unicast : Number of clients : %d\n", unicast_vars->client_number); - - if(unicast_vars->fd_info[actual_fd].type==UNICAST_LISTEN_CHANNEL) - { - //Event on a channel connection, we open a new socket for this client and - //we store the wanted channel for when we will get the GET - log_message(MSG_DEBUG,"Unicast : Connection on a channel socket the client will get the channel %d\n", unicast_vars->fd_info[actual_fd].channel); - tempClient->askedChannel=unicast_vars->fd_info[actual_fd].channel; - } - } - } - else if(unicast_vars->fd_info[actual_fd].type==UNICAST_CLIENT) - { - //Event on a client connectio i.e. the client asked something - log_message(MSG_FLOOD,"Unicast : New message for socket %d\n", fds->pfds[actual_fd].fd); - iRet=unicast_handle_message(unicast_vars,unicast_vars->fd_info[actual_fd].client, channels, number_of_channels, fds); - if (iRet==-2 ) //iRet==-2 --> 0 received data or error, we close the connection - { - unicast_close_connection(unicast_vars,fds,fds->pfds[actual_fd].fd,channels); - //We check if we hage to parse fds->pfds[actual_fd].revents (the last fd moved to the actual one) - if(fds->pfds[actual_fd].revents) - actual_fd--;//Yes, we force the loop to see it again - } - } - else - { - log_message(MSG_WARN,"Unicast : File descriptor with bad type, please contact\n Debug information : actual_fd %d unicast_vars->fd_info[actual_fd].type %d\n", - actual_fd, unicast_vars->fd_info[actual_fd].type); - } - } - } - return 0; - -} - - - - -/** @brief Accept an incoming connection -* -* -* @param unicast_vars the unicast parameters -* @param socketIn the socket on wich the connection was made -*/ -unicast_client_t *unicast_accept_connection(unicast_parameters_t *unicast_vars, int socketIn) -{ - - unsigned int l; - int tempSocket; - unicast_client_t *tempClient; - struct sockaddr_in tempSocketAddrIn; - - l = sizeof(struct sockaddr); - tempSocket = accept(socketIn, (struct sockaddr *) &tempSocketAddrIn, &l); - if (tempSocket < 0 ) - { - log_message(MSG_WARN,"Unicast : Error when accepting the incoming connection : %s\n", strerror(errno)); - return NULL; - } - struct sockaddr_in tempSocketAddr; - l = sizeof(struct sockaddr); - getsockname(tempSocket, (struct sockaddr *) &tempSocketAddr, &l); - log_message(MSG_DETAIL,"Unicast : New connection from %s:%d to %s:%d \n",inet_ntoa(tempSocketAddrIn.sin_addr), tempSocketAddrIn.sin_port,inet_ntoa(tempSocketAddr.sin_addr), tempSocketAddr.sin_port); - - //Now we set this socket to be non blocking because we poll it - int flags; - flags = fcntl(tempSocket, F_GETFL, 0); - flags |= O_NONBLOCK; - if (fcntl(tempSocket, F_SETFL, flags) < 0) - { - log_message(MSG_ERROR,"Set non blocking failed : %s\n",strerror(errno)); - return NULL; - } - - /* if the maximum number of clients is reached, raise a temporary error*/ - if((unicast_vars->max_clients>0)&&(unicast_vars->client_number>=unicast_vars->max_clients)) - { - int iRet; - log_message(MSG_INFO,"Unicast : Too many clients connected, we raise an error to %s\n", inet_ntoa(tempSocketAddrIn.sin_addr)); - iRet=write(tempSocket,HTTP_503_REPLY, strlen(HTTP_503_REPLY)); //iRet is to make the copiler happy we will close the connection anyways - close(tempSocket); - return NULL; - } - - tempClient=unicast_add_client(unicast_vars, tempSocketAddrIn, tempSocket); - if( tempClient == NULL) - { - //We cannot create the client, we close the socket cleanly - close(tempSocket); - return NULL; - } - - return tempClient; -} -/** @brief Close an unicast connection and delete the client -* -* @param unicast_vars the unicast parameters -* @param fds The polling file descriptors -* @param Socket The socket of the client we want to disconnect -* @param channels The channel list -*/ -void unicast_close_connection(unicast_parameters_t *unicast_vars, fds_t *fds, int Socket, mumudvb_channel_t *channels) -{ - - int actual_fd; - actual_fd=0; - //We find the FD correspondig to this client - while((actual_fdpfdsnum) && (fds->pfds[actual_fd].fd!=Socket)) - actual_fd++; - if(actual_fd==fds->pfdsnum) - { - log_message(MSG_ERROR,"Unicast : close connection : we did't find the file descriptor this should never happend, please contact\n"); - actual_fd=0; - //We find the FD correspondig to this client - while(actual_fdpfdsnum) - { - log_message(MSG_ERROR,"Unicast : fds->pfds[actual_fd].fd %d Socket %d \n", fds->pfds[actual_fd].fd,Socket); - actual_fd++; - } - return; - } - log_message(MSG_DEBUG,"Unicast : We close the connection\n"); - //We delete the client - unicast_del_client(unicast_vars, unicast_vars->fd_info[actual_fd].client, channels); - //We move the last fd to the actual/deleted one, and decrease the number of fds by one - fds->pfds[actual_fd].fd = fds->pfds[fds->pfdsnum-1].fd; - fds->pfds[actual_fd].events = fds->pfds[fds->pfdsnum-1].events; - fds->pfds[actual_fd].revents = fds->pfds[fds->pfdsnum-1].revents; - //we move the file descriptor information - unicast_vars->fd_info[actual_fd] = unicast_vars->fd_info[fds->pfdsnum-1]; - //last one set to 0 for poll() - fds->pfds[fds->pfdsnum-1].fd=0; - fds->pfds[fds->pfdsnum-1].events=POLLIN|POLLPRI; - fds->pfds[fds->pfdsnum-1].revents=0; //We clear it to avoid nasty bugs ... - fds->pfdsnum--; - fds->pfds=realloc(fds->pfds,(fds->pfdsnum+1)*sizeof(struct pollfd)); - if (fds->pfds==NULL) - { - log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - Interrupted=ERROR_MEMORY<<8; - } - unicast_vars->fd_info=realloc(unicast_vars->fd_info,(fds->pfdsnum)*sizeof(unicast_fd_info_t)); - if (unicast_vars->fd_info==NULL) - { - log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - Interrupted=ERROR_MEMORY<<8; - } - log_message(MSG_DEBUG,"Unicast : Number of clients : %d\n", unicast_vars->client_number); -} @@ -483,46 +109,16 @@ void unicast_close_connection(unicast_parameters_t *unicast_vars, fds_t *fds, in * @param channels the channel array * @param number_of_channels quite explicit ... */ -int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t *client, mumudvb_channel_t *channels, int number_of_channels, fds_t *fds) +int unicast_handle_http_message(unicast_parameters_t *unicast_vars, unicast_client_t *client, mumudvb_channel_t *channels, int number_of_channels, fds_t *fds) { - int received_len; (void) unicast_vars; - /************ auto increasing buffer to receive the message **************/ - if((client->buffersize-client->bufferpos)buffer=realloc(client->buffer,(client->buffersize + RECV_BUFFER_MULTIPLE+1)*sizeof(char)); //the +1 if for the \0 at the end - if(client->buffer==NULL) - { - log_message(MSG_ERROR,"Unicast : Problem with realloc for the client buffer : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - client->buffersize=0; - client->bufferpos=0; - return -1; - } - memset (client->buffer+client->buffersize, 0, RECV_BUFFER_MULTIPLE*sizeof(char)); //We fill the buffer with zeros to be sure - client->buffersize += RECV_BUFFER_MULTIPLE; - } - - received_len=recv(client->Socket, client->buffer+client->bufferpos, RECV_BUFFER_MULTIPLE, 0); - - if(received_len>0) - { - if(client->bufferpos==0) - log_message(MSG_DEBUG,"Unicast : beginning of buffer %c%c%c%c%c\n",client->buffer[0],client->buffer[1],client->buffer[2],client->buffer[3],client->buffer[4]); - client->bufferpos+=received_len; - log_message(MSG_FLOOD,"Unicast : We received %d, buffer len %d new buffer pos %d\n",received_len,client->buffersize, client->bufferpos); - } + int iRet; - if(received_len==-1) - { - log_message(MSG_ERROR,"Unicast : Problem with recv : %s\n",strerror(errno)); - return -1; - } - if(received_len==0) - return -2; //To say to the main program to close the connection + iRet=unicast_new_message(client); + if(iRet) + return iRet; - /***************** Now we parse the message to see if something was asked *****************/ - client->buffer[client->buffersize]='\0'; //For avoiding strlen to look too far (other option is to use the gnu extension strnlen) //We search for the end of the HTTP request if(strlen(client->buffer)>5 && strstr(client->buffer, "\n\r\n\0")) { @@ -533,7 +129,7 @@ int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t requested_channel=0; pos=0; err404=0; - struct unicast_reply* reply=NULL; + unicast_reply_t* reply=NULL; log_message(MSG_DEBUG,"Unicast : End of HTTP request, we parse it\n"); @@ -553,92 +149,17 @@ int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t log_message(MSG_DEBUG,"Unicast : Channel by socket, number %d\n",requested_channel); client->askedChannel=-1; } - //Channel by number - //GET /bynumber/channelnumber - else if(strstr(client->buffer +pos ,"/bynumber/")==(client->buffer +pos)) - { - if(client->channel!=-1) - { - log_message(MSG_INFO,"Unicast : A channel (%d) is already streamed to this client, it shouldn't ask for a new one without closing the connection, error 501\n",client->channel); - iRet=write(client->Socket,HTTP_501_REPLY, strlen(HTTP_501_REPLY)); //iRet is to make the copiler happy we will close the connection anyways - return -2; //to delete the client - } - pos+=strlen("/bynumber/"); - substring = strtok (client->buffer+pos, " "); - if(substring == NULL) - err404=1; - else - { - requested_channel=atoi(substring); - if(requested_channel && requested_channel<=number_of_channels) - log_message(MSG_DEBUG,"Unicast : Channel by number, number %d\n",requested_channel); - else - { - log_message(MSG_INFO,"Unicast : Channel by number, number %d out of range\n",requested_channel); - err404=1; - requested_channel=0; - } - } - } - //Channel by autoconf_sid_list - //GET /bytsid/tsid - else if(strstr(client->buffer +pos ,"/bysid/")==(client->buffer +pos)) - { - if(client->channel!=-1) - { - log_message(MSG_INFO,"Unicast : A channel (%d) is already streamed to this client, it shouldn't ask for a new one without closing the connection, error 501\n",client->channel); - iRet=write(client->Socket,HTTP_501_REPLY, strlen(HTTP_501_REPLY)); //iRet is to make the copiler happy we will close the connection anyways - return -2; //to delete the client - } - pos+=strlen("/bytsid/"); - substring = strtok (client->buffer+pos, " "); - if(substring == NULL) - err404=1; - else - { - int requested_sid; - requested_sid=atoi(substring); - for(int current_channel=0; current_channelbuffer +pos ,"/byname/")==(client->buffer +pos)) + requested_channel=parse_channel_path(client->buffer +pos,&err404, number_of_channels,channels); + if(requested_channel&& (client->channel!=-1)) { - if(client->channel!=-1) - { - log_message(MSG_INFO,"Unicast : A channel is already streamed to this client, it shouldn't ask for a new one without closing the connection, error 501\n"); - iRet=write(client->Socket,HTTP_501_REPLY, strlen(HTTP_501_REPLY));//iRet is to make the copiler happy we will close the connection anyways - return -2; //to delete the client - } - pos+=strlen("/byname/"); - log_message(MSG_DEBUG,"Unicast : Channel by number\n"); - substring = strtok (client->buffer+pos, " "); - if(substring == NULL) - err404=1; - else - { - log_message(MSG_DEBUG,"Unicast : Channel by name, name %s\n",substring); - //search the channel - err404=1;//Temporary - /** @todo implement the search without the spaces*/ - } + log_message(MSG_INFO,"Unicast : A channel (%d) is already streamed to this client, it shouldn't ask for a new one without closing the connection, error 501\n",client->channel); + iRet=write(client->Socket,HTTP_501_REPLY, strlen(HTTP_501_REPLY)); //iRet is to make the copiler happy we will close the connection anyways + return CLOSE_CONNECTION; //to delete the client } + //Channels list - else if(strstr(client->buffer +pos ,"/channels_list.html ")==(client->buffer +pos)) + if(strstr(client->buffer +pos ,"/channels_list.html ")==(client->buffer +pos)) { //We get the host name if availaible char *hoststr; @@ -651,53 +172,67 @@ int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t substring=NULL; log_message(MSG_DETAIL,"Channel list\n"); unicast_send_streamed_channels_list (number_of_channels, channels, client->Socket, substring); - return -2; //We close the connection afterwards + return CLOSE_CONNECTION; //We close the connection afterwards } //playlist, m3u else if(strstr(client->buffer +pos ,"/playlist.m3u ")==(client->buffer +pos)) { log_message(MSG_DETAIL,"play list\n"); - unicast_send_play_list_unicast (number_of_channels, channels, client->Socket, unicast_vars->portOut, 0 ); - return -2; //We close the connection afterwards + unicast_send_play_list_unicast (number_of_channels, channels, client->Socket, unicast_vars->http_portOut, 0 ); + return CLOSE_CONNECTION; //We close the connection afterwards } //playlist, m3u else if(strstr(client->buffer +pos ,"/playlist_port.m3u ")==(client->buffer +pos)) { log_message(MSG_DETAIL,"play list\n"); - unicast_send_play_list_unicast (number_of_channels, channels, client->Socket, unicast_vars->portOut, 1 ); - return -2; //We close the connection afterwards + unicast_send_play_list_unicast (number_of_channels, channels, client->Socket, unicast_vars->http_portOut, 1 ); + return CLOSE_CONNECTION; //We close the connection afterwards + } + //playlist RTSP, m3u + else if( (strstr(client->buffer +pos ,"/playlist_rtsp.m3u ")==(client->buffer +pos)) && unicast_vars->unicast_rtsp_enable ) + { + log_message(MSG_DETAIL,"play list\n"); + unicast_send_play_list_unicast_rtsp (number_of_channels, channels, client->Socket, unicast_vars->http_portOut, unicast_vars->rtsp_portOut, 0 ); + return CLOSE_CONNECTION; //We close the connection afterwards } else if(strstr(client->buffer +pos ,"/playlist_multicast.m3u ")==(client->buffer +pos)) { log_message(MSG_DETAIL,"play list\n"); unicast_send_play_list_multicast (number_of_channels, channels, client->Socket, 0 ); - return -2; //We close the connection afterwards + return CLOSE_CONNECTION; //We close the connection afterwards } else if(strstr(client->buffer +pos ,"/playlist_multicast_vlc.m3u ")==(client->buffer +pos)) { log_message(MSG_DETAIL,"play list\n"); unicast_send_play_list_multicast (number_of_channels, channels, client->Socket, 1 ); - return -2; //We close the connection afterwards + return CLOSE_CONNECTION; //We close the connection afterwards } //statistics, text version else if(strstr(client->buffer +pos ,"/channels_list.json ")==(client->buffer +pos)) { log_message(MSG_DETAIL,"Channel list Json\n"); unicast_send_streamed_channels_list_js (number_of_channels, channels, client->Socket); - return -2; //We close the connection afterwards + return CLOSE_CONNECTION; //We close the connection afterwards } else if(strstr(client->buffer +pos ,"/monitor/signal_power.json ")==(client->buffer +pos)) { log_message(MSG_DETAIL,"Signal power json\n"); unicast_send_signal_power_js(client->Socket, fds); - return -2; //We close the connection afterwards + return CLOSE_CONNECTION; //We close the connection afterwards } else if(strstr(client->buffer +pos ,"/monitor/channels_traffic.json ")==(client->buffer +pos)) { log_message(MSG_DETAIL,"Channel traffic json\n"); unicast_send_channel_traffic_js(number_of_channels, channels, client->Socket); - return -2; //We close the connection afterwards + return CLOSE_CONNECTION; //We close the connection afterwards } + else if(strstr(client->buffer +pos ,"/monitor/clients.json ")==(client->buffer +pos)) + { + log_message(MSG_DETAIL,"Unicast Clients\n"); + unicast_send_channel_clients_js(unicast_vars->client_number, client->Socket); + return CLOSE_CONNECTION; //We close the connection afterwards + } + //Not implemented path --> 404 else err404=1; @@ -709,15 +244,15 @@ int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t reply = unicast_reply_init(); if (NULL == reply) { log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); - return -2; + return CLOSE_CONNECTION; } unicast_reply_write(reply, HTTP_404_REPLY_HTML, VERSION); unicast_reply_send(reply, client->Socket, 404, "text/html"); if (0 != unicast_reply_free(reply)) { log_message(MSG_INFO,"Unicast : Error when releasing the HTTP reply after sendinf it\n"); - return -2; + return CLOSE_CONNECTION; } - return -2; //to delete the client + return CLOSE_CONNECTION; //to delete the client } //We have found a channel, we add the client if(requested_channel) @@ -725,7 +260,7 @@ int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t if(!channel_add_unicast_client(client,&channels[requested_channel-1])) client->channel=requested_channel-1; else - return -2; + return CLOSE_CONNECTION; } } @@ -736,7 +271,7 @@ int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t { log_message(MSG_INFO,"Unicast : Unhandled HTTP method : \"%s\", error 501\n", strtok (client->buffer+pos, " ")); iRet=write(client->Socket,HTTP_501_REPLY, strlen(HTTP_501_REPLY));//iRet is to make the copiler happy we will close the connection anyways - return -2; //to delete the client + return CLOSE_CONNECTION; //to delete the client } else { @@ -756,154 +291,6 @@ int unicast_handle_message(unicast_parameters_t *unicast_vars, unicast_client_t } -////////////////// -// HTTP Toolbox // -////////////////// - - - -/** @brief Init reply structure -* -*/ -struct unicast_reply* unicast_reply_init() -{ - struct unicast_reply* reply = malloc(sizeof (struct unicast_reply)); - if (NULL == reply) - { - log_message(MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - return NULL; - } - reply->buffer_header = malloc(REPLY_SIZE_STEP * sizeof (char)); - if (NULL == reply->buffer_header) - { - free(reply); - log_message(MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - return NULL; - } - reply->length_header = REPLY_SIZE_STEP; - reply->used_header = 0; - reply->buffer_body = malloc(REPLY_SIZE_STEP * sizeof (char)); - if (NULL == reply->buffer_body) - { - free(reply->buffer_header); - free(reply); - log_message(MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - return NULL; - } - reply->length_body = REPLY_SIZE_STEP; - reply->used_body = 0; - reply->type = REPLY_BODY; - return reply; -} - -/** @brief Release the reply structure -* -*/ -int unicast_reply_free(struct unicast_reply *reply) -{ - if (NULL == reply) - return 1; - if ((NULL == reply->buffer_header)&&(NULL == reply->buffer_body)) - return 1; - if(reply->buffer_header != NULL) - free(reply->buffer_header); - if(reply->buffer_body != NULL) - free(reply->buffer_body); - free(reply); - return 0; -} - -/** @brief Write data in a buffer using the same syntax that printf() -* -* auto-realloc buffer if needed -*/ -int unicast_reply_write(struct unicast_reply *reply, const char* msg, ...) -{ - char **buffer; - char *temp_buffer; - int *length; - int *used; - buffer=NULL; - va_list args; - if (NULL == msg) - return -1; - switch(reply->type) - { - case REPLY_HEADER: - buffer=&reply->buffer_header; - length=&reply->length_header; - used=&reply->used_header; - break; - case REPLY_BODY: - buffer=&reply->buffer_body; - length=&reply->length_body; - used=&reply->used_body; - break; - default: - log_message(MSG_WARN,"Unicast : unicast_reply_write with wrong type, please contact\n"); - return -1; - } - va_start(args, msg); - int estimated_len = vsnprintf(NULL, 0, msg, args); /* !! imply gcc -std=c99 */ - //Since vsnprintf put the mess we reinitiate the args - va_end(args); - va_start(args, msg); - while (*length - *used < estimated_len) { - temp_buffer = realloc(*buffer, *length + REPLY_SIZE_STEP); - if(temp_buffer == NULL) - { - log_message(MSG_ERROR,"Problem with realloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); - return -1; - } - *buffer=temp_buffer; - *length += REPLY_SIZE_STEP; - } - int real_len = vsnprintf(*buffer+*used, *length - *used, msg, args); - if (real_len != estimated_len) { - log_message(MSG_WARN,"Unicast : Error when writing the HTTP reply\n"); - } - *used += real_len; - va_end(args); - return 0; -} - -/** @brief Dump the filled buffer on the socket adding HTTP header informations -*/ -int unicast_reply_send(struct unicast_reply *reply, int socket, int code, const char* content_type) -{ - //we add the header information - reply->type = REPLY_HEADER; - unicast_reply_write(reply, "HTTP/1.0 "); - switch(code) - { - case 200: - unicast_reply_write(reply, "200 OK\r\n"); - break; - case 404: - unicast_reply_write(reply, "404 Not found\r\n"); - break; - default: - log_message(MSG_ERROR,"reply send with bad code please contact\n"); - return 0; - } - unicast_reply_write(reply, "Server: mumudvb/" VERSION "\r\n"); - unicast_reply_write(reply, "Content-type: %s\r\n", content_type); - unicast_reply_write(reply, "Content-length: %d\r\n", reply->used_body); - unicast_reply_write(reply, "\r\n"); /* end header */ - //we merge the header and the body - reply->buffer_header = realloc(reply->buffer_header, reply->used_header+reply->used_body); - memcpy(&reply->buffer_header[reply->used_header],reply->buffer_body,sizeof(char)*reply->used_body); - reply->used_header+=reply->used_body; - //now we write the data - int size = write(socket, reply->buffer_header, reply->used_header); - return size; -} - - -////////////////////// -// End HTTP Toolbox // -////////////////////// - /** @brief Send a basic html file containig the list of streamed channels @@ -917,7 +304,7 @@ int unicast_send_streamed_channels_list (int number_of_channels, mumudvb_channel_t *channels, int Socket, char *host) { - struct unicast_reply* reply = unicast_reply_init(); + unicast_reply_t* reply = unicast_reply_init(); if (NULL == reply) { log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); return -1; @@ -935,19 +322,19 @@ unicast_send_streamed_channels_list (int number_of_channels, mumudvb_channel_t * host,curr_channel+1, host,curr_channel+1, channels[curr_channel].ipOut,channels[curr_channel].portOut); - else - unicast_reply_write(reply, "Channel number %d : \"%s\"
Multicast ip : %s:%d

\r\n",curr_channel,channels[curr_channel].name,channels[curr_channel].ipOut,channels[curr_channel].portOut); + else + unicast_reply_write(reply, "Channel number %d : \"%s\"
Multicast ip : %s:%d

\r\n",curr_channel,channels[curr_channel].name,channels[curr_channel].ipOut,channels[curr_channel].portOut); } - unicast_reply_write(reply, HTTP_CHANNELS_REPLY_END); + unicast_reply_write(reply, HTTP_CHANNELS_REPLY_END); - unicast_reply_send(reply, Socket, 200, "text/html"); + unicast_reply_send(reply, Socket, 200, "text/html"); - if (0 != unicast_reply_free(reply)) { - log_message(MSG_INFO,"Unicast : Error when releasing the HTTP reply after sendinf it\n"); - return -1; - } + if (0 != unicast_reply_free(reply)) { + log_message(MSG_INFO,"Unicast : Error when releasing the HTTP reply after sendinf it\n"); + return -1; + } - return 0; + return 0; } @@ -963,7 +350,7 @@ unicast_send_play_list_unicast (int number_of_channels, mumudvb_channel_t *chann { int curr_channel; - struct unicast_reply* reply = unicast_reply_init(); + unicast_reply_t* reply = unicast_reply_init(); if (NULL == reply) { log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); return -1; @@ -998,7 +385,63 @@ unicast_send_play_list_unicast (int number_of_channels, mumudvb_channel_t *chann } } - unicast_reply_send(reply, Socket, 200, "audio/x-mpegurl"); + unicast_reply_send(reply, Socket, 200, "audio/x-mpegurl"); + + if (0 != unicast_reply_free(reply)) { + log_message(MSG_INFO,"Unicast : Error when releasing the HTTP reply after sendinf it\n"); + return -1; + } + + return 0; +} + +/** @brief Send a basic text file containig the RTSP playlist +* +* @param number_of_channels the number of channels +* @param channels the channels array +* @param Socket the socket on wich the information have to be sent +* @param perport says if the channel have to be given by the url /bynumber or by their port +*/ +int +unicast_send_play_list_unicast_rtsp (int number_of_channels, mumudvb_channel_t *channels, int Socket, int unicast_portOut, int rtsp_portOut, int perport) +{ + int curr_channel; + + unicast_reply_t* reply = unicast_reply_init(); + if (NULL == reply) { + log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); + return -1; + } + + //We get the ip address on which the client is connected + struct sockaddr_in tempSocketAddr; + unsigned int l; + l = sizeof(struct sockaddr); + getsockname(Socket, (struct sockaddr *) &tempSocketAddr, &l); + //we write the playlist + unicast_reply_write(reply, "#EXTM3U\r\n"); + + //"#EXTINF:0,title\r\nURL" + for (curr_channel = 0; curr_channel < number_of_channels; curr_channel++) + if (channels[curr_channel].streamed_channel_old) + { + if(!perport) + { + unicast_reply_write(reply, "#EXTINF:0,%s\r\nrtsp://%s:%d/bynumber/%d\r\n", + channels[curr_channel].name, + inet_ntoa(tempSocketAddr.sin_addr) , + rtsp_portOut , + curr_channel+1); + } + else if(channels[curr_channel].unicast_port) + { + unicast_reply_write(reply, "#EXTINF:0,%s\r\nrtsp://%s:%d/\r\n", + channels[curr_channel].name, + inet_ntoa(tempSocketAddr.sin_addr) , + channels[curr_channel].unicast_port); + } + } + unicast_reply_send(reply, Socket, 200, "audio/x-mpegurl"); if (0 != unicast_reply_free(reply)) { log_message(MSG_INFO,"Unicast : Error when releasing the HTTP reply after sendinf it\n"); @@ -1008,6 +451,7 @@ unicast_send_play_list_unicast (int number_of_channels, mumudvb_channel_t *chann return 0; } + /** @brief Send a basic text file containig the playlist * * @param number_of_channels the number of channels @@ -1022,7 +466,7 @@ unicast_send_play_list_multicast (int number_of_channels, mumudvb_channel_t *cha char vlcchar[2]; extern multicast_parameters_t multicast_vars; - struct unicast_reply* reply = unicast_reply_init(); + unicast_reply_t* reply = unicast_reply_init(); if (NULL == reply) { log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); return -1; @@ -1077,7 +521,7 @@ int unicast_send_streamed_channels_list_js (int number_of_channels, mumudvb_chan unicast_client_t *unicast_client=NULL; int clients=0; - struct unicast_reply* reply = unicast_reply_init(); + unicast_reply_t* reply = unicast_reply_init(); if (NULL == reply) { log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); return -1; @@ -1109,12 +553,12 @@ int unicast_send_streamed_channels_list_js (int number_of_channels, mumudvb_chan channels[curr_channel].service_id, service_type_to_str(channels[curr_channel].channel_type), channels[curr_channel].num_pids); - unicast_reply_write(reply, "\"pids\":["); - for(int i=0;iused_body -= 2; // dirty hack to erase the last comma - unicast_reply_write(reply, "]"); - unicast_reply_write(reply, "},\n"); + unicast_reply_write(reply, "\"pids\":["); + for(int i=0;iused_body -= 2; // dirty hack to erase the last comma + unicast_reply_write(reply, "]"); + unicast_reply_write(reply, "},\n"); } reply->used_body -= 2; // dirty hack to erase the last comma @@ -1138,7 +582,7 @@ int unicast_send_signal_power_js (int Socket, fds_t *fds) { int strength, ber, snr; - struct unicast_reply* reply = unicast_reply_init(); + unicast_reply_t* reply = unicast_reply_init(); if (NULL == reply) { log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); @@ -1174,7 +618,7 @@ unicast_send_channel_traffic_js (int number_of_channels, mumudvb_channel_t *chan int curr_channel; extern long real_start_time; - struct unicast_reply* reply = unicast_reply_init(); + unicast_reply_t* reply = unicast_reply_init(); if (NULL == reply) { log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); return -1; @@ -1201,6 +645,35 @@ unicast_send_channel_traffic_js (int number_of_channels, mumudvb_channel_t *chan +/** @brief Send a basic JSON file containig the clients +* +* @param number_of_clients the number of clients +* @param channels the channels array +* @param Socket the socket on wich the information have to be sent +*/ +int + unicast_send_channel_clients_js (int number_of_clients, int Socket) +{ + unicast_reply_t* reply = unicast_reply_init(); + if (NULL == reply) { + log_message(MSG_INFO,"Unicast : Error when creating the HTTP reply\n"); + return -1; + } + + unicast_reply_write(reply, "{\"number\":%d},\n", number_of_clients); + + unicast_reply_send(reply, Socket, 200, "application/json"); + + if (0 != unicast_reply_free(reply)) { + log_message(MSG_INFO,"Unicast : Error when releasing the HTTP reply after sendinf it\n"); + return -1; + } + return 0; +} + + + + diff --git a/src/unicast_http.h b/src/unicast_http.h index 2ec51b7c..08f7c3e4 100644 --- a/src/unicast_http.h +++ b/src/unicast_http.h @@ -26,25 +26,13 @@ * @brief HTML unicast headers */ -#ifndef _UNICAST_H -#define _UNICAST_H +#ifndef _UNICAST_HTTP_H +#define _UNICAST_HTTP_H #include "mumudvb.h" #include "unicast_queue.h" +#include "unicast.h" -/** @brief The different fd/socket types */ -enum - { - UNICAST_MASTER=1, - UNICAST_LISTEN_CHANNEL, - UNICAST_CLIENT, - }; - - - -#define RECV_BUFFER_MULTIPLE 100 -/**@brief the timeout for disconnecting a client with only consecutive errors*/ -#define UNICAST_CONSECUTIVE_ERROR_TIMEOUT 5 #define HTTP_OK_REPLY "HTTP/1.0 200 OK\r\n"\ @@ -88,104 +76,9 @@ enum "\r\n" -/** @brief A client connected to the unicast connection. - * - *There is two chained list of client : a global one wich contain all the clients. Another one in each channel wich contain the associated clients. - */ -typedef struct unicast_client_t{ - /**HTTP socket*/ - struct sockaddr_in SocketAddr; - /**HTTP socket*/ - int Socket; - /**Reception buffer*/ - char *buffer; - /**Size of the buffer*/ - int buffersize; - /**Position in the buffer*/ - int bufferpos; - /**Is there consecutive errors ?*/ - int consecutive_errors; - /**When the first consecutive error happeard*/ - long first_error_time; - /**Channel : -1 if not associated yet*/ - int channel; - /**Future channel : we will set the channel when we will receive the get*/ - int askedChannel; - /**Next client*/ - struct unicast_client_t *next; - /**Previous client*/ - struct unicast_client_t *prev; - /**Next client in the channel*/ - struct unicast_client_t *chan_next; - /**Previous client in the channel*/ - struct unicast_client_t *chan_prev; - /** The packets in queue*/ - unicast_queue_header_t queue; - /** The latest write error for this client*/ - int last_write_error; -}unicast_client_t; - - -/** @brief The information on the unicast file descriptors/sockets - * There is three kind of descriptors : - * The master connection : this connection will interpret the HTTP path asked, to give the channel, the channel list or debugging information - * Client connections : This is the connections for connected clients - * Channel listening connections : When a client connect to one of these sockets, the associated channel will be given directly without interpreting the PATH - * - * The numbering of this socket information is the same as the file descriptors numbering - */ -typedef struct unicast_fd_info_t{ - /**The fd/socket type*/ - int type; - /** The channel if it's a channel socket*/ - int channel; - /** The client if it's a client socket*/ - unicast_client_t *client; -}unicast_fd_info_t; - - -/** @brief The parameters for unicast -*/ -typedef struct unicast_parameters_t{ - /** Do we activate unicast ?*/ - int unicast; - /**The "HTTP" ip address*/ - char ipOut[20]; - /** The "HTTP" port*/ - int portOut; - /** The "HTTP" port string version before parsing*/ - char *portOut_str; - /** The HTTP input socket*/ - struct sockaddr_in sIn; - /** The HTTP input socket*/ - int socketIn; - /** The clients, contains all the clients, associated to a channel or not*/ - unicast_client_t *clients; - /** The number of connected clients*/ - int client_number; - /** The maximum number of simultaneous clients allowed*/ - int max_clients; - /** The timeout before disconnecting a client wich does only errors*/ - int consecutive_errors_timeout; - /** The information on the file descriptors : ie the type of FD, the client associated if it's a client fd, the channel if it's a channel fd */ - unicast_fd_info_t *fd_info; - /** The maximim size of the queue */ - int queue_max_size; -}unicast_parameters_t; - -int unicast_create_listening_socket(int socket_type, int socket_channel, char *ipOut, int port, struct sockaddr_in *sIn, int *socketIn, fds_t *fds, unicast_parameters_t *unicast_vars); - -int unicast_handle_fd_event(unicast_parameters_t *unicast_vars, fds_t *fds, mumudvb_channel_t *channels, int number_of_channels); - int unicast_del_client(unicast_parameters_t *unicast_vars, unicast_client_t *client, mumudvb_channel_t *channels); -int channel_add_unicast_client(unicast_client_t *client,mumudvb_channel_t *channel); - -void unicast_freeing(unicast_parameters_t *unicast_vars, mumudvb_channel_t *channels); - -int read_unicast_configuration(unicast_parameters_t *unicast_vars, mumudvb_channel_t *current_channel, int ip_ok, char *substring); - -void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *channels, fds_t *fds, unicast_parameters_t *unicast_vars); +int unicast_handle_http_message(unicast_parameters_t *unicast_vars, unicast_client_t *client, mumudvb_channel_t *channels, int num_of_channels, fds_t *fds); diff --git a/src/unicast_queue.c b/src/unicast_queue.c index 7dda9c30..315de9fc 100644 --- a/src/unicast_queue.c +++ b/src/unicast_queue.c @@ -1,7 +1,7 @@ /* - * MuMuDVB - UDP-ize a DVB transport stream. + * MuMuDVB - Stream a DVB transport stream. * - * (C) 2009 Brice DUBOST + * (C) 2009-2010 Brice DUBOST * * The latest version can be found at http://mumudvb.braice.net * @@ -25,7 +25,7 @@ /** @file * @brief File for HTTP unicast Queue and data sending * @author Brice DUBOST - * @date 2009 + * @date 2009-2010 */ #include @@ -38,12 +38,14 @@ #include "mumudvb.h" #include "errors.h" #include "log.h" +#include "unicast.h" +#include "unicast_common.h" int unicast_queue_remove_data(unicast_queue_header_t *header); int unicast_queue_add_data(unicast_queue_header_t *header, unsigned char *data, int data_len); unsigned char *unicast_queue_get_data(unicast_queue_header_t* , int* ); -void unicast_close_connection(unicast_parameters_t *unicast_vars, fds_t *fds, int Socket, mumudvb_channel_t *channels); + /** @brief Send the buffer for the channel * @@ -62,12 +64,31 @@ void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *cha int data_from_queue; int packets_left; struct timeval tv; + int clientsocket; + struct sockaddr_in clientsocketaddr; actual_client=actual_channel->clients; while(actual_client!=NULL) { - buffer=actual_channel->buf; - buffer_len=actual_channel->nb_bytes; + if(actual_client->client_type==CLIENT_HTTP) + { + buffer=actual_channel->buf; + buffer_len=actual_channel->nb_bytes; + clientsocket=actual_client->Socket; + clientsocketaddr=actual_client->SocketAddr; + } + else if(actual_client->client_type==CLIENT_RTSP) + { + buffer=actual_channel->buf_with_rtp_header; + buffer_len=actual_channel->nb_bytes+RTP_HEADER_LEN; + clientsocket=actual_client->rtsp_Socket; + clientsocketaddr=actual_client->rtsp_SocketAddr; + } + else + { + log_message(MSG_ERROR,"bug in the program please contact line %d file %s \n",__LINE__, __FILE__); + return; + } data_from_queue=0; if(actual_client->queue.packets_in_queue!=0) { @@ -82,8 +103,8 @@ void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *cha { actual_client->queue.full=1; log_message(MSG_DETAIL,"Unicast: The queue is full, we now throw away new packets for client %s:%d\n", - inet_ntoa(actual_client->SocketAddr.sin_addr), - actual_client->SocketAddr.sin_port); + inet_ntoa(clientsocketaddr.sin_addr), + ntohs(clientsocketaddr.sin_port)); } } buffer=unicast_queue_get_data(&actual_client->queue, &buffer_len); @@ -94,7 +115,7 @@ void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *cha while(packets_left>0) { //we send the data - written_len=write(actual_client->Socket,buffer, buffer_len); + written_len=write(clientsocket,buffer, buffer_len); //We check if all the data was successfully written if(written_lenlast_write_error) { log_message(MSG_DEBUG,"Unicast: New error when writing to client %s:%d : %s\n", - inet_ntoa(actual_client->SocketAddr.sin_addr), - actual_client->SocketAddr.sin_port, + inet_ntoa(clientsocketaddr.sin_addr), + ntohs(clientsocketaddr.sin_port), strerror(errno)); actual_client->last_write_error=errno; written_len=0; @@ -115,30 +136,38 @@ void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *cha else { log_message(MSG_DEBUG,"Unicast: Not all the data was written to %s:%d. Asked len : %d, written len %d\n", - inet_ntoa(actual_client->SocketAddr.sin_addr), - actual_client->SocketAddr.sin_port, + inet_ntoa(clientsocketaddr.sin_addr), + ntohs(clientsocketaddr.sin_port), actual_channel->nb_bytes, written_len); } - if(!data_from_queue) - { - //We store the non sent data in the queue - if((actual_client->queue.data_bytes_in_queue+buffer_len-written_len)< unicast_vars->queue_max_size) - { - unicast_queue_add_data(&actual_client->queue, buffer+written_len, buffer_len-written_len); - log_message(MSG_DEBUG,"Unicast: We start queuing packets ... \n"); - } - } + if(actual_client->client_type==CLIENT_RTSP) + { + log_message(MSG_DEBUG,"RTSP client, we close the connection\n"); + temp_client=actual_client->chan_next; + unicast_del_client(unicast_vars,actual_client, channels); + actual_client=temp_client; + continue; + } + if(!data_from_queue) + { + //We store the non sent data in the queue + if((actual_client->queue.data_bytes_in_queue+buffer_len-written_len)< unicast_vars->queue_max_size) + { + unicast_queue_add_data(&actual_client->queue, buffer+written_len, buffer_len-written_len); + log_message(MSG_DEBUG,"Unicast: We start queuing packets ... \n"); + } + } if(!actual_client->consecutive_errors) { log_message(MSG_DETAIL,"Unicast: Error when writing to client %s:%d : %s\n", - inet_ntoa(actual_client->SocketAddr.sin_addr), - actual_client->SocketAddr.sin_port, + inet_ntoa(clientsocketaddr.sin_addr), + ntohs(clientsocketaddr.sin_port), strerror(errno)); - gettimeofday (&tv, (struct timezone *) NULL); - actual_client->first_error_time = tv.tv_sec; - actual_client->consecutive_errors=1; + gettimeofday (&tv, (struct timezone *) NULL); + actual_client->first_error_time = tv.tv_sec; + actual_client->consecutive_errors=1; } else { @@ -147,11 +176,11 @@ void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *cha if((unicast_vars->consecutive_errors_timeout > 0) && (tv.tv_sec - actual_client->first_error_time) > unicast_vars->consecutive_errors_timeout) { log_message(MSG_INFO,"Unicast: Consecutive errors when writing to client %s:%d during too much time, we disconnect\n", - inet_ntoa(actual_client->SocketAddr.sin_addr), - actual_client->SocketAddr.sin_port); - temp_client=actual_client->chan_next; - unicast_close_connection(unicast_vars,fds,actual_client->Socket,channels); - actual_client=temp_client; + inet_ntoa(clientsocketaddr.sin_addr), + ntohs(clientsocketaddr.sin_port)); + temp_client=actual_client->chan_next; + unicast_close_connection(unicast_vars,fds,clientsocket,channels,1); + actual_client=temp_client; } } } @@ -161,8 +190,8 @@ void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *cha if (actual_client->consecutive_errors) { log_message(MSG_DETAIL,"Unicast: We can write again to client %s:%d\n", - inet_ntoa(actual_client->SocketAddr.sin_addr), - actual_client->SocketAddr.sin_port); + inet_ntoa(clientsocketaddr.sin_addr), + ntohs(clientsocketaddr.sin_port)); actual_client->consecutive_errors=0; actual_client->last_write_error=0; if(data_from_queue) @@ -186,8 +215,8 @@ void unicast_data_send(mumudvb_channel_t *actual_channel, mumudvb_channel_t *cha { packets_left=0; log_message(MSG_DEBUG,"Unicast: The queue is now empty :) client %s:%d \n", - inet_ntoa(actual_client->SocketAddr.sin_addr), - actual_client->SocketAddr.sin_port); + inet_ntoa(clientsocketaddr.sin_addr), + ntohs(clientsocketaddr.sin_port)); } } } diff --git a/src/unicast_queue.h b/src/unicast_queue.h index 21f8005e..7d1b4be0 100644 --- a/src/unicast_queue.h +++ b/src/unicast_queue.h @@ -53,20 +53,7 @@ typedef struct unicast_queue_header_t{ }unicast_queue_header_t; - void unicast_queue_clear(unicast_queue_header_t *header); - - - - - - - - - - - - #endif \ No newline at end of file diff --git a/src/unicast_rtsp.c b/src/unicast_rtsp.c new file mode 100644 index 00000000..38a8f5c8 --- /dev/null +++ b/src/unicast_rtsp.c @@ -0,0 +1,629 @@ +/* +* MuMuDVB - Stream a DVB transport stream. +* +* (C) 2010 Brice DUBOST +* +* The latest version can be found at http://mumudvb.braice.net +* +* Copyright notice: +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/** @file +* @brief File for RTSP unicast +* @author Brice DUBOST +* @date 2010 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unicast_rtsp.h" +#include "unicast_queue.h" +#include "mumudvb.h" +#include "errors.h" +#include "log.h" +#include "unicast.h" +#include "unicast_common.h" + +extern int Interrupted; + +//from unicast_client.c +int channel_add_unicast_client(unicast_client_t *client,mumudvb_channel_t *channel); +int unicast_del_client(unicast_parameters_t *unicast_vars, unicast_client_t *client, mumudvb_channel_t *channels); + + +/** @todo handle non keepalive clients, and auto disconnect to old clients without streaming */ + +int unicast_rtsp_describe_reply (int Socket, int CSeq); +int unicast_rtsp_options_reply (int Socket, int CSeq); +int unicast_rtsp_setup_reply (unicast_client_t *client, int CSeq, int Tsprt_type); +int unicast_rtsp_play_reply (int Socket, int CSeq, unicast_client_t *client, mumudvb_channel_t *channels, int number_of_channels); +int unicast_rtsp_teardown_reply (int Socket, int CSeq, unicast_client_t *client , mumudvb_channel_t *channels, unicast_parameters_t *unicast_vars, fds_t *fds, int number_of_channels); + + + +/** @brief Create a random number as session id +*/ +int unicast_rtsp_session(char *session) +{ + for (int i=0;i<15;i++) + { + float randomnum; + randomnum = rand(); + randomnum/=RAND_MAX; + session[i]='a'+(int)(randomnum*25); + } + session[15]='\0'; + return 0; +} + + +/** @brief Deal with an incoming message on the unicast client connection +* This function will store and answer the RTSP requests +* +* @param unicast_vars the unicast parameters +* @param client The client from which the message was received +* @param channels the channel array +* @param number_of_channels quite explicit ... +*/ +int unicast_handle_rtsp_message(unicast_parameters_t *unicast_vars, unicast_client_t *client, mumudvb_channel_t *channels, int number_of_channels, fds_t *fds) +{ + (void) unicast_vars; + (void) number_of_channels; + int iRet; + + iRet=unicast_new_message(client); + if(iRet) + { + log_message(MSG_DEBUG,"Unicast : problem with unicast_new_message iRet %d\n",iRet); + return iRet; + } + + //We search for the end of the RTSP request + if(!(strlen(client->buffer)>5 && strstr(client->buffer, "\n\r\n\0"))) + return 0; + + log_message(MSG_DEBUG,"Unicast : End of RTSP request, we parse it\n"); + + /* The client is initially supplied with an RTSP URL, on the form: + rtsp://server.address:port/object.sdp */ + + /** @todo important : test if the client already exist using the session number*/ + char *poscseq; + int CSeq=0; + int pos; + pos=0; + poscseq=strstr(client->buffer ,"CSeq:"); + log_message(MSG_FLOOD,"Buffer : %s\n",client->buffer); + if(poscseq==NULL) + { + log_message(MSG_ERROR,"Sequence number not found\n"); + /** @todo Implement an error following the RTSP protocol*/ + return CLOSE_CONNECTION; //tu n'est pas content ... + } + log_message(MSG_DEBUG,"CSeq found at position %d\n", (int)(poscseq-(client->buffer))); + pos=(int)(poscseq-(client->buffer)); + pos+=strlen("Cseq: "); + CSeq = atoi(client->buffer+pos); //replace by CSeq = atoi(poscseq+strlen("Cseq: ")); + log_message(MSG_INFO,"Cseq: %d\n", CSeq); + + if(strstr(client->buffer,"DESCRIBE")==client->buffer) + { + unicast_rtsp_describe_reply(client->Socket, CSeq); // send response trough RTSP Server socket + unicast_flush_client(client); + return 0; + } + else if(strstr(client->buffer,"OPTIONS")==client->buffer) + { + unicast_rtsp_options_reply(client->Socket, CSeq); // send response trough RTSP Server socket + unicast_flush_client(client); + return 0; + } + else if(strstr(client->buffer,"SETUP")==client->buffer) + { + pos=0; + char *pos_search; + char session[16]; + unicast_rtsp_session(session); + strcpy(client->session, session); + log_message(MSG_DEBUG,"Session : %s\n", session ); + + // We parse transport type + int TransportType=TRANSPORT_UNDEFINED; // RTP/AVP=0 | RTP/AVP/UDP = 0 , RTP/AVP/TCP=1 + pos_search=strstr(client->buffer ,"Transport:"); + if(pos_search==NULL) + { + log_message(MSG_DEBUG,"Transport type not found\n"); + return CLOSE_CONNECTION; + } + + log_message(MSG_DEBUG,"Transport type at position %d\n", (int)(pos_search-(client->buffer))); + pos=(int)(pos_search-(client->buffer)); + pos+=strlen("Transport: "); + if (!strncmp (client->buffer+pos, "RTP/AVP/TCP", strlen("RTP/AVP/TCP"))) { + TransportType=RTP_AVP_TCP; + log_message(MSG_INFO,"Transport: RTP/AVP/TCP\n"); + } + else if (!strncmp (client->buffer+pos, "RTP/AVP/UDP", strlen("RTP/AVP/UDP"))) + { + TransportType=RTP_AVP_UDP; + log_message(MSG_INFO,"Transport: RTP/AVP/UDP\n"); + } + else if (!strncmp (client->buffer+pos, "RTP/AVP", strlen("RTP/AVP"))) + { + TransportType=RTP_AVP_UDP; + log_message(MSG_INFO,"Transport: RTP/AVP\n"); + } + else + { + log_message(MSG_ERROR,"Error 461 : Unsupported transport type"); +/* rtsp_reply_prepare_headers(reply, 461, CSeq); + rtsp_send_reply(reply, Socket,"text/plain"); + + if (0 != unicast_reply_free(reply)) + { + log_message(MSG_INFO,"Unicast : Error when releasing the RTSP reply after sendinf it\n"); + return -1; + } +*/ + return CLOSE_CONNECTION; + } + + // Parse client ports + pos_search=strstr(client->buffer ,"client_port="); + log_message(MSG_FLOOD,"Buffer : %s\n",client->buffer); + if(pos_search==NULL) + { + log_message(MSG_DEBUG,"Client ports not found\n"); + /** @todo implement RTSP error + rtsp_reply_prepare_headers(reply, 451, CSeq); + rtsp_send_reply(reply, Socket); */ + + return CLOSE_CONNECTION; + } + pos=(int)(pos_search-(client->buffer)); + pos+=strlen("client_port="); + client->rtsp_client_port = atoi(client->buffer+pos); + log_message(MSG_INFO,"client_port= %d\n", client->rtsp_client_port); + + + + log_message(MSG_DETAIL,"Unicast : Client ip %s port %d \n",client->client_ip,(unsigned short) client->rtsp_client_port); + + client->rtsp_Socket=makeUDPsocket (client->client_ip, client->rtsp_client_port,&client->rtsp_SocketAddr); + log_message(MSG_DETAIL,"Unicast : New RTSP socket n°%d\n, d_IP %s, d_port:%d\n", client->rtsp_Socket, inet_ntoa(client->rtsp_SocketAddr.sin_addr), ntohs(client->rtsp_SocketAddr.sin_port)); + + struct sockaddr_in tempSocketAddr; + unsigned int l; + l = sizeof(struct sockaddr); + getsockname(client->Socket, (struct sockaddr *) &tempSocketAddr, &l); + l = sizeof(struct sockaddr_in); + struct sockaddr_in tempsin; + tempsin.sin_family = AF_INET; + tempsin.sin_port=htons(unicast_vars->rtsp_portOut); + tempsin.sin_addr=tempSocketAddr.sin_addr; + int iRet; + iRet=bind(client->rtsp_Socket,(struct sockaddr *) &tempsin,l); + if (iRet == 0) + { + log_message( MSG_ERROR,"bind failed : %s\n", strerror(errno) ); + } + //client->rtsp_server_port=ntohs(client->rtsp_SocketAddr.sin_port); + client->rtsp_server_port=unicast_vars->rtsp_portOut; + unicast_rtsp_setup_reply(client, CSeq, TransportType); // send response trough RTSP Server socket + unicast_flush_client(client); + return 0; + } + else if(strstr(client->buffer,"PLAY")==client->buffer) + { + iRet=unicast_rtsp_play_reply(client->Socket, CSeq, client, channels, number_of_channels); // send response trough RTSP Server socket + if(iRet) + return CLOSE_CONNECTION; + unicast_flush_client(client); + unicast_show(unicast_vars, channels); + +/** @todo important : test if the client already exist when a new client arrives, delete dead clients*/ + + return 0; + } + else if(strstr(client->buffer,"TEARDOWN")==client->buffer) + { + unicast_rtsp_teardown_reply(client->Socket, CSeq, client, channels, unicast_vars, fds, number_of_channels); + return CLOSE_CONNECTION; + } + else + { + // @todo return a 400 error + log_message(MSG_WARN,"This is not understood by the server\n"); + unicast_flush_client(client); + } + + return 0; +} + + +/** @brief Dump the filled buffer on the socket adding RTSP header informations +*/ +int rtsp_reply_prepare_headers(unicast_reply_t *reply, int code, int CSeq) +{ + //we add the header information + reply->type = REPLY_HEADER; + unicast_reply_write(reply, "RTSP/1.0 "); + switch(code) + { + case 200: + unicast_reply_write(reply, "200 OK\r\n"); + break; + case 400: + unicast_reply_write(reply, "400 Bad Request\r\n"); + break; + case 404: + unicast_reply_write(reply, "404 Not found\r\n"); + break; + case 451: + unicast_reply_write(reply, "451 Invalid parameter\r\n"); + break; + case 454: + unicast_reply_write(reply, "454 Session Not Found\r\n"); + break; + case 503: + unicast_reply_write(reply, "503 Too many clients\r\n"); + break; + default: + log_message(MSG_ERROR,"reply send with bad code please contact\n"); + return 0; + } + unicast_reply_write(reply, "CSeq: %d\r\n",CSeq); + unicast_reply_write(reply, "Server: mumudvb/" VERSION "\r\n"); + unicast_reply_write(reply, "Public: DESCRIBE, OPTIONS, SETUP, TEARDOWN, PLAY\r\n"); + reply->type = REPLY_BODY; + return 0; +} + +/** @brief Show all the clients sessions +* +* @param unicast_vars the unicast parameters +* @param channels : the channels structure +*/ +void unicast_show(unicast_parameters_t *unicast_vars, mumudvb_channel_t *channels) +{ + unicast_client_t *actual_client; + unicast_client_t *next_client; + int i=0; + + for(actual_client=unicast_vars->clients; actual_client != NULL; actual_client=next_client) + { + next_client= actual_client->next; + // if(client->client_type==CLIENT_RTSP && client->rtsp_Socket) + if (actual_client->client_type==CLIENT_RTSP) + { + log_message(MSG_DEBUG,"Unicast : Client n°%d, Session : %s\n", i, actual_client->session); + } + i++; + + } +} + +/** @brief Send a RTSP message + * The method add headers the to a reply message + */ +int rtsp_send_reply_sdp(unicast_reply_t *reply, int socket, const char* content_type) +{ + //we add the header information + reply->type = REPLY_HEADER; + if(reply->used_body) + { + unicast_reply_write(reply, "Content-type: %s\r\n", content_type); + unicast_reply_write(reply, "Content-length: %d\r\n", reply->used_body); + } + unicast_reply_write(reply, "\r\n"); /* end header */ + //we merge the header and the body + reply->buffer_header = realloc(reply->buffer_header, reply->used_header+reply->used_body); + memcpy(&reply->buffer_header[reply->used_header],reply->buffer_body,sizeof(char)*reply->used_body); + reply->used_header+=reply->used_body; + //now we write the data + int size = write(socket, reply->buffer_header, reply->used_header); + return size; +} + + +/** @brief Send a RTSP message + * The method add headers the to a reply message + */ +int rtsp_send_reply(unicast_reply_t *reply, int socket) +{ + //we add the header information + reply->type = REPLY_HEADER; + if(reply->used_body) + { + unicast_reply_write(reply, "Content-length: %d\r\n", reply->used_body); + } + unicast_reply_write(reply, "\r\n"); /* end header */ + //we merge the header and the body + reply->buffer_header = realloc(reply->buffer_header, reply->used_header+reply->used_body); + memcpy(&reply->buffer_header[reply->used_header],reply->buffer_body,sizeof(char)*reply->used_body); + reply->used_header+=reply->used_body; + //now we write the data + int size = write(socket, reply->buffer_header, reply->used_header); + return size; +} + + +/** @brief Send RTSP_DESCRIBE_REPLY + * The DESCRIBE method retrieves the description of a presentation or + * media object identified by the request URL from a server. It may use + * the Accept header to specify the description formats that the client + * understands. The server responds with a description of the requested + * resource. The DESCRIBE reply-response pair constitutes the media + * initialization phase of RTSP. + * @param Socket the socket on wich the information have to be sent + */ +int unicast_rtsp_describe_reply (int Socket, int CSeq) + { + unicast_reply_t* reply = unicast_reply_init(); + if (NULL == reply) + { + log_message(MSG_ERROR,"Unicast : Error when creating the RTSP reply\n"); + return -1; + } + log_message(MSG_INFO,"RTSP DESCRIBE request\n"); + + char *TransportType="RTP/AVP"; + //if (TransportType) TransportType="RTP/AVP/TCP"; // =0:UDP, =1:TCP + char *source_host = "MuMuDVB-server"; + + // time of day as Session ID and Session version value + struct timeval tv; + gettimeofday(&tv, NULL); + log_message(MSG_DEBUG,"Unicast : time of day: %d\n", tv.tv_sec); + + unicast_reply_write(reply, "v=0\r\n"); + unicast_reply_write(reply, "o=- %u %u IN IP4 %s\r\n", tv.tv_sec, tv.tv_sec, source_host); + unicast_reply_write(reply, "s=unknown\r\n"); + unicast_reply_write(reply, "i=unknown\r\n"); + unicast_reply_write(reply, "c=IN IP4 0.0.0.0\r\n"); + unicast_reply_write(reply, "t=0 0\r\n"); + unicast_reply_write(reply, "m=video 0 %s 33\r\n", TransportType); + + rtsp_reply_prepare_headers(reply, 200, CSeq); + rtsp_send_reply_sdp(reply, Socket,"application/sdp"); + + if (0 != unicast_reply_free(reply)) + { + log_message(MSG_ERROR,"Unicast : Error when releasing the RTSP reply after sendinf it\n"); + return -1; + } + + return 0; +} + + +/** @brief Send RTSP_OPTIONS_REPLY + * + * @param Socket the socket on wich the information have to be sent + */ +int unicast_rtsp_options_reply (int Socket, int CSeq) +{ + unicast_reply_t* reply = unicast_reply_init(); + if (NULL == reply) + { + log_message(MSG_ERROR,"Unicast : Error when creating the RTSP reply\n"); + return -1; + } + + log_message(MSG_INFO,"RTSP OPTIONS request\n"); ; + + rtsp_reply_prepare_headers(reply, 200, CSeq); + rtsp_send_reply(reply, Socket); + + if (0 != unicast_reply_free(reply)) + { + log_message(MSG_ERROR,"Unicast : Error when releasing the RTSP reply after sendinf it\n"); + return -1; + } + + return 0; +} + + +/** @brief Send RTSP_SETUP_REPLY + * + * @param Socket the socket on wich the information have to be sent + */ +int unicast_rtsp_setup_reply (unicast_client_t *client, int CSeq, int Tsprt_type) +{ + int Socket=client->Socket; + char TransportType[20], broadcast_type[20]; + unicast_reply_t* reply = unicast_reply_init(); + if (NULL == reply) + { + log_message(MSG_ERROR,"Unicast : Error when creating the RTSP reply\n"); + return -1; + } + + log_message(MSG_INFO,"RTSP SETUP request\n"); + + rtsp_reply_prepare_headers(reply, 200, CSeq); + reply->type = REPLY_HEADER; + unicast_reply_write(reply, "Session: %s\r\n", client->session); + + if (Tsprt_type==RTP_AVP_TCP) + strcpy(TransportType,"RTP/AVP/TCP"); + else + strcpy(TransportType,"RTP/AVP"); + + /* @todo Get the broadcast_type from the DESCRIBE request + if (Tsprt_type==2) broadcast_type="multicast"; + else broadcast_type="unicast"; */ + strcpy( broadcast_type,"unicast"); + log_message(MSG_DEBUG, "Transport: %s;%s;mode=play;destination=%s;client_port=%d-%d;server_port=%d\r\n", TransportType, broadcast_type, client->client_ip, client->rtsp_client_port, client->rtsp_client_port+1, client->rtsp_server_port); + unicast_reply_write(reply, "Transport: %s;%s;mode=play;destination=%s;client_port=%d-%d;server_port=%d\r\n", TransportType, broadcast_type, client->client_ip, client->rtsp_client_port, client->rtsp_client_port+1, client->rtsp_server_port); + rtsp_send_reply(reply, Socket); + + if (0 != unicast_reply_free(reply)) + { + log_message(MSG_ERROR,"Unicast : Error when releasing the RTSP reply after sendinf it\n"); + return -1; + } + + return 0; +} + +/** @brief Send RTSP_PLAY_REPLY + * + * @param Socket the socket on wich the information have to be sent + */ +int unicast_rtsp_play_reply (int Socket, int CSeq, unicast_client_t *client, mumudvb_channel_t *channels, int number_of_channels) +{ + int err404; + int requested_channel; + requested_channel=0; + char *tempstring; + err404=0; + unicast_reply_t* reply = unicast_reply_init(); + if (NULL == reply) + { + log_message(MSG_ERROR,"Unicast : Error when creating the RTSP reply\n"); + return -1; + } + + log_message(MSG_INFO,"Unicast : RTSP PLAY request\n"); + + tempstring=strstr(client->buffer,"rtsp://"); + tempstring=strstr(tempstring+7,"/"); + if(tempstring) + requested_channel=parse_channel_path(tempstring,&err404, number_of_channels, channels); + else + { + log_message(MSG_INFO,"Unicast : Malformed PLAY request, we return 404\n"); + err404=1; + } + if(!requested_channel) + err404=1; + if(err404) + { + log_message(MSG_INFO,"Unicast : Path not found i.e. 404\n"); + unicast_reply_write(reply, "Session: %s\r\n", client->session); + rtsp_reply_prepare_headers(reply, 404, CSeq); + rtsp_send_reply(reply, Socket); + if (0 != unicast_reply_free(reply)) { + log_message(MSG_ERROR,"Unicast : Error when releasing the RTSP reply after sendinf it\n"); + } + return CLOSE_CONNECTION; //to delete the client + } + + unicast_reply_write(reply, "Session: %s\r\n", client->session); + rtsp_reply_prepare_headers(reply, 200, CSeq); + rtsp_send_reply(reply, Socket); + + if(requested_channel) + { + log_message(MSG_INFO,"Unicast : PLAY channel %d\n",requested_channel); + // @toto Add client IP into the unicast list. + client->channel=requested_channel-1; + channel_add_unicast_client(client, &channels[requested_channel-1]); + } + + if (0 != unicast_reply_free(reply)) + { + log_message(MSG_INFO,"Unicast : Error when releasing the RTSP reply after sendinf it\n"); + return -1; + } + + return 0; +} + +/** @brief Send RTSP_PLAY_TEARDOWN + * + * @param Socket the socket on wich the information have to be sent + */ +int unicast_rtsp_teardown_reply (int Socket, int CSeq, unicast_client_t *client , mumudvb_channel_t *channels, unicast_parameters_t *unicast_vars, fds_t *fds, int number_of_channels) +{ + int err=0; + int requested_channel; + requested_channel=0; + char *tempstring; + + unicast_reply_t* reply = unicast_reply_init(); + if (NULL == reply) + { + log_message(MSG_ERROR,"Unicast : Error when creating the RTSP reply\n"); + return -1; + } + + log_message(MSG_INFO,"RTSP TEARDOWN request\n"); + + tempstring=strstr(client->buffer,"rtsp://"); + tempstring=strstr(tempstring+7,"/"); + if(tempstring) + requested_channel=parse_channel_path(tempstring,&err, number_of_channels, channels); + else + { + log_message(MSG_INFO,"Unicast : Malformed TEARDOWN request, we return 400\n"); + } + if(!requested_channel) + err=404; + if(err!=0) + { + log_message(MSG_INFO,"Unicast : Path not found i.e. 404\n"); + unicast_reply_write(reply, "Session: %s\r\n", client->session); + rtsp_reply_prepare_headers(reply, err, CSeq); + rtsp_send_reply(reply, Socket); + if (0 != unicast_reply_free(reply)) { + log_message(MSG_ERROR,"Unicast : Error when releasing the RTSP reply after sendinf it\n"); + } + return CLOSE_CONNECTION; //to delete the client + } + + unicast_reply_write(reply, "Session: %s\r\n", client->session); + rtsp_reply_prepare_headers(reply, 200, CSeq); + rtsp_send_reply(reply, Socket); + + if(requested_channel) + { + log_message(MSG_INFO,"Unicast : TEARDOWN for channel n°%d\n",requested_channel); + /* @todo Delete client IP in the socket list. + + log_message(MSG_DEBUG,"Unicast : We delete channel n°%d from the socket list\n", requested_channel-1); + unicast_del_client(unicast_vars, client, &channels[requested_channel-1]); */ + } + + if (0 != unicast_reply_free(reply)) + { + log_message(MSG_INFO,"Unicast : Error when releasing the RTSP reply after sendinf it\n"); + return -1; + } + + return 0; +} + diff --git a/src/unicast_rtsp.h b/src/unicast_rtsp.h new file mode 100644 index 00000000..782de834 --- /dev/null +++ b/src/unicast_rtsp.h @@ -0,0 +1,56 @@ +/* + * MuMuDVB - Stream a DVB transport stream. + * + * (C) 2010 Brice DUBOST + * + * The latest version can be found at http://mumudvb.braice.net + * + * Copyright notice: + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/**@file + * @brief RTSP unicast headers + */ + +#ifndef _UNICAST_RTSP_H +#define _UNICAST_RTSP_H + +#include "mumudvb.h" +#include "unicast_queue.h" +#include "unicast.h" + + +#define RTSP_503_REPLY "RTSP/1.0 503 Too many clients\r\n"\ + "\r\n" + +#define RTSP_454_REPLY "RTSP/1.0 454 Session Not Found\r\n"\ + "\r\n" + +#define RTSP_461_REPLY "RTSP/1.0 461 Unsupported transport\r\n"\ + "\r\n" + +enum +{ + TRANSPORT_UNDEFINED=0, + RTP_AVP_UDP, + RTP_AVP_TCP, +}; + +int unicast_handle_rtsp_message(unicast_parameters_t *unicast_vars, unicast_client_t *client, mumudvb_channel_t *channels, int number_of_channels, fds_t *fds); + + +#endif