summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/xine.h.in4
-rw-r--r--src/demuxers/Makefile.am7
-rw-r--r--src/demuxers/demux_slave.c462
-rw-r--r--src/input/input_net.c34
-rw-r--r--src/xine-engine/Makefile.am4
-rw-r--r--src/xine-engine/broadcaster.c375
-rw-r--r--src/xine-engine/broadcaster.h49
-rw-r--r--src/xine-engine/buffer.c8
-rw-r--r--src/xine-engine/buffer.h10
-rw-r--r--src/xine-engine/metronom.c4
-rw-r--r--src/xine-engine/metronom.h3
-rw-r--r--src/xine-engine/xine.c8
-rw-r--r--src/xine-engine/xine_interface.c18
-rw-r--r--src/xine-engine/xine_internal.h4
14 files changed, 967 insertions, 23 deletions
diff --git a/include/xine.h.in b/include/xine.h.in
index 84c37c9d6..9c43ac71c 100644
--- a/include/xine.h.in
+++ b/include/xine.h.in
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: xine.h.in,v 1.83 2003/05/09 17:57:14 miguelfreitas Exp $
+ * $Id: xine.h.in,v 1.84 2003/05/15 20:23:16 miguelfreitas Exp $
*
* public xine-lib (libxine) interface and documentation
*
@@ -291,7 +291,7 @@ int xine_get_param (xine_stream_t *stream, int param);
#define XINE_PARAM_IGNORE_VIDEO 13 /* disable video decoding */
#define XINE_PARAM_IGNORE_AUDIO 14 /* disable audio decoding */
#define XINE_PARAM_IGNORE_SPU 15 /* disable spu decoding */
-#define XINE_PARAM_ENABLE_BROADCAST 16 /* (not implemented yet) */
+#define XINE_PARAM_BROADCASTER_PORT 16 /* 0: disable, x: server port */
#define XINE_PARAM_METRONOM_PREBUFFER 17 /* unit: 1/90000 sec */
#define XINE_PARAM_EQ_30HZ 18 /* equalizer gains -100..100 */
#define XINE_PARAM_EQ_60HZ 19 /* equalizer gains -100..100 */
diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am
index 001e3b035..a76069964 100644
--- a/src/demuxers/Makefile.am
+++ b/src/demuxers/Makefile.am
@@ -41,7 +41,8 @@ lib_LTLIBRARIES = $(ogg_module) $(asf_module) $(mng_module) $(image_module) \
xineplug_dmx_real.la \
xineplug_dmx_rawdv.la \
xineplug_dmx_pva.la \
- xineplug_dmx_yuv_frames.la
+ xineplug_dmx_yuv_frames.la \
+ xineplug_dmx_slave.la
xineplug_dmx_ogg_la_SOURCES = demux_ogg.c
xineplug_dmx_ogg_la_LIBADD = $(OGG_LIBS) $(VORBIS_LIBS) $(THEORA_LIBS) $(XINE_LIB)
@@ -123,6 +124,10 @@ xineplug_dmx_yuv_frames_la_SOURCES = demux_yuv_frames.c
xineplug_dmx_yuv_frames_la_LIBADD = $(XINE_LIB)
xineplug_dmx_yuv_frames_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+xineplug_dmx_slave_la_SOURCES = demux_slave.c
+xineplug_dmx_slave_la_LIBADD = $(XINELIB)
+xineplug_dmx_slave_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+
xineplug_dmx_image_la_SOURCES = demux_image.c
xineplug_dmx_image_la_LIBADD = $(XINE_LIB)
xineplug_dmx_image_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
diff --git a/src/demuxers/demux_slave.c b/src/demuxers/demux_slave.c
new file mode 100644
index 000000000..8a0152367
--- /dev/null
+++ b/src/demuxers/demux_slave.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2000-2003 the xine project
+ * May 2003 - Miguel Freitas
+ * This plugin was sponsored by 1Control
+ *
+ * This file is part of xine, a free 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
+ *
+ * $Id: demux_slave.c,v 1.2 2003/05/15 20:23:16 miguelfreitas Exp $
+ *
+ * demuxer for slave "protocol"
+ * master xine must be started with XINE_PARAM_BROADCASTER_PORT set, that is,
+ * 'xine --broadcast-port <port_number>'
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "compat.h"
+#include "demux.h"
+
+#define SCRATCH_SIZE 1024
+#define CHECK_VPTS_INTERVAL 2*90000
+#define NETWORK_PREBUFFER 90000
+
+typedef struct {
+
+ demux_plugin_t demux_plugin;
+
+ xine_stream_t *stream;
+
+ config_values_t *config;
+
+ fifo_buffer_t *video_fifo;
+ fifo_buffer_t *audio_fifo;
+
+ input_plugin_t *input;
+
+ int64_t last_vpts;
+ int send_newpts;
+ int status;
+
+ /* additional decoder flags and other dec-spec. stuff */
+ uint32_t decoder_info[BUF_NUM_DEC_INFO];
+ /* pointers to dec-spec. stuff */
+ void *decoder_info_ptr[BUF_NUM_DEC_INFO];
+ xine_list_t *dec_infos; /* dec-spec. stuff */
+
+ uint8_t scratch[SCRATCH_SIZE+1];
+ int scratch_used;
+
+} demux_slave_t ;
+
+typedef struct {
+
+ demux_class_t demux_class;
+
+ /* class-wide, global variables here */
+
+ xine_t *xine;
+ config_values_t *config;
+} demux_slave_class_t;
+
+
+#define MAX_COMMAND_SIZE 20
+
+static int demux_slave_next (demux_slave_t *this) {
+ buf_element_t *buf;
+ int n, i;
+ char fifo_name[11];
+ uint8_t *p, *s;
+ int64_t curvpts;
+
+ /* fill the scratch buffer */
+ n = this->input->read(this->input, &this->scratch[this->scratch_used],
+ SCRATCH_SIZE - this->scratch_used);
+ this->scratch_used += n;
+ this->scratch[this->scratch_used] = '\0';
+
+ if( !n ) {
+ printf("demux_slave: connection closed\n");
+ this->status = DEMUX_FINISHED;
+ return 0;
+ }
+
+ p = strchr(this->scratch,'\n');
+ s = strchr(this->scratch,' ');
+
+ if( !s || s > p )
+ s = p;
+
+ if( !p || !s || (s-this->scratch+1) > MAX_COMMAND_SIZE ) {
+ printf("demux_slave: protocol error\n");
+ this->status = DEMUX_FINISHED;
+ return 0;
+ }
+
+ *s++ = '\0';
+ p++;
+
+ if( !strcmp(this->scratch,"buffer") ) {
+ int32_t size ; /* size of _content_ */
+ uint32_t type;
+ int64_t pts; /* presentation time stamp, used for a/v sync */
+ int64_t disc_off; /* discontinuity offset */
+ uint32_t decoder_flags; /* stuff like keyframe, is_header ... see below */
+
+ if( sscanf(s,"fifo=%10s size=%d type=%u pts=%lld disc=%lld flags=%u",
+ fifo_name, &size, &type, &pts, &disc_off, &decoder_flags) != 6 ) {
+ printf("demux_slave: 'buffer' command error\n");
+ this->status = DEMUX_FINISHED;
+ return 0;
+ }
+
+ if( type == BUF_CONTROL_NEWPTS ) {
+ this->send_newpts = 0;
+ this->last_vpts = 0;
+ }
+
+ /* if we join an already existing broadcaster we must take care
+ * of the initial pts.
+ */
+ if( pts && this->send_newpts ) {
+ xine_demux_control_newpts( this->stream, pts, 0 );
+ this->send_newpts = 0;
+ }
+
+ /* check if we are not late on playback.
+ * that might happen if user hits "pause" on the master, for example.
+ */
+ if( pts &&
+ (curvpts = this->stream->xine->clock->get_current_time(this->stream->xine->clock)) >
+ (this->last_vpts + CHECK_VPTS_INTERVAL) ) {
+ if( this->last_vpts &&
+ pts - (NETWORK_PREBUFFER/2) +
+ this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET) <
+ curvpts ) {
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ printf("demux_slave: we are running late, forcing newpts.\n");
+ xine_demux_control_newpts( this->stream, pts - NETWORK_PREBUFFER, 0 );
+ }
+ this->last_vpts = curvpts;
+ }
+
+
+ if( !strcmp(fifo_name,"video") || !this->audio_fifo )
+ buf = this->video_fifo->buffer_pool_alloc(this->video_fifo);
+ else
+ buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo);
+
+ /* copy data to buf, either from stratch or network */
+ n = this->scratch_used - (p-this->scratch);
+ if( n > size )
+ n = size;
+ if( n )
+ memcpy(buf->content, p, n);
+ if( n < size )
+ this->input->read(this->input, &buf->content[n], size-n);
+
+ p += n;
+ n = this->scratch_used - (p-this->scratch);
+ if( n )
+ memmove(this->scratch, p, n);
+ this->scratch_used = n;
+
+ /* populate our buf */
+ buf->size = size;
+ buf->type = type;
+ buf->pts = pts;
+ buf->disc_off = disc_off;
+ buf->decoder_flags = decoder_flags;
+
+ /* set decoder info */
+ for( i = 0; i < BUF_NUM_DEC_INFO; i++ ) {
+ buf->decoder_info[i] = this->decoder_info[i];
+ buf->decoder_info_ptr[i] = this->decoder_info_ptr[i];
+ }
+ memset(this->decoder_info, 0, sizeof(this->decoder_info));
+ memset(this->decoder_info_ptr, 0, sizeof(this->decoder_info_ptr));
+
+ if( !strcmp(fifo_name,"video") )
+ this->video_fifo->put(this->video_fifo, buf);
+ else if (this->audio_fifo)
+ this->audio_fifo->put(this->audio_fifo, buf);
+ else
+ buf->free_buffer(buf);
+
+ } else if( !strcmp(this->scratch,"decoder_info") ) {
+
+ uint32_t decoder_info;
+ int has_data;
+ int size;
+
+ if( sscanf(s,"index=%d decoder_info=%u has_data=%d",
+ &i, &decoder_info, &has_data) != 3 ||
+ i < 0 || i > BUF_NUM_DEC_INFO) {
+ printf("demux_slave: 'decoder_info' command error\n");
+ this->status = DEMUX_FINISHED;
+ return 0;
+ }
+
+ this->decoder_info[i] = decoder_info;
+
+ size = (has_data) ? decoder_info : 0;
+
+ if( size ) {
+ this->decoder_info_ptr[i] = malloc(size);
+ xine_list_append_content(this->dec_infos, this->decoder_info_ptr[i]);
+ }
+
+ n = this->scratch_used - (p-this->scratch);
+ if( n > size )
+ n = size;
+ if( n )
+ memcpy(this->decoder_info_ptr[i], p, n);
+ if( n < size )
+ this->input->read(this->input, (char *)this->decoder_info_ptr[i]+n, size-n);
+
+ p += n;
+ n = this->scratch_used - (p-this->scratch);
+ if( n )
+ memmove(this->scratch, p, n);
+ this->scratch_used = n;
+
+
+ } else if( !strcmp(this->scratch,"flush_engine") ) {
+
+ xine_demux_flush_engine( this->stream );
+ n = this->scratch_used - (p-this->scratch);
+ if( n )
+ memmove(this->scratch, p, n);
+ this->scratch_used = n;
+
+ } else {
+ printf("demux_slave: unknown command '%s'\n", this->scratch);
+ n = this->scratch_used - (p-this->scratch);
+ if( n )
+ memmove(this->scratch, p, n);
+ this->scratch_used = n;
+ }
+
+ return 1;
+}
+
+static int demux_slave_send_chunk (demux_plugin_t *this_gen) {
+
+ demux_slave_t *this = (demux_slave_t *) this_gen;
+
+ demux_slave_next(this);
+
+ return this->status;
+}
+
+static int demux_slave_get_status (demux_plugin_t *this_gen) {
+ demux_slave_t *this = (demux_slave_t *) this_gen;
+
+ return this->status;
+}
+
+
+static void demux_slave_send_headers (demux_plugin_t *this_gen) {
+
+ demux_slave_t *this = (demux_slave_t *) this_gen;
+
+ this->video_fifo = this->stream->video_fifo;
+ this->audio_fifo = this->stream->audio_fifo;
+
+ xine_demux_control_start(this->stream);
+
+ this->status = DEMUX_OK;
+
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
+
+ this->last_vpts = 0;
+ this->send_newpts = 1;
+}
+
+static int demux_slave_seek (demux_plugin_t *this_gen,
+ off_t start_pos, int start_time) {
+
+ demux_slave_t *this = (demux_slave_t *) this_gen;
+
+ return this->status;
+}
+
+static void demux_slave_dispose (demux_plugin_t *this_gen) {
+ demux_slave_t *this = (demux_slave_t *) this_gen;
+ void *data;
+
+ /* free all decoder information */
+ data = xine_list_first_content (this->dec_infos);
+ while (data) {
+ free(data);
+ xine_list_delete_current (this->dec_infos);
+ if( this->dec_infos->cur )
+ data = this->dec_infos->cur->content;
+ else
+ data = NULL;
+ }
+ xine_list_free(this->dec_infos);
+
+ free (this);
+}
+
+static int demux_slave_get_stream_length(demux_plugin_t *this_gen) {
+ return 0 ; /*FIXME: implement */
+}
+
+static uint32_t demux_slave_get_capabilities(demux_plugin_t *this_gen) {
+ return DEMUX_CAP_NOCAP;
+}
+
+static int demux_slave_get_optional_data(demux_plugin_t *this_gen,
+ void *data, int data_type) {
+ return DEMUX_OPTIONAL_UNSUPPORTED;
+}
+
+
+static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream,
+ input_plugin_t *input_gen) {
+
+ input_plugin_t *input = (input_plugin_t *) input_gen;
+ demux_slave_t *this;
+ static char slave_id_str[] = "master xine v1\n";
+ int len;
+
+ this = xine_xmalloc (sizeof (demux_slave_t));
+
+ switch (stream->content_detection_method) {
+
+ case METHOD_BY_EXTENSION: {
+ char *mrl;
+
+ mrl = input->get_mrl (input);
+
+ if(!strncmp(mrl, "slave://", 8))
+ break;
+
+ free (this);
+ return NULL;
+ }
+
+ case METHOD_BY_CONTENT: {
+
+ if ( (len = xine_demux_read_header(input, this->scratch, SCRATCH_SIZE)) > 0) {
+ if (!strncmp(this->scratch,slave_id_str,strlen(slave_id_str)))
+ break;
+ }
+
+ free (this);
+ return NULL;
+ }
+
+ case METHOD_EXPLICIT:
+ break;
+
+ default:
+ free (this);
+ return NULL;
+ }
+
+ this->stream = stream;
+ this->input = input;
+ this->dec_infos = xine_list_new();
+
+ this->demux_plugin.send_headers = demux_slave_send_headers;
+ this->demux_plugin.send_chunk = demux_slave_send_chunk;
+ this->demux_plugin.seek = demux_slave_seek;
+ this->demux_plugin.dispose = demux_slave_dispose;
+ this->demux_plugin.get_status = demux_slave_get_status;
+ this->demux_plugin.get_stream_length = demux_slave_get_stream_length;
+ this->demux_plugin.get_video_frame = NULL;
+ this->demux_plugin.got_video_frame_cb= NULL;
+ this->demux_plugin.get_capabilities = demux_slave_get_capabilities;
+ this->demux_plugin.get_optional_data = demux_slave_get_optional_data;
+ this->demux_plugin.demux_class = class_gen;
+
+ this->status = DEMUX_FINISHED;
+
+ this->input->read(this->input, this->scratch,strlen(slave_id_str));
+ this->scratch_used = 0;
+
+ memset(this->decoder_info, 0, sizeof(this->decoder_info));
+ memset(this->decoder_info_ptr, 0, sizeof(this->decoder_info_ptr));
+
+ return &this->demux_plugin;
+}
+
+static char *get_description (demux_class_t *this_gen) {
+ return "";
+}
+
+static char *get_identifier (demux_class_t *this_gen) {
+ return "slave";
+}
+
+static char *get_extensions (demux_class_t *this_gen) {
+ return "";
+}
+
+static char *get_mimetypes (demux_class_t *this_gen) {
+ return NULL;
+}
+
+static void class_dispose (demux_class_t *this_gen) {
+
+ demux_slave_class_t *this = (demux_slave_class_t *) this_gen;
+
+ free (this);
+}
+
+static void *init_plugin (xine_t *xine, void *data) {
+
+ demux_slave_class_t *this;
+
+ this = xine_xmalloc (sizeof (demux_slave_class_t));
+ this->config = xine->config;
+ this->xine = xine;
+
+ this->demux_class.open_plugin = open_plugin;
+ this->demux_class.get_description = get_description;
+ this->demux_class.get_identifier = get_identifier;
+ this->demux_class.get_mimetypes = get_mimetypes;
+ this->demux_class.get_extensions = get_extensions;
+ this->demux_class.dispose = class_dispose;
+
+ return this;
+}
+
+/*
+ * exported plugin catalog entry
+ */
+
+plugin_info_t xine_plugin_info[] = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_DEMUX, 21, "slave", XINE_VERSION_CODE, NULL, init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
diff --git a/src/input/input_net.c b/src/input/input_net.c
index 9978d37bd..48bba37e6 100644
--- a/src/input/input_net.c
+++ b/src/input/input_net.c
@@ -20,7 +20,7 @@
* Read from a tcp network stream over a lan (put a tweaked mp1e encoder the
* other end and you can watch tv anywhere in the house ..)
*
- * $Id: input_net.c,v 1.46 2003/04/26 22:34:32 guenter Exp $
+ * $Id: input_net.c,v 1.47 2003/05/15 20:23:17 miguelfreitas Exp $
*
* how to set up mp1e for use with this plugin:
*
@@ -84,7 +84,8 @@ typedef struct {
int fh;
char *mrl;
-
+ char *host_port;
+
char preview[MAX_PREVIEW_SIZE];
off_t preview_size;
@@ -206,12 +207,10 @@ static off_t net_plugin_read (input_plugin_t *this_gen,
static buf_element_t *net_plugin_read_block (input_plugin_t *this_gen,
fifo_buffer_t *fifo, off_t todo) {
- net_input_plugin_t *this = (net_input_plugin_t *) this_gen;
+ /* net_input_plugin_t *this = (net_input_plugin_t *) this_gen; */
buf_element_t *buf = fifo->buffer_pool_alloc (fifo);
off_t total_bytes;
- nbc_check_buffers (this->nbc);
-
buf->content = buf->mem;
buf->type = BUF_DEMUX_BLOCK;
@@ -318,6 +317,7 @@ static void net_plugin_dispose (input_plugin_t *this_gen ) {
}
free (this->mrl);
+ free (this->host_port);
if (this->nbc) {
nbc_close (this->nbc);
@@ -333,7 +333,7 @@ static int net_plugin_open (input_plugin_t *this_gen ) {
char *pptr;
int port = 7658;
- filename = (char *) &this->mrl[6];
+ filename = this->host_port;
pptr=strrchr(filename, ':');
if(pptr) {
*pptr++ = 0;
@@ -359,6 +359,7 @@ static int net_plugin_open (input_plugin_t *this_gen ) {
static input_plugin_t *net_class_get_instance (input_class_t *cls_gen, xine_stream_t *stream, const char *mrl) {
/* net_input_plugin_t *this = (net_input_plugin_t *) this_gen; */
net_input_plugin_t *this;
+ nbc_t *nbc = NULL;
char *filename;
if (!strncasecmp (mrl, "tcp://", 6)) {
@@ -367,16 +368,35 @@ static input_plugin_t *net_class_get_instance (input_class_t *cls_gen, xine_stre
if((!filename) || (strlen(filename) == 0)) {
return NULL;
}
+
+ nbc = nbc_init (stream);
+
+ } else if (!strncasecmp (mrl, "slave://", 8)) {
+
+ filename = (char *) &mrl[8];
+
+ if((!filename) || (strlen(filename) == 0)) {
+ return NULL;
+ }
+
+ /* the only difference for slave:// is that network buffering control
+ * is not used. otherwise, dvd still menus are not displayed (it freezes
+ * with "buffering..." all the time)
+ */
+
+ nbc = NULL;
+
} else {
return NULL;
}
this = xine_xmalloc(sizeof(net_input_plugin_t));
this->mrl = strdup(mrl);
+ this->host_port = strdup(filename);
this->stream = stream;
this->fh = -1;
this->curpos = 0;
- this->nbc = nbc_init (this->stream);
+ this->nbc = nbc;
this->preview_size = 0;
this->input_plugin.open = net_plugin_open;
diff --git a/src/xine-engine/Makefile.am b/src/xine-engine/Makefile.am
index d28686976..e1b8ecd68 100644
--- a/src/xine-engine/Makefile.am
+++ b/src/xine-engine/Makefile.am
@@ -13,7 +13,7 @@ libxine_la_SOURCES = xine.c metronom.c configfile.c buffer.c \
load_plugins.c video_decoder.c buffer_types.c \
audio_decoder.c video_out.c audio_out.c resample.c events.c lrb.c \
video_overlay.c osd.c scratch.c locale.c demux.c vo_scale.c \
- xine_interface.c post.c tvmode.c
+ xine_interface.c post.c tvmode.c broadcaster.c
if HAVE_NVTV
libxine_la_DEPENDENCIES = @INTLLIBS@ $(XINEUTILS_LIB) $(NVTVCLIENT_LIB)
@@ -32,7 +32,7 @@ libxine_la_LDFLAGS = \
include_HEADERS = buffer.h metronom.h configfile.h vo_scale.h \
audio_out.h resample.h video_out.h xine_internal.h spu_decoder.h \
lrb.h video_overlay.h osd.h scratch.h xine_plugin.h xineintl.h \
- plugin_catalog.h audio_decoder.h video_decoder.h post.h
+ plugin_catalog.h audio_decoder.h video_decoder.h post.h broadcaster.h
noinst_HEADERS = bswap.h
diff --git a/src/xine-engine/broadcaster.c b/src/xine-engine/broadcaster.c
new file mode 100644
index 000000000..271149539
--- /dev/null
+++ b/src/xine-engine/broadcaster.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2000-2003 the xine project
+ * May 2003 - Miguel Freitas
+ * This feature was sponsored by 1Control
+ *
+ * This file is part of xine, a free 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
+ *
+ * $Id: broadcaster.c,v 1.1 2003/05/15 20:23:18 miguelfreitas Exp $
+ *
+ * broadcaster.c - xine network broadcaster
+ *
+ * how it works:
+ * - one xine instance must be set as master by using XINE_PARAM_BROADCASTER_PORT.
+ * 'xine --broadcast-port <port_number>'
+ * - master will wait for connections on specified port, accepting new clients.
+ * - several xine clients may connect to the server as "slaves", using mrl:
+ * slave://master_address:port
+ * - streams played on master will appear on every slave.
+ * if master is not meant to use video/audio devices it may be started with
+ * 'xine -V none -A none'
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <dlfcn.h>
+#include <pthread.h>
+
+#include "xine_internal.h"
+#include "buffer.h"
+#include "xineutils.h"
+
+#define QLEN 5 /* maximum connection queue length */
+#define _BUFSIZ 512
+
+struct broadcaster_s {
+ xine_stream_t *stream; /* stream to broadcast */
+ int port; /* server port */
+ int msock; /* master network socket */
+ xine_list_t *connections; /* active connections */
+
+ int running;
+ pthread_t manager_thread;
+ pthread_mutex_t lock;
+};
+
+
+/* network functions */
+
+static int sock_check_opened(int socket) {
+ fd_set readfds, writefds, exceptfds;
+ int retval;
+ struct timeval timeout;
+
+ for(;;) {
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(socket, &exceptfds);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ retval = select(socket + 1, &readfds, &writefds, &exceptfds, &timeout);
+
+ if(retval == -1 && (errno != EAGAIN && errno != EINTR))
+ return 0;
+
+ if (retval != -1)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Write to socket.
+ */
+static int sock_data_write(int socket, char *buf, int len) {
+ ssize_t size;
+ int wlen = 0;
+
+ if((socket < 0) || (buf == NULL))
+ return -1;
+
+ if(!sock_check_opened(socket))
+ return -1;
+
+ while(len) {
+ size = write(socket, buf, len);
+
+ if(size <= 0) {
+ printf("broadcaster: error writing to socket %d\n",socket);
+ return -1;
+ }
+
+ len -= size;
+ wlen += size;
+ buf += size;
+ }
+
+ return wlen;
+}
+
+static int sock_string_write(int socket, char *msg, ...) {
+ char buf[_BUFSIZ];
+ va_list args;
+
+ va_start(args, msg);
+ vsnprintf(buf, _BUFSIZ, msg, args);
+ va_end(args);
+
+ /* Each line sent is '\n' terminated */
+ if((buf[strlen(buf)] == '\0') && (buf[strlen(buf) - 1] != '\n'))
+ sprintf(buf, "%s%c", buf, '\n');
+
+ return sock_data_write(socket, buf, strlen(buf));
+}
+
+/*
+ * this is the most important broadcaster function.
+ * it sends data to every connected client (slaves).
+ */
+static void broadcaster_data_write(broadcaster_t *this, char *buf, int len) {
+ int *psock;
+
+ psock = xine_list_first_content (this->connections);
+ while (psock) {
+
+ /* in case of failure remove from list */
+ if( sock_data_write(*psock, buf, len) < 0 ) {
+
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ printf("broadcaster: closing socket %d\n", *psock);
+ close(*psock);
+ free(psock);
+ if( this->connections->cur->next )
+ psock = this->connections->cur->next->content;
+ else
+ psock = NULL;
+ xine_list_delete_current (this->connections);
+ } else
+ psock = xine_list_next_content (this->connections);
+ }
+}
+
+static void broadcaster_string_write(broadcaster_t *this, char *msg, ...) {
+ char buf[_BUFSIZ];
+ va_list args;
+
+ va_start(args, msg);
+ vsnprintf(buf, _BUFSIZ, msg, args);
+ va_end(args);
+
+ /* Each line sent is '\n' terminated */
+ if((buf[strlen(buf)] == '\0') && (buf[strlen(buf) - 1] != '\n'))
+ sprintf(buf, "%s%c", buf, '\n');
+
+ broadcaster_data_write(this, buf, strlen(buf));
+}
+
+
+/*
+ * this thread takes care of accepting new connections.
+ */
+static void *manager_loop (void *this_gen) {
+ broadcaster_t *this = (broadcaster_t *) this_gen;
+ struct sockaddr_in fsin; /* the from address of a client */
+ int alen; /* from-address length */
+ fd_set rfds; /* read file descriptor set */
+ fd_set efds; /* exception descriptor set */
+
+ while( this->running ) {
+ FD_ZERO(&rfds);
+ FD_SET(this->msock, &rfds);
+ FD_ZERO(&efds);
+ FD_SET(this->msock, &efds);
+
+ if (select(this->msock+1, &rfds, (fd_set *)0, &efds, (struct timeval *)0) > 0) {
+
+ pthread_mutex_lock( &this->lock );
+
+ if (FD_ISSET(this->msock, &rfds))
+ {
+ int ssock;
+ alen = sizeof(fsin);
+
+ ssock = accept(this->msock, (struct sockaddr *)&fsin, &alen);
+ if (ssock >= 0) {
+ /* identification string, helps demuxer probing */
+ if( sock_string_write(ssock,"master xine v1") > 0 ) {
+ int *psock = malloc(sizeof(int));
+ *psock = ssock;
+
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ printf("broadcaster: new connection socket %d\n", *psock);
+ xine_list_append_content(this->connections, psock);
+ }
+ }
+ }
+
+ pthread_mutex_unlock( &this->lock );
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/*
+ * receive xine buffers and send them through the broadcaster
+ */
+static void send_buf (broadcaster_t *this, char *from, buf_element_t *buf) {
+ int i;
+
+ /* ignore END buffers since they would stop the slavery */
+ if( buf->type == BUF_CONTROL_END )
+ return;
+
+ /* assume RESET_DECODER is result of a xine_flush_engine */
+ if( buf->type == BUF_CONTROL_RESET_DECODER && !strcmp(from,"video") ) {
+ broadcaster_string_write(this, "flush_engine");
+ }
+
+ /* send decoder information if any */
+ for( i = 0; i < BUF_NUM_DEC_INFO; i++ ) {
+ if( buf->decoder_info[i] ) {
+ broadcaster_string_write(this, "decoder_info index=%d decoder_info=%u has_data=%d",
+ i, buf->decoder_info[i], (buf->decoder_info_ptr[i]) ? 1 : 0);
+ if( buf->decoder_info_ptr[i] )
+ broadcaster_data_write(this, buf->decoder_info_ptr[i], buf->decoder_info[i]);
+ }
+ }
+
+ broadcaster_string_write(this, "buffer fifo=%s size=%ld type=%lu pts=%lld disc=%lld flags=%lu",
+ from, buf->size, buf->type, buf->pts, buf->disc_off, buf->decoder_flags );
+
+ if( buf->size )
+ broadcaster_data_write(this, buf->content, buf->size);
+}
+
+
+/* buffer callbacks */
+static void video_put_cb (fifo_buffer_t *fifo, buf_element_t *buf, void *this_gen) {
+ broadcaster_t *this = (broadcaster_t *) this_gen;
+
+ pthread_mutex_lock( &this->lock );
+ send_buf(this, "video", buf);
+ pthread_mutex_unlock( &this->lock );
+}
+
+static void audio_put_cb (fifo_buffer_t *fifo, buf_element_t *buf, void *this_gen) {
+ broadcaster_t *this = (broadcaster_t *) this_gen;
+
+ pthread_mutex_lock( &this->lock );
+ send_buf(this, "audio", buf);
+ pthread_mutex_unlock( &this->lock );
+}
+
+broadcaster_t *init_broadcaster(xine_stream_t *stream, int port)
+{
+ broadcaster_t *this;
+ struct sockaddr_in servAddr;
+ int msock, err;
+
+ msock = socket(PF_INET, SOCK_STREAM, 0);
+ if( msock < 0 )
+ {
+ printf("broadcaster: error opening master socket.\n");
+ return NULL;
+ }
+ servAddr.sin_family = AF_INET;
+ servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ servAddr.sin_port = htons(port);
+
+ if(bind(msock, (struct sockaddr *) &servAddr, sizeof(servAddr))<0)
+ {
+ printf("broadcaster: error binding to port %d\n", port);
+ return NULL;
+ }
+
+ listen(msock,QLEN);
+
+ signal( SIGPIPE, SIG_IGN );
+
+ this = xine_xmalloc(sizeof(broadcaster_t));
+ this->port = port;
+ this->stream = stream;
+ this->msock = msock;
+ this->connections = xine_list_new();
+
+ pthread_mutex_init (&this->lock, NULL);
+
+ stream->video_fifo->register_put_cb(stream->video_fifo, video_put_cb, this);
+
+ if(stream->audio_fifo)
+ stream->audio_fifo->register_put_cb(stream->audio_fifo, audio_put_cb, this);
+
+ this->running = 1;
+ if ((err = pthread_create (&this->manager_thread,
+ NULL, manager_loop, (void *)this)) != 0) {
+ printf ("broadcaster: can't create new thread (%s)\n",
+ strerror(err));
+ abort();
+ }
+
+ return this;
+}
+
+void close_broadcaster(broadcaster_t *this)
+{
+ int *psock;
+
+ psock = xine_list_first_content (this->connections);
+ while (psock) {
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG)
+ printf("broadcaster: closing socket %d\n", *psock);
+ close(*psock);
+ free(psock);
+ xine_list_delete_current (this->connections);
+ if( this->connections->cur )
+ psock = this->connections->cur->content;
+ else
+ psock = NULL;
+ }
+ xine_list_free(this->connections);
+
+ this->running = 0;
+ close(this->msock);
+ pthread_mutex_lock( &this->lock );
+ pthread_cancel(this->manager_thread);
+ pthread_join(this->manager_thread,NULL);
+
+ this->stream->video_fifo->unregister_put_cb(this->stream->video_fifo, video_put_cb);
+
+ if(this->stream->audio_fifo)
+ this->stream->audio_fifo->unregister_put_cb(this->stream->audio_fifo, audio_put_cb);
+
+ free(this);
+}
+
+int get_broadcaster_port(broadcaster_t *this)
+{
+ return this->port;
+}
diff --git a/src/xine-engine/broadcaster.h b/src/xine-engine/broadcaster.h
new file mode 100644
index 000000000..113f4ceb5
--- /dev/null
+++ b/src/xine-engine/broadcaster.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2000-2003 the xine project
+ *
+ * This file is part of xine, a free 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
+ *
+ * $Id: broadcaster.h,v 1.1 2003/05/15 20:23:18 miguelfreitas Exp $
+ *
+ * broadcaster.h
+ *
+ */
+
+#ifndef HAVE_BROADCASTER_H
+#define HAVE_BROADCASTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+typedef struct broadcaster_s broadcaster_t;
+
+broadcaster_t *init_broadcaster(xine_stream_t *stream, int port);
+void close_broadcaster(broadcaster_t *this);
+int get_broadcaster_port(broadcaster_t *this);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/xine-engine/buffer.c b/src/xine-engine/buffer.c
index 0deca48b4..fb12fca94 100644
--- a/src/xine-engine/buffer.c
+++ b/src/xine-engine/buffer.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: buffer.c,v 1.29 2003/05/13 16:38:05 miguelfreitas Exp $
+ * $Id: buffer.c,v 1.30 2003/05/15 20:23:18 miguelfreitas Exp $
*
*
* contents:
@@ -92,8 +92,10 @@ static buf_element_t *buffer_pool_alloc (fifo_buffer_t *this) {
buf->pts = 0;
buf->size = 0;
buf->decoder_flags = 0;
+ memset(buf->decoder_info, 0, sizeof(buf->decoder_info));
+ memset(buf->decoder_info_ptr, 0, sizeof(buf->decoder_info_ptr));
extra_info_reset( buf->extra_info );
-
+
return buf;
}
@@ -127,6 +129,8 @@ static buf_element_t *buffer_pool_try_alloc (fifo_buffer_t *this) {
buf->pts = 0;
buf->size = 0;
buf->decoder_flags = 0;
+ memset(buf->decoder_info, 0, sizeof(buf->decoder_info));
+ memset(buf->decoder_info_ptr, 0, sizeof(buf->decoder_info_ptr));
extra_info_reset( buf->extra_info );
}
return buf;
diff --git a/src/xine-engine/buffer.h b/src/xine-engine/buffer.h
index 91a930f06..e3339e753 100644
--- a/src/xine-engine/buffer.h
+++ b/src/xine-engine/buffer.h
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: buffer.h,v 1.112 2003/05/13 16:38:06 miguelfreitas Exp $
+ * $Id: buffer.h,v 1.113 2003/05/15 20:23:18 miguelfreitas Exp $
*
*
* contents:
@@ -221,6 +221,8 @@ extern "C" {
typedef struct extra_info_s extra_info_t;
#endif
+#define BUF_NUM_DEC_INFO 4
+
typedef struct buf_element_s buf_element_t;
struct buf_element_s {
buf_element_t *next;
@@ -238,8 +240,10 @@ struct buf_element_s {
uint32_t decoder_flags; /* stuff like keyframe, is_header ... see below */
- uint32_t decoder_info[4]; /* additional decoder flags and other dec-spec. stuff */
- void *decoder_info_ptr[4]; /* pointers to dec-spec. stuff */
+ /* additional decoder flags and other dec-spec. stuff */
+ uint32_t decoder_info[BUF_NUM_DEC_INFO];
+ /* pointers to dec-spec. stuff */
+ void *decoder_info_ptr[BUF_NUM_DEC_INFO];
void (*free_buffer) (buf_element_t *buf);
diff --git a/src/xine-engine/metronom.c b/src/xine-engine/metronom.c
index cd72cfcfd..24c6fb999 100644
--- a/src/xine-engine/metronom.c
+++ b/src/xine-engine/metronom.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: metronom.c,v 1.117 2003/05/06 20:50:11 miguelfreitas Exp $
+ * $Id: metronom.c,v 1.118 2003/05/15 20:23:18 miguelfreitas Exp $
*/
#ifdef HAVE_CONFIG_H
@@ -629,6 +629,8 @@ static int64_t metronom_get_option (metronom_t *this, int option) {
return this->spu_offset;
case METRONOM_FRAME_DURATION:
return this->img_duration;
+ case METRONOM_VPTS_OFFSET:
+ return this->vpts_offset;
}
printf ("metronom: unknown option in get_option: %d\n",
option);
diff --git a/src/xine-engine/metronom.h b/src/xine-engine/metronom.h
index 07b51b85f..2df24d87e 100644
--- a/src/xine-engine/metronom.h
+++ b/src/xine-engine/metronom.h
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: metronom.h,v 1.48 2003/05/06 20:50:12 miguelfreitas Exp $
+ * $Id: metronom.h,v 1.49 2003/05/15 20:23:18 miguelfreitas Exp $
*
* metronom: general pts => virtual calculation/assoc
*
@@ -217,6 +217,7 @@ struct metronom_s {
#define METRONOM_ADJ_VPTS_OFFSET 3
#define METRONOM_FRAME_DURATION 4
#define METRONOM_SPU_OFFSET 5
+#define METRONOM_VPTS_OFFSET 6
metronom_t *metronom_init (int have_audio, xine_stream_t *stream);
diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c
index c4ec29622..eec327b37 100644
--- a/src/xine-engine/xine.c
+++ b/src/xine-engine/xine.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: xine.c,v 1.246 2003/04/25 22:27:35 f1rmb Exp $
+ * $Id: xine.c,v 1.247 2003/05/15 20:23:18 miguelfreitas Exp $
*
* top-level xine functions
*
@@ -398,7 +398,8 @@ xine_stream_t *xine_stream_new (xine_t *this,
stream->next_audio_port = NULL;
stream->next_video_port = NULL;
stream->metronom_prebuffer = PREBUFFER_PTS_OFFSET;
-
+ stream->broadcaster = NULL;
+
/*
* initial master/slave
*/
@@ -1049,6 +1050,9 @@ void xine_dispose (xine_stream_t *stream) {
stream->slave->master = NULL;
}
+ if(stream->broadcaster)
+ close_broadcaster(stream->broadcaster);
+
if (stream->xine->verbosity >= XINE_VERBOSITY_DEBUG)
printf ("xine_exit: shutdown audio\n");
diff --git a/src/xine-engine/xine_interface.c b/src/xine-engine/xine_interface.c
index bbcbfc214..6115b0c0e 100644
--- a/src/xine-engine/xine_interface.c
+++ b/src/xine-engine/xine_interface.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: xine_interface.c,v 1.53 2003/04/29 15:14:16 mroi Exp $
+ * $Id: xine_interface.c,v 1.54 2003/05/15 20:23:18 miguelfreitas Exp $
*
* convenience/abstraction layer, functions to implement
* libxine's public interface
@@ -422,6 +422,15 @@ void xine_set_param (xine_stream_t *stream, int param, int value) {
stream->metronom_prebuffer = value;
break;
+ case XINE_PARAM_BROADCASTER_PORT:
+ if( !stream->broadcaster && value ) {
+ stream->broadcaster = init_broadcaster(stream, value);
+ } else if ( stream->broadcaster && !value ) {
+ close_broadcaster(stream->broadcaster);
+ stream->broadcaster = NULL;
+ }
+ break;
+
default:
printf ("xine_interface: unknown param %d\n", param);
}
@@ -511,6 +520,13 @@ int xine_get_param (xine_stream_t *stream, int param) {
case XINE_PARAM_METRONOM_PREBUFFER:
return stream->metronom_prebuffer;
+
+ case XINE_PARAM_BROADCASTER_PORT:
+ if( stream->broadcaster )
+ return get_broadcaster_port(stream->broadcaster);
+ else
+ return 0;
+ break;
default:
printf ("xine_interface: unknown param %d\n", param);
diff --git a/src/xine-engine/xine_internal.h b/src/xine-engine/xine_internal.h
index b2644368e..d8f234048 100644
--- a/src/xine-engine/xine_internal.h
+++ b/src/xine-engine/xine_internal.h
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: xine_internal.h,v 1.136 2003/04/21 06:12:23 f1rmb Exp $
+ * $Id: xine_internal.h,v 1.137 2003/05/15 20:23:18 miguelfreitas Exp $
*
*/
@@ -71,6 +71,7 @@ typedef struct extra_info_s extra_info_t;
#include "plugin_catalog.h"
#include "video_decoder.h"
#include "audio_decoder.h"
+#include "broadcaster.h"
#define XINE_MAX_EVENT_LISTENERS 50
#define XINE_MAX_EVENT_TYPES 100
@@ -260,6 +261,7 @@ struct xine_stream_s {
pthread_cond_t next_audio_port_wired;
int64_t metronom_prebuffer;
+ broadcaster_t *broadcaster;
};