diff --git a/libmpdemux/Makefile b/libmpdemux/Makefile index 4057c677dd..6a7af65fa8 100644 --- a/libmpdemux/Makefile +++ b/libmpdemux/Makefile @@ -5,12 +5,12 @@ include ../config.mak SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c demux_mpg.c demux_viv.c demuxer.c dvdauth.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c frequencies.c demux_fli.c ifeq ($(STREAMING),yes) -SRCS += asf_streaming.c url.c http.c network.c +SRCS += asf_streaming.c url.c http.c network.c rtp.c endif OBJS = $(SRCS:.c=.o) INCLUDE = -I../loader $(CSS_INC) $(EXTRA_INC) -CFLAGS = $(OPTFLAGS) $(INCLUDE) +CFLAGS = -Wall $(OPTFLAGS) $(INCLUDE) .SUFFIXES: .c .o diff --git a/libmpdemux/network.c b/libmpdemux/network.c index 570cbfc2fe..0abb5ddb1e 100644 --- a/libmpdemux/network.c +++ b/libmpdemux/network.c @@ -23,6 +23,7 @@ #include "http.h" #include "url.h" #include "asf.h" +#include "rtp.h" static struct { char *mime_type; @@ -81,6 +82,30 @@ streaming_ctrl_free( streaming_ctrl_t *streaming_ctrl ) { free( streaming_ctrl ); } +int +read_rtp_from_server(int fd, char *buffer, int length) { + int ret; + int done=0; + fd_set set; + struct timeval tv; + struct rtpheader rh; + char *data; + int len; + static int got_first = 0; + static int sequence; + + if( buffer==NULL || length<0 ) return -1; + + getrtp2(fd, &rh, &data, &len); + if( got_first && rh.b.sequence != sequence+1 ) + printf("RTP packet sequence error! Expected: %d, received: %d\n", + sequence+1, rh.b.sequence); + got_first = 1; + sequence = rh.b.sequence; + memcpy(buffer, data, len); + return(len); +} + // Connect to a server using a TCP connection int connect2Server(char *host, int port) { @@ -254,6 +279,17 @@ extension=NULL; return DEMUXER_TYPE_UNKNOWN; } + // Checking for RTP + if( !strcasecmp(url->protocol, "rtp") ) { + if( url->port==0 ) + { + printf("You must enter a port number for RTP streams!\n"); + exit(1); //fixme + } + *fd_out=-1; + return DEMUXER_TYPE_UNKNOWN; + } + // Checking for ASF if( !strncasecmp(url->protocol, "mms", 3) ) { //if( url->port==0 ) url->port = 80; @@ -429,6 +465,100 @@ nop_streaming_start( stream_t *stream ) { return fd; } +// Start listening on a UDP port. If multicast, join the group. +int +rtp_open_socket( URL_t *url ) { + int fd; + int socket_server_fd; + int err, err_len; + fd_set set; + struct timeval tv; + struct sockaddr_in server_address; + struct ip_mreq mcast; + + printf("Listening for traffic on %s:%d ...\n", url->hostname, url->port ); + + socket_server_fd = socket(AF_INET, SOCK_DGRAM, 0); +// fcntl( socket_server_fd, F_SETFL, fcntl(socket_server_fd, F_GETFL) | O_NONBLOCK ); + if( socket_server_fd==-1 ) { + perror("Failed to create socket"); + return -1; + } + + if( isalpha(url->hostname[0]) ) { + struct hostent *hp =(struct hostent*)gethostbyname( url->hostname ); + if( hp==NULL ) { + printf("Counldn't resolve name: %s\n", url->hostname); + return -1; + } + memcpy( (void*)&server_address.sin_addr.s_addr, (void*)hp->h_addr, hp->h_length ); + } else { + inet_pton(AF_INET, url->hostname, &server_address.sin_addr); + } + server_address.sin_family=AF_INET; + server_address.sin_port=htons(url->port); + + if( bind( socket_server_fd, (struct sockaddr*)&server_address, sizeof(server_address) )==-1 ) { + if( errno!=EINPROGRESS ) { + perror("Failed to connect to server"); + close(socket_server_fd); + return -1; + } + } + if((ntohl(server_address.sin_addr.s_addr) >> 28) == 0xe) { + mcast.imr_multiaddr.s_addr = server_address.sin_addr.s_addr; + //mcast.imr_interface.s_addr = inet_addr("10.1.1.2"); + mcast.imr_interface.s_addr = 0; + if( setsockopt( socket_server_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast, sizeof(mcast))) { + perror("IP_ADD_MEMBERSHIP failed (do you have multicasting enabled in your kernel?)"); + return -1; + } + } + + //tv.tv_sec = 0; + //tv.tv_usec = (10 * 1000000); // 10 seconds timeout + FD_ZERO( &set ); + FD_SET( socket_server_fd, &set ); + //if( select(socket_server_fd+1, &set, NULL, NULL, &tv)>0 ) { + if( select(socket_server_fd+1, &set, NULL, NULL, NULL)>0 ) { + err_len = sizeof( err ); + getsockopt( socket_server_fd, SOL_SOCKET, SO_ERROR, &err, &err_len ); + if( err ) { + printf("Timeout! No data from host %s\n", url->hostname ); + printf("Socket error: %d\n", err ); + close(socket_server_fd); + return -1; + } + } + return socket_server_fd; +} + +int +rtp_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *streaming_ctrl ) { + return read_rtp_from_server( fd, buffer, size ); +} + +int +rtp_streaming_start( stream_t *stream ) { + streaming_ctrl_t *streaming_ctrl; + int fd; + + if( streaming_ctrl==NULL ) return -1; + streaming_ctrl = stream->streaming_ctrl; + fd = stream->fd; + + if( fd<0 ) { + fd = rtp_open_socket( (streaming_ctrl->url) ); + if( fd<0 ) return -1; + } + + streaming_ctrl->streaming_read = rtp_streaming_read; + streaming_ctrl->prebuffer_size = 180000; + streaming_ctrl->buffering = 0; //1; + streaming_ctrl->status = streaming_playing_e; + return fd; +} + int streaming_start(stream_t *stream, URL_t *url, int demuxer_type) { int ret=-1; @@ -443,6 +573,13 @@ streaming_start(stream_t *stream, URL_t *url, int demuxer_type) { // stream->streaming_ctrl->demuxer_type = demuxer_type; stream->fd = -1; + // For RTP streams, we usually don't know the stream type until we open it. + if( !strcmp( url->protocol, "rtp")) + { + stream->fd = rtp_streaming_start( stream ); + } + // For connection-oriented streams, we can usually determine the streaming type. + else switch( demuxer_type ) { case DEMUXER_TYPE_ASF: // Send the appropriate HTTP request diff --git a/libmpdemux/rtp.c b/libmpdemux/rtp.c new file mode 100644 index 0000000000..186ce2859b --- /dev/null +++ b/libmpdemux/rtp.c @@ -0,0 +1,193 @@ +/* Imported from the dvbstream-0.2 project */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MPEG-2 TS RTP stack */ + +#define DEBUG 1 +#include "rtp.h" + +void initrtp(struct rtpheader *foo) { /* fill in the MPEG-2 TS deefaults */ + /* Note: MPEG-2 TS defines a timestamping base frequency of 90000 Hz. */ + foo->b.v=2; + foo->b.p=0; + foo->b.x=0; + foo->b.cc=0; + foo->b.m=0; + foo->b.pt=33; /* MPEG-2 TS */ + foo->b.sequence=rand() & 65535; + foo->timestamp=rand(); + foo->ssrc=rand(); +} + +/* Send a single RTP packet, converting the RTP header to network byte order. */ +int sendrtp(int fd, struct sockaddr_in *sSockAddr, struct rtpheader *foo, char *data, int len) { + char *buf=(char*)alloca(len+sizeof(struct rtpheader)); + int *cast=(int *)foo; + int *outcast=(int *)buf; + outcast[0]=htonl(cast[0]); + outcast[1]=htonl(cast[1]); + memmove(outcast+2,data,len); + fprintf(stderr,"v=%x %x\n",foo->b.v,buf[0]); + return sendto(fd,buf,len+3,0,(struct sockaddr *)sSockAddr,sizeof(*sSockAddr)); +} + +int getrtp2(int fd, struct rtpheader *rh, char** data, int* lengthData) { + static char buf[1600]; + unsigned int intP; + char* charP = (char*) &intP; + int headerSize; + int lengthPacket; + lengthPacket=recv(fd,buf,1590,0); + if (lengthPacket==0) + exit(1); + if (lengthPacket<0) { + fprintf(stderr,"socket read error\n"); + exit(2); + } + if (lengthPacket<12) { + fprintf(stderr,"packet too small (%d) to be an rtp frame (>12bytes)\n", lengthPacket); + exit(3); + } + rh->b.v = (unsigned int) ((buf[0]>>6)&0x03); + rh->b.p = (unsigned int) ((buf[0]>>5)&0x01); + rh->b.x = (unsigned int) ((buf[0]>>4)&0x01); + rh->b.cc = (unsigned int) ((buf[0]>>0)&0x0f); + rh->b.m = (unsigned int) ((buf[1]>>7)&0x01); + rh->b.pt = (unsigned int) ((buf[1]>>0)&0x7f); + intP = 0; + memcpy(charP+2,&buf[2],2); + rh->b.sequence = ntohl(intP); + intP = 0; + memcpy(charP,&buf[4],4); + rh->timestamp = ntohl(intP); + + headerSize = 12 + 4*rh->b.cc; /* in bytes */ + + *lengthData = lengthPacket - headerSize; + *data = (char*) buf + headerSize; + + // fprintf(stderr,"Reading rtp: v=%x p=%x x=%x cc=%x m=%x pt=%x seq=%x ts=%x lgth=%d\n",rh->b.v,rh->b.p,rh->b.x,rh->b.cc,rh->b.m,rh->b.pt,rh->b.sequence,rh->timestamp,lengthPacket); + + return(0); +} + +/* Send a single RTP packet, converting the RTP header to network byte order. */ +int sendrtp2(int fd, struct sockaddr_in *sSockAddr, struct rtpheader *foo, char *data, int len) { + char *buf=(char*)alloca(len+72); + unsigned int intP; + char* charP = (char*) &intP; + int headerSize; + buf[0] = 0x00; + buf[0] |= ((((char) foo->b.v)<<6)&0xc0); + buf[0] |= ((((char) foo->b.p)<<5)&0x20); + buf[0] |= ((((char) foo->b.x)<<4)&0x10); + buf[0] |= ((((char) foo->b.cc)<<0)&0x0f); + buf[1] = 0x00; + buf[1] |= ((((char) foo->b.m)<<7)&0x80); + buf[1] |= ((((char) foo->b.pt)<<0)&0x7f); + intP = htonl(foo->b.sequence); + memcpy(&buf[2],charP+2,2); + intP = htonl(foo->timestamp); + memcpy(&buf[4],&intP,4); + /* SSRC: not implemented */ + buf[8] = 0x0f; + buf[9] = 0x0f; + buf[10] = 0x0f; + buf[11] = 0x0f; + headerSize = 12 + 4*foo->b.cc; /* in bytes */ + memcpy(buf+headerSize,data,len); + + // fprintf(stderr,"Sending rtp: v=%x p=%x x=%x cc=%x m=%x pt=%x seq=%x ts=%x lgth=%d\n",foo->b.v,foo->b.p,foo->b.x,foo->b.cc,foo->b.m,foo->b.pt,foo->b.sequence,foo->timestamp,len+headerSize); + + foo->b.sequence++; + return sendto(fd,buf,len+headerSize,0,(struct sockaddr *)sSockAddr,sizeof(*sSockAddr)); +} + + +int getrtp(int fd, struct rtpheader *rh, char** data, int* lengthData) { + static char buf[1600]; + int headerSize; + int lengthPacket; + int i; + + lengthPacket=recv(fd,buf,1590,0); + headerSize = 3; + *lengthData = lengthPacket - headerSize; + *data = (char*) buf + headerSize; + fprintf(stderr,"[%d] %02x %x\n",lengthPacket,buf[8],buf[0]); +} + +/* create a sender socket. */ +int makesocket(char *szAddr,unsigned short port,int TTL,struct sockaddr_in *sSockAddr) { + int iRet, iLoop = 1; + struct sockaddr_in sin; + char cTtl = (char)TTL; + char cLoop=0; + + int iSocket = socket( AF_INET, SOCK_DGRAM, 0 ); + + if (iSocket < 0) { + fprintf(stderr,"socket() failed.\n"); + exit(1); + } + + sSockAddr->sin_family = sin.sin_family = AF_INET; + sSockAddr->sin_port = sin.sin_port = htons(port); + sSockAddr->sin_addr.s_addr = inet_addr(szAddr); + + iRet = setsockopt(iSocket, SOL_SOCKET, SO_REUSEADDR, &iLoop, sizeof(int)); + if (iRet < 0) { + fprintf(stderr,"setsockopt SO_REUSEADDR failed\n"); + exit(1); + } + + iRet = setsockopt(iSocket, IPPROTO_IP, IP_MULTICAST_TTL, &cTtl, sizeof(char)); + if (iRet < 0) { + fprintf(stderr,"setsockopt IP_MULTICAST_TTL failed. multicast in kernel?\n"); + exit(1); + } + + cLoop = 1; /* !? */ + iRet = setsockopt(iSocket, IPPROTO_IP, IP_MULTICAST_LOOP, + &cLoop, sizeof(char)); + if (iRet < 0) { + fprintf(stderr,"setsockopt IP_MULTICAST_LOOP failed. multicast in kernel?\n"); + exit(1); + } + + return iSocket; +} + +/* create a receiver socket, i.e. join the multicast group. */ +int makeclientsocket(char *szAddr,unsigned short port,int TTL,struct sockaddr_in *sSockAddr) { + int socket=makesocket(szAddr,port,TTL,sSockAddr); + struct ip_mreq blub; + struct sockaddr_in sin; + unsigned int tempaddr; + sin.sin_family=AF_INET; + sin.sin_port=htons(port); + sin.sin_addr.s_addr=inet_addr(szAddr); + if (bind(socket,&sin,sizeof(sin))) { + perror("bind failed"); + exit(1); + } + tempaddr=inet_addr(szAddr); + if ((ntohl(tempaddr) >> 28) == 0xe) { + blub.imr_multiaddr.s_addr = inet_addr(szAddr); + blub.imr_interface.s_addr = 0; + if (setsockopt(socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,&blub,sizeof(blub))) { + perror("setsockopt IP_ADD_MEMBERSHIP failed (multicast kernel?)"); + exit(1); + } + } + return socket; +} + diff --git a/libmpdemux/rtp.h b/libmpdemux/rtp.h new file mode 100644 index 0000000000..3a37ced3dc --- /dev/null +++ b/libmpdemux/rtp.h @@ -0,0 +1,31 @@ +#ifndef _RTP_H +#define _RTP_H + +#include + +struct rtpbits { + unsigned int v:2; /* version: 2 */ + unsigned int p:1; /* is there padding appended: 0 */ + unsigned int x:1; /* number of extension headers: 0 */ + unsigned int cc:4; /* number of CSRC identifiers: 0 */ + unsigned int m:1; /* marker: 0 */ + unsigned int pt:7; /* payload type: 33 for MPEG2 TS - RFC 1890 */ + unsigned int sequence:16; /* sequence number: random */ +}; + +struct rtpheader { /* in network byte order */ + struct rtpbits b; + int timestamp; /* start: random */ + int ssrc; /* random */ +}; + + +void initrtp(struct rtpheader *foo); /* fill in the MPEG-2 TS deefaults */ +int sendrtp(int fd, struct sockaddr_in *sSockAddr, struct rtpheader *foo, char *data, int len); +int getrtp2(int fd, struct rtpheader *rh, char** data, int* lengthData); +int sendrtp2(int fd, struct sockaddr_in *sSockAddr, struct rtpheader *foo, char *data, int len); +int getrtp(int fd, struct rtpheader *rh, char** data, int* lengthData); +int makesocket(char *szAddr,unsigned short port,int TTL,struct sockaddr_in *sSockAddr); +int makeclientsocket(char *szAddr,unsigned short port,int TTL,struct sockaddr_in *sSockAddr); + +#endif