summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibaut Mattern <tmattern@users.sourceforge.net>2004-10-14 23:25:24 +0000
committerThibaut Mattern <tmattern@users.sourceforge.net>2004-10-14 23:25:24 +0000
commit0afc766174a39031fa27686bba32e28dd87af09c (patch)
treea08f81e534bf1550f66dce996180ac9cfd5ed573
parent54ef2ac454dab005c5a6a808b2c5bbcdfe544b8f (diff)
downloadxine-lib-0afc766174a39031fa27686bba32e28dd87af09c.tar.gz
xine-lib-0afc766174a39031fa27686bba32e28dd87af09c.tar.bz2
Implemented stream_t reference counter idea.
See these two threads: http://thread.gmane.org/gmane.comp.video.xine.devel/10819 http://thread.gmane.org/gmane.comp.video.xine.devel/10424 Fixed _x_handle_stream_end __stop_internal race. See this thread: http://thread.gmane.org/gmane.comp.video.xine.devel/10818 If the lib is broken after this patch, you know who to blame ;-) CVS patchset: 7036 CVS date: 2004/10/14 23:25:24
-rw-r--r--src/xine-engine/Makefile.am4
-rw-r--r--src/xine-engine/audio_decoder.c7
-rw-r--r--src/xine-engine/demux.c49
-rw-r--r--src/xine-engine/refcounter.c77
-rw-r--r--src/xine-engine/refcounter.h49
-rw-r--r--src/xine-engine/video_decoder.c5
-rw-r--r--src/xine-engine/video_out.c9
-rw-r--r--src/xine-engine/xine.c128
-rw-r--r--src/xine-engine/xine_internal.h6
9 files changed, 233 insertions, 101 deletions
diff --git a/src/xine-engine/Makefile.am b/src/xine-engine/Makefile.am
index 696a501b6..ac4d3e2f5 100644
--- a/src/xine-engine/Makefile.am
+++ b/src/xine-engine/Makefile.am
@@ -25,7 +25,7 @@ libxine_la_SOURCES = xine.c metronom.c configfile.c buffer.c \
audio_decoder.c video_out.c audio_out.c resample.c events.c \
video_overlay.c osd.c scratch.c demux.c vo_scale.c \
xine_interface.c post.c tvmode.c broadcaster.c io_helper.c \
- input_rip.c info_helper.c
+ input_rip.c info_helper.c refcounter.c
# FIXME: these are currently unused:
EXTRA_DIST = lrb.c lrb.h accel_xvmc.h
@@ -45,7 +45,7 @@ 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 \
video_overlay.h osd.h scratch.h xine_plugin.h xineintl.h \
plugin_catalog.h audio_decoder.h video_decoder.h post.h \
- io_helper.h broadcaster.h info_helper.h
+ io_helper.h broadcaster.h info_helper.h refcounter.h
noinst_HEADERS = bswap.h
diff --git a/src/xine-engine/audio_decoder.c b/src/xine-engine/audio_decoder.c
index b69908384..f48a5a35c 100644
--- a/src/xine-engine/audio_decoder.c
+++ b/src/xine-engine/audio_decoder.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: audio_decoder.c,v 1.129 2004/07/06 22:53:22 miguelfreitas Exp $
+ * $Id: audio_decoder.c,v 1.130 2004/10/14 23:25:24 tmattern Exp $
*
*
* functions that implement audio decoding
@@ -162,11 +162,6 @@ static void *audio_decoder_loop (void *stream_gen) {
pthread_mutex_unlock (&stream->counter_lock);
stream->audio_channel_auto = -1;
- if (!stream->video_thread) {
- /* set engine status, send frontend notification event */
- _x_handle_stream_end (stream, buf->decoder_flags & BUF_FLAG_END_STREAM);
- }
-
break;
case BUF_CONTROL_QUIT:
diff --git a/src/xine-engine/demux.c b/src/xine-engine/demux.c
index d01e2a887..4e56a5256 100644
--- a/src/xine-engine/demux.c
+++ b/src/xine-engine/demux.c
@@ -20,7 +20,7 @@
* Demuxer helper functions
* hide some xine engine details from demuxers and reduce code duplication
*
- * $Id: demux.c,v 1.50 2004/06/13 21:28:57 miguelfreitas Exp $
+ * $Id: demux.c,v 1.51 2004/10/14 23:25:24 tmattern Exp $
*/
@@ -160,8 +160,8 @@ void _x_demux_control_headers_done (xine_stream_t *stream) {
buf->type = BUF_CONTROL_HEADERS_DONE;
stream->audio_fifo->put (stream->audio_fifo, buf);
- while ((stream->header_count_audio<header_count_audio) ||
- (stream->header_count_video<header_count_video)) {
+ while ((stream->header_count_audio < header_count_audio) ||
+ (stream->header_count_video < header_count_video)) {
struct timeval tv;
struct timespec ts;
@@ -230,6 +230,9 @@ static void *demux_loop (void *stream_gen) {
xine_stream_t *stream = (xine_stream_t *)stream_gen;
int status;
+ int finished_count_audio = 0;
+ int finished_count_video = 0;
+ int non_user;
lprintf ("loop starting...\n");
@@ -277,19 +280,32 @@ static void *demux_loop (void *stream_gen) {
lprintf ("loop finished (status: %d)\n", status);
- /* demux_thread_running is zero if demux loop has being stopped by user */
- if (stream->demux_thread_running) {
- _x_demux_control_end(stream, BUF_FLAG_END_STREAM);
- } else {
- _x_demux_control_end(stream, BUF_FLAG_END_USER);
- }
+ pthread_mutex_lock (&stream->counter_lock);
+ if (stream->audio_thread)
+ finished_count_audio = stream->finished_count_audio + 1;
+ if (stream->video_thread)
+ finished_count_video = stream->finished_count_video + 1;
+ pthread_mutex_unlock (&stream->counter_lock);
+
+ _x_demux_control_end(stream, 0);
lprintf ("loop finished, end buffer sent\n");
+ /* demux_thread_running is zero if demux loop has being stopped by user */
+ non_user = stream->demux_thread_running;
stream->demux_thread_running = 0;
-
pthread_mutex_unlock( &stream->demux_lock );
+ pthread_mutex_lock (&stream->counter_lock);
+ while ((stream->finished_count_audio < finished_count_audio) ||
+ (stream->finished_count_video < finished_count_video)) {
+ lprintf ("waiting for finisheds.\n");
+ pthread_cond_wait (&stream->counter_changed, &stream->counter_lock);
+ }
+ pthread_mutex_unlock (&stream->counter_lock);
+
+ _x_handle_stream_end(stream, non_user);
+ stream->demux_thread = 0;
return NULL;
}
@@ -327,21 +343,18 @@ int _x_demux_stop_thread (xine_stream_t *stream) {
pthread_mutex_lock( &stream->demux_lock );
stream->demux_thread_running = 0;
stream->demux_action_pending = 0;
+
+ /* At that point, the demuxer has sent the last audio/video buffer,
+ * so it's a safe place to flush the engine.
+ */
+ _x_demux_flush_engine( stream );
pthread_mutex_unlock( &stream->demux_lock );
lprintf ("joining thread %ld\n", stream->demux_thread );
- /* FIXME: counter_lock isn't meant to protect demux_thread update.
- however we can't use demux_lock here. should we create a new lock? */
- pthread_mutex_lock (&stream->counter_lock);
-
- /* <join; demux_thread = 0;> must be atomic */
if( stream->demux_thread )
pthread_join (stream->demux_thread, &p);
- stream->demux_thread = 0;
- pthread_mutex_unlock (&stream->counter_lock);
-
/*
* Wake up xine_play if it's waiting for a frame
*/
diff --git a/src/xine-engine/refcounter.c b/src/xine-engine/refcounter.c
new file mode 100644
index 000000000..11c6ceb11
--- /dev/null
+++ b/src/xine-engine/refcounter.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2000-2004 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: refcounter.c,v 1.1 2004/10/14 23:25:24 tmattern Exp $
+ *
+ */
+#define LOG_MODULE "refcounter"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include "refcounter.h"
+#include "xine_internal.h"
+
+refcounter_t* _x_new_refcounter(void *object, void (*destructor)(void *))
+{
+ refcounter_t *new_refcounter;
+
+ new_refcounter = (refcounter_t *) xine_xmalloc (sizeof (refcounter_t));
+ new_refcounter->count = 1;
+ new_refcounter->object = object;
+ new_refcounter->destructor = destructor;
+ pthread_mutex_init (&new_refcounter->lock, NULL);
+ lprintf("new referenced object %p\n", object);
+ return new_refcounter;
+}
+
+int _x_refcounter_inc(refcounter_t *refcounter)
+{
+ int res;
+
+ pthread_mutex_lock(&refcounter->lock);
+ if (!refcounter->count)
+ _x_abort();
+ res = ++refcounter->count;
+ pthread_mutex_unlock(&refcounter->lock);
+
+ return res;
+}
+
+int _x_refcounter_dec(refcounter_t *refcounter)
+{
+ int res;
+
+ pthread_mutex_lock(&refcounter->lock);
+ res = --refcounter->count;
+ pthread_mutex_unlock(&refcounter->lock);
+ if (!res) {
+ lprintf("calling destructor of object %p\n", refcounter->object);
+ refcounter->destructor(refcounter->object);
+ }
+
+ return res;
+}
+
+void _x_refcounter_dispose(refcounter_t *refcounter)
+{
+ pthread_mutex_destroy (&refcounter->lock);
+ free(refcounter);
+}
diff --git a/src/xine-engine/refcounter.h b/src/xine-engine/refcounter.h
new file mode 100644
index 000000000..98a9f26ae
--- /dev/null
+++ b/src/xine-engine/refcounter.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2000-2004 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: refcounter.h,v 1.1 2004/10/14 23:25:24 tmattern Exp $
+ *
+ */
+#ifndef HAVE_REFCOUNTER_H
+#define HAVE_REFCOUNTER_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <pthread.h>
+
+typedef struct {
+ pthread_mutex_t lock;
+ int count;
+ void* object; /* referenced object */
+ void (*destructor)(void *); /* object destructor */
+} refcounter_t;
+
+typedef void (*refcounter_destructor)(void*);
+
+refcounter_t* _x_new_refcounter(void *object, refcounter_destructor destructor);
+
+int _x_refcounter_inc(refcounter_t *refcounter);
+
+int _x_refcounter_dec(refcounter_t *refcounter);
+
+void _x_refcounter_dispose(refcounter_t *refcounter);
+
+#endif /* HAVE_REFCOUNTER_H */
diff --git a/src/xine-engine/video_decoder.c b/src/xine-engine/video_decoder.c
index 9e2fa3af1..5a7a16ede 100644
--- a/src/xine-engine/video_decoder.c
+++ b/src/xine-engine/video_decoder.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: video_decoder.c,v 1.150 2004/07/06 22:53:23 miguelfreitas Exp $
+ * $Id: video_decoder.c,v 1.151 2004/10/14 23:25:24 tmattern Exp $
*
*/
@@ -234,9 +234,6 @@ static void *video_decoder_loop (void *stream_gen) {
pthread_cond_broadcast(&stream->first_frame_reached);
}
pthread_mutex_unlock (&stream->first_frame_lock);
-
- /* set engine status, send frontend notification event */
- _x_handle_stream_end (stream, buf->decoder_flags & BUF_FLAG_END_STREAM);
break;
case BUF_CONTROL_QUIT:
diff --git a/src/xine-engine/video_out.c b/src/xine-engine/video_out.c
index 5495a029f..3deca0760 100644
--- a/src/xine-engine/video_out.c
+++ b/src/xine-engine/video_out.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: video_out.c,v 1.209 2004/10/09 06:44:21 mroi Exp $
+ * $Id: video_out.c,v 1.210 2004/10/14 23:25:24 tmattern Exp $
*
* frame allocation / queuing / scheduling / output functions
*/
@@ -246,6 +246,8 @@ static void vo_frame_dec_lock (vo_frame_t *img) {
img->lock_counter--;
if (!img->lock_counter) {
vos_t *this = (vos_t *) img->port;
+ if (img->stream)
+ _x_refcounter_dec(img->stream->refcounter);
vo_append_to_img_buf_queue (this->free_img_buf_queue, img);
}
@@ -335,6 +337,7 @@ static vo_frame_t *vo_get_frame (xine_video_port_t *this_gen,
img->crop_right = 0;
img->crop_top = 0;
img->crop_bottom = 0;
+ img->stream = NULL;
_x_extra_info_reset ( img->extra_info );
@@ -367,6 +370,7 @@ static int vo_frame_draw (vo_frame_t *img, xine_stream_t *stream) {
this->current_height = img->height;
if (stream) {
+ _x_refcounter_inc(stream->refcounter);
_x_extra_info_merge( img->extra_info, stream->video_decoder_extra_info );
stream->metronom->got_video_frame (stream->metronom, img);
}
@@ -660,7 +664,7 @@ static vo_frame_t * duplicate_frame( vos_t *this, vo_frame_t *img ) {
dupl->duration = img->duration;
dupl->is_first = img->is_first;
- dupl->stream = img->stream;
+ dupl->stream = NULL;
memcpy( dupl->extra_info, img->extra_info, sizeof(extra_info_t) );
/* delay frame processing for now, we might not even need it (eg. frame will be discarded) */
@@ -1599,6 +1603,7 @@ static vo_frame_t * crop_frame( xine_video_port_t *this_gen, vo_frame_t *img ) {
dupl->is_first = img->is_first;
dupl->stream = img->stream;
+ _x_refcounter_inc(img->stream->refcounter);
memcpy( dupl->extra_info, img->extra_info, sizeof(extra_info_t) );
/* delay frame processing for now, we might not even need it (eg. frame will be discarded) */
diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c
index 19d911301..7432bff60 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.297 2004/08/30 07:37:42 f1rmb Exp $
+ * $Id: xine.c,v 1.298 2004/10/14 23:25:24 tmattern Exp $
*/
/*
@@ -48,9 +48,9 @@
#define LOG_MODULE "xine"
#define LOG_VERBOSE
-/*
+
#define LOG
-*/
+
#define XINE_ENABLE_EXPERIMENTAL_FEATURES
#define XINE_ENGINE_INTERNAL
@@ -83,9 +83,6 @@ void _x_handle_stream_end (xine_stream_t *stream, int non_user) {
return;
stream->status = XINE_STATUS_STOP;
- /* join thread if needed to fix resource leaks */
- _x_demux_stop_thread( stream );
-
if (non_user) {
/* frontends will not be interested in receiving this event
* if they have called xine_stop explicitly, so only send
@@ -274,9 +271,6 @@ static void __set_speed_internal (xine_stream_t *stream, int speed) {
/* stream->ignore_speed_change must be set, when entering this function */
static void __stop_internal (xine_stream_t *stream) {
- int finished_count_audio = 0;
- int finished_count_video = 0;
-
lprintf ("status before = %d\n", stream->status);
if (stream->status == XINE_STATUS_STOP) {
@@ -294,43 +288,10 @@ static void __stop_internal (xine_stream_t *stream) {
/*
* stop demux
*/
-
- pthread_mutex_lock (&stream->counter_lock);
- if (stream->audio_thread)
- finished_count_audio = stream->finished_count_audio + 1;
- else
- finished_count_audio = 0;
-
- if (stream->video_thread)
- finished_count_video = stream->finished_count_video + 1;
- else
- finished_count_video = 0;
-
- pthread_mutex_unlock (&stream->counter_lock);
-
lprintf ("stopping demux\n");
if (stream->demux_plugin) {
-
_x_demux_stop_thread( stream );
lprintf ("stop thread done\n");
-
- _x_demux_flush_engine( stream );
- lprintf ("flush engine done\n");
-
- /*
- * wait until engine has really stopped
- */
-
-#if 0
- pthread_mutex_lock (&stream->counter_lock);
- while ((stream->finished_count_audio<finished_count_audio) ||
- (stream->finished_count_video<finished_count_video)) {
-
- lprintf ("waiting for finisheds.\n");
- pthread_cond_wait (&stream->counter_changed, &stream->counter_lock);
- }
- pthread_mutex_unlock (&stream->counter_lock);
-#endif
}
lprintf ("demux stopped\n");
lprintf ("done\n");
@@ -379,7 +340,21 @@ static void __close_internal (xine_stream_t *stream) {
}
stream->ignore_speed_change = 1;
+ stream->xine->port_ticket->acquire(stream->xine->port_ticket, 1);
+
+ if (stream->audio_out)
+ stream->audio_out->set_property(stream->audio_out, AO_PROP_DISCARD_BUFFERS, 1);
+ if (stream->video_out)
+ stream->video_out->set_property(stream->video_out, VO_PROP_DISCARD_FRAMES, 1);
+
__stop_internal( stream );
+
+ if (stream->video_out)
+ stream->video_out->set_property(stream->video_out, VO_PROP_DISCARD_FRAMES, 0);
+ if (stream->audio_out)
+ stream->audio_out->set_property(stream->audio_out, AO_PROP_DISCARD_BUFFERS, 0);
+
+ stream->xine->port_ticket->release(stream->xine->port_ticket, 1);
stream->ignore_speed_change = 0;
lprintf ("disposing demux\n");
@@ -466,6 +441,7 @@ static int __stream_rewire_video(xine_post_out_t *output, void *data)
return 1;
}
+void __xine_dispose_internal (xine_stream_t *stream);
xine_stream_t *xine_stream_new (xine_t *this,
xine_audio_port_t *ao, xine_video_port_t *vo) {
@@ -581,6 +557,11 @@ xine_stream_t *xine_stream_new (xine_t *this,
stream->osd_renderer = _x_osd_renderer_init(stream);
else
stream->osd_renderer = NULL;
+
+ /*
+ * create a reference counter
+ */
+ stream->refcounter = _x_new_refcounter(stream, (refcounter_destructor)__xine_dispose_internal);
/*
* register stream
@@ -1212,35 +1193,11 @@ int xine_eject (xine_stream_t *stream) {
return status;
}
-void xine_dispose (xine_stream_t *stream) {
+void __xine_dispose_internal (xine_stream_t *stream) {
xine_stream_t *s;
- xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "xine_dispose\n");
-
- stream->status = XINE_STATUS_QUIT;
-
- xine_close(stream);
-
- if( stream->master != stream ) {
- stream->master->slave = NULL;
- }
- if( stream->slave && stream->slave->master == stream ) {
- stream->slave->master = NULL;
- }
-
- if(stream->broadcaster)
- _x_close_broadcaster(stream->broadcaster);
-
- xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "shutdown audio\n");
- _x_audio_decoder_shutdown (stream);
-
- xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "shutdown video\n");
- _x_video_decoder_shutdown (stream);
-
- if (stream->osd_renderer)
- stream->osd_renderer->close( stream->osd_renderer );
-
+ lprintf("stream: %p\n", stream);
pthread_mutex_destroy (&stream->info_mutex);
pthread_mutex_destroy (&stream->meta_mutex);
pthread_mutex_destroy (&stream->frontend_lock);
@@ -1264,12 +1221,47 @@ void xine_dispose (xine_stream_t *stream) {
}
pthread_mutex_unlock(&stream->xine->streams_lock);
+ _x_refcounter_dispose(stream->refcounter);
+
free (stream->current_extra_info);
free (stream->video_decoder_extra_info);
free (stream->audio_decoder_extra_info);
free (stream);
}
+void xine_dispose (xine_stream_t *stream) {
+ /* decrease the reference counter
+ * if there is no more reference on this stream, the __xine_dispose_internal
+ * function is called
+ */
+ xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "xine_dispose\n");
+ stream->status = XINE_STATUS_QUIT;
+
+ xine_close(stream);
+
+ if( stream->master != stream ) {
+ stream->master->slave = NULL;
+ }
+ if( stream->slave && stream->slave->master == stream ) {
+ stream->slave->master = NULL;
+ }
+
+ if(stream->broadcaster)
+ _x_close_broadcaster(stream->broadcaster);
+
+ xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "shutdown audio\n");
+ _x_audio_decoder_shutdown (stream);
+
+ xprintf (stream->xine, XINE_VERBOSITY_DEBUG, "shutdown video\n");
+ _x_video_decoder_shutdown (stream);
+
+ if (stream->osd_renderer)
+ stream->osd_renderer->close( stream->osd_renderer );
+
+
+ _x_refcounter_dec(stream->refcounter);
+}
+
void xine_exit (xine_t *this) {
int i;
diff --git a/src/xine-engine/xine_internal.h b/src/xine-engine/xine_internal.h
index 488df397b..2cdf53a2c 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.160 2004/09/26 22:54:52 valtri Exp $
+ * $Id: xine_internal.h,v 1.161 2004/10/14 23:25:24 tmattern Exp $
*
*/
@@ -34,6 +34,7 @@ extern "C" {
#ifdef XINE_COMPILE
# include "xine.h"
+# include "refcounter.h"
# include "input/input_plugin.h"
# include "demuxers/demux.h"
# include "video_out.h"
@@ -51,6 +52,7 @@ extern "C" {
# include "info_helper.h"
#else
# include <xine.h>
+# include <xine/refcounter.h>
# include <xine/input_plugin.h>
# include <xine/demux.h>
# include <xine/video_out.h>
@@ -322,6 +324,8 @@ struct xine_stream_s {
int err;
broadcaster_t *broadcaster;
+
+ refcounter_t *refcounter;
#endif
};