From 02af7a2a50311dcef6173a97157c35e98969d80c Mon Sep 17 00:00:00 2001 From: Miguel Freitas Date: Thu, 15 May 2003 20:23:16 +0000 Subject: add broadcaster CVS patchset: 4857 CVS date: 2003/05/15 20:23:16 --- include/xine.h.in | 4 +- src/demuxers/Makefile.am | 7 +- src/demuxers/demux_slave.c | 462 +++++++++++++++++++++++++++++++++++++++ src/input/input_net.c | 34 ++- src/xine-engine/Makefile.am | 4 +- src/xine-engine/broadcaster.c | 375 +++++++++++++++++++++++++++++++ src/xine-engine/broadcaster.h | 49 +++++ src/xine-engine/buffer.c | 8 +- src/xine-engine/buffer.h | 10 +- src/xine-engine/metronom.c | 4 +- src/xine-engine/metronom.h | 3 +- src/xine-engine/xine.c | 8 +- src/xine-engine/xine_interface.c | 18 +- src/xine-engine/xine_internal.h | 4 +- 14 files changed, 967 insertions(+), 23 deletions(-) create mode 100644 src/demuxers/demux_slave.c create mode 100644 src/xine-engine/broadcaster.c create mode 100644 src/xine-engine/broadcaster.h 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 ' + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#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 ' + * - 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; }; -- cgit v1.2.3