summaryrefslogtreecommitdiff
path: root/src/input/input_rtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/input/input_rtp.c')
-rw-r--r--src/input/input_rtp.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/src/input/input_rtp.c b/src/input/input_rtp.c
new file mode 100644
index 000000000..88fa5784d
--- /dev/null
+++ b/src/input/input_rtp.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2000-2001 the xine project
+ *
+ * This file is part of xine, a unix video player.
+ *
+ * xine 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.
+ *
+ * xine 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Xine input plugin for multicast video streams.
+ *
+ *
+ * This is something of an experiment - it doesn't work well yet. Originally
+ * the intent was to read an rtp stream, from, for example, Cisco IP
+ * Tv. That's still a long term goal but RTP doesn't fit well in an input
+ * plugin because typically video is carried on one multicast group and audio
+ * in another - i.e it's already demultiplexed and an input plugin would
+ * actually have to reassemble the content. Now that demultiplexers are
+ * becomming separate loadable objects the right thing to do is to write an
+ * RTP demux plugin and a playlist plugin that handles SDP.
+ *
+ *
+ * In the meantime some experience with multicast video was wanted. Not
+ * having hardware available to construct a stream on the fly a server was
+ * written to multicast the contents of an mpeg program stream - it just
+ * reads a pack then transmits it at the appropriate time as follows.
+ *
+ * fd is open for read on mpeg stream, sock for write on a multicast socket.
+ *
+ * while (1) {
+ * /* read pack */
+ * read(fd, buf, 2048)
+ * /* end of stream */
+ * if (buf[3] == 0xb9)
+ * return 0;
+ *
+ * /* extract the system reference clock, srcb, from the pack */
+ *
+ * send_at = srcb/90000.0;
+ * while (time_now < send_at) {
+ * wait;
+ * }
+ * r = write(sock, buf, 2048);
+ * }
+ *
+ * One problem is that a stream from a DVD needs each pack sending
+ * at approx 2.5ms intervals which is a shorter interval than the
+ * standard linux clock. The RTC can be used for more finely grained
+ * timing.
+ *
+ * If you live in a non multicast friendly environment then the stream
+ * can be unicast.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#include "input_plugin.h"
+
+static int last_input_error;
+static int input_eof;
+static uint32_t xine_debug;
+
+typedef struct _input_buffer {
+ struct _input_buffer *next;
+ unsigned char *buf;
+} input_buffer;
+
+#define N_BUFFERS 128
+#define IBUFFER_SIZE 2048
+
+static int input_file_handle = -1;
+
+input_buffer *free_buffers;
+input_buffer **fifo_head;
+input_buffer fifo_tail;
+
+pthread_mutex_t buffer_mutex;
+pthread_cond_t buffer_notempty;
+
+static pthread_t reader_thread;
+
+static void * input_plugin_read_loop(void *);
+
+static int host_connect_attempt(struct in_addr ia, int port) {
+ int s=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ struct sockaddr_in sin;
+
+ if(s==-1) {
+ perror("socket");
+ return -1;
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr = ia;
+ sin.sin_port = htons(port);
+
+ /* datagram socket */
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin))) {
+ perror("bind failed");
+ exit(1);
+ }
+ /* multicast ? */
+ if ((ntohl(sin.sin_addr.s_addr) >> 28) == 0xe) {
+ struct ip_mreqn mreqn;
+
+ mreqn.imr_multiaddr.s_addr = sin.sin_addr.s_addr;
+ mreqn.imr_address.s_addr = INADDR_ANY;
+ mreqn.imr_ifindex = 0;
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreqn,sizeof(mreqn))) {
+ perror("setsockopt IP_ADD_MEMBERSHIP failed (multicast kernel?)");
+ exit(1);
+ }
+ }
+
+ return s;
+}
+
+static int host_connect(const char *host, int port) {
+ struct hostent *h;
+ int i;
+ int s;
+
+ h=gethostbyname(host);
+ if(h==NULL)
+ {
+ fprintf(stderr,"unable to resolve '%s'.\n", host);
+ return -1;
+ }
+
+
+ for(i=0; h->h_addr_list[i]; i++)
+ {
+ struct in_addr ia;
+ memcpy(&ia, h->h_addr_list[i],4);
+ s=host_connect_attempt(ia, port);
+ if(s != -1)
+ return s;
+ }
+ fprintf(stderr, "unable to connect to '%s'.\n", host);
+ return -1;
+}
+
+static void input_plugin_init (void) {
+ int bufn;
+
+ for (bufn = 0; bufn < N_BUFFERS; bufn++) {
+ input_buffer *buf = malloc(sizeof(input_buffer));
+ if (!buf) {
+ fprintf(stderr, "unable to allocate input buffer.\n");
+ exit(1);
+ }
+ buf->buf = malloc(IBUFFER_SIZE);
+ if (!buf->buf) {
+ fprintf(stderr, "unable to allocate input buffer.\n");
+ exit(1);
+ }
+ buf->next = free_buffers;
+ free_buffers = buf;
+ }
+}
+
+static int input_plugin_open (char *mrl) {
+ char *filename;
+ char *pptr;
+ int port = 7658;
+ pthread_attr_t thread_attrs;
+
+ if (!strncmp (mrl, "rtp:",4)) {
+ filename = &mrl[4];
+ } else if (!strncmp (mrl, "udp:",4)) {
+ filename = &mrl[4];
+ } else
+ return 0;
+
+ if(strncmp(filename, "//", 2)==0)
+ filename+=2;
+
+ printf ("Opening >%s<\n", filename);
+
+ pptr=strrchr(filename, ':');
+ if(pptr)
+ {
+ *pptr++=0;
+ sscanf(pptr,"%d", &port);
+ }
+
+ if (input_file_handle != -1)
+ close(input_file_handle);
+ input_file_handle = host_connect(filename, port);
+
+ if (input_file_handle == -1) {
+ return 0;
+ }
+
+ last_input_error = 0;
+ input_eof = 0;
+ fifo_tail.next = &fifo_tail;
+ fifo_head = &fifo_tail.next;
+
+ pthread_cond_init(&buffer_notempty, NULL);
+ pthread_attr_init(&thread_attrs);
+ pthread_attr_setdetachstate(&thread_attrs, PTHREAD_CREATE_DETACHED);
+ pthread_create(&reader_thread, &thread_attrs, input_plugin_read_loop, (void *)input_file_handle);
+ pthread_attr_destroy(&thread_attrs);
+
+ return 1;
+}
+
+static uint32_t input_plugin_read (char *buf, uint32_t nlen) {
+ input_buffer *ibuf;
+
+ pthread_mutex_lock (&buffer_mutex);
+ while (fifo_tail.next == &fifo_tail) {
+ if (input_eof) {
+ pthread_mutex_unlock (&buffer_mutex);
+ return 0;
+ }
+ if (last_input_error) {
+ pthread_mutex_unlock (&buffer_mutex);
+ return last_input_error;
+ }
+ pthread_cond_wait(&buffer_notempty, &buffer_mutex);
+ }
+ ibuf = fifo_tail.next;
+ fifo_tail.next = fifo_tail.next->next;
+
+ /* Is FIFO now empty */
+ if (fifo_tail.next == &fifo_tail)
+ fifo_head = &fifo_tail.next;
+
+ pthread_mutex_unlock (&buffer_mutex);
+
+ memcpy(buf, ibuf->buf, nlen < IBUFFER_SIZE ? nlen : IBUFFER_SIZE);
+
+ pthread_mutex_lock (&buffer_mutex);
+ ibuf->next = free_buffers;
+ free_buffers = ibuf;
+ pthread_mutex_unlock (&buffer_mutex);
+
+ return nlen < IBUFFER_SIZE ? nlen : IBUFFER_SIZE;
+}
+
+static void * input_plugin_read_loop(void *arg) {
+ int inf = (int) arg;
+ input_buffer *buf;
+ int r;
+ unsigned short seq = 0;
+ static int warned = 0;
+
+ char whirly[] = "/-\\|";
+ int gig = 0;
+
+ while (1) {
+ pthread_mutex_lock (&buffer_mutex);
+ /* we expect to be able to get a free buffer - possibly we
+ could be a bit more reasonable but this will do for now. */
+ if (!free_buffers) {
+ input_eof = 1;
+ if (!warned) {
+ printf("OUCH - ran out of buffers\n");
+ warned = 1;
+ }
+ pthread_cond_signal(&buffer_notempty);
+ continue;
+ }
+ warned = 0;
+ buf = free_buffers;
+ free_buffers = free_buffers->next;
+ pthread_mutex_unlock (&buffer_mutex);
+
+ /* printf("%c\r", whirly[(gig++ % 4)]); */
+ /* fflush(stdout); */
+ r = read(inf, buf->buf, IBUFFER_SIZE);
+ if (r < 0) {
+ /* descriptor may be closed by main thread */
+ if (r != EBADF)
+ last_input_error = r;
+ return 0;
+ }
+ if (r == 0) {
+ input_eof = 1;
+ return 0;
+ }
+
+ /* For now - check whether we're dropping input */
+ if (++seq != *(unsigned short *)buf->buf) {
+ printf("OUCH - dropped input packet %d %d\n", seq, *(unsigned short *)buf->buf);
+ seq = *(unsigned short *)buf->buf;
+ }
+ buf->buf[1] = buf->buf[0] = 0;
+ pthread_mutex_lock (&buffer_mutex);
+ buf->next = *fifo_head;
+ *fifo_head = buf;
+ fifo_head = &buf->next;
+ pthread_cond_signal(&buffer_notempty);
+ pthread_mutex_unlock (&buffer_mutex);
+ }
+}
+
+static off_t input_plugin_seek (off_t offset, int origin) {
+
+ return -1;
+}
+
+static uint32_t input_plugin_get_length (void) {
+ return 0;
+}
+
+static uint32_t input_plugin_get_capabilities (void) {
+ return 0;
+}
+
+static uint32_t input_plugin_get_blocksize (void) {
+ return 2048;
+}
+
+static void input_plugin_close (void) {
+ close(input_file_handle);
+ input_file_handle = -1;
+}
+
+static int input_plugin_eject (void) {
+ return 1;
+}
+
+static char *input_plugin_get_identifier (void) {
+ return "RTP";
+}
+
+static int input_plugin_is_branch_possible (char *next_mrl) {
+ return 0;
+}
+
+static input_plugin_t plugin_op = {
+ NULL,
+ NULL,
+ input_plugin_init,
+ input_plugin_open,
+ input_plugin_read,
+ input_plugin_seek,
+ input_plugin_get_length,
+ input_plugin_get_capabilities,
+ NULL,
+ input_plugin_get_blocksize,
+ input_plugin_eject,
+ input_plugin_close,
+ input_plugin_get_identifier,
+ NULL,
+ input_plugin_is_branch_possible,
+ NULL
+};
+
+input_plugin_t *input_plugin_getinfo(uint32_t dbglvl) {
+
+ xine_debug = dbglvl;
+
+ return &plugin_op;
+}