diff options
Diffstat (limited to 'src/xine-engine')
-rw-r--r-- | src/xine-engine/audio_out.c | 77 | ||||
-rw-r--r-- | src/xine-engine/audio_out.h | 6 | ||||
-rw-r--r-- | src/xine-engine/post.c | 865 | ||||
-rw-r--r-- | src/xine-engine/post.h | 252 | ||||
-rw-r--r-- | src/xine-engine/video_out.c | 66 | ||||
-rw-r--r-- | src/xine-engine/video_out.h | 8 |
6 files changed, 1001 insertions, 273 deletions
diff --git a/src/xine-engine/audio_out.c b/src/xine-engine/audio_out.c index 7654f7ecf..84caca40c 100644 --- a/src/xine-engine/audio_out.c +++ b/src/xine-engine/audio_out.c @@ -17,7 +17,7 @@ * along with self program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_out.c,v 1.161 2004/01/01 14:20:52 mroi Exp $ + * $Id: audio_out.c,v 1.162 2004/01/07 19:52:42 mroi Exp $ * * 22-8-2001 James imported some useful AC3 sections from the previous alsa driver. * (c) 2001 Andy Lo A Foe <andy@alsaplayer.org> @@ -100,6 +100,8 @@ #define ZERO_BUF_SIZE 5000 +#define NULL_STREAM (xine_stream_t *)-1 + /* By adding gap errors (difference between reported and expected * sound card clock) into metronom's vpts_offset we can use its * smoothing algorithms to correct sound card clock drifts. @@ -1041,6 +1043,7 @@ static void *ao_loop (void *this_gen) { pthread_mutex_lock(&this->streams_lock); for (stream = xine_list_first_content(this->streams); stream; stream = xine_list_next_content(this->streams)) { + if (stream == NULL_STREAM) continue; stream->metronom->set_option(stream->metronom, METRONOM_ADJ_VPTS_OFFSET, -gap/SYNC_GAP_RATE ); last_sync_time = cur_time; @@ -1116,37 +1119,31 @@ int xine_get_next_audio_frame (xine_audio_port_t *this_gen, xine_audio_frame_t *frame) { aos_t *this = (aos_t *) this_gen; - audio_buffer_t *in_buf, *out_buf; - xine_stream_t *stream; + audio_buffer_t *in_buf = NULL, *out_buf; + xine_stream_t *stream = NULL; lprintf ("get_next_audio_frame\n"); - do { + while (!in_buf || !stream) { stream = xine_list_first_content(this->streams); - if (!stream) - xine_usec_sleep (1000); - } while( !stream ); + if (!stream) { + xine_usec_sleep (5000); + continue; + } - pthread_mutex_lock (&this->out_fifo->mutex); - - in_buf = this->out_fifo->first; - - /* FIXME: ugly, use conditions and locks instead */ - - while (!in_buf - && (stream->demux_plugin->get_status (stream->demux_plugin)==DEMUX_OK)) { - - pthread_mutex_unlock(&this->out_fifo->mutex); - xine_usec_sleep (1000); - pthread_mutex_lock(&this->out_fifo->mutex); - + /* FIXME: ugly, use conditions and locks instead? */ + + pthread_mutex_lock (&this->out_fifo->mutex); in_buf = this->out_fifo->first; - } - - if (!in_buf) { - pthread_mutex_unlock(&this->out_fifo->mutex); - xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_audio: EOS\n"); - return 0; + if (!in_buf) { + pthread_mutex_unlock(&this->out_fifo->mutex); + if (stream != NULL_STREAM && stream->audio_fifo->fifo_size == 0 && + stream->demux_plugin->get_status(stream->demux_plugin) !=DEMUX_OK) + /* no further data can be expected here */ + return 0; + xine_usec_sleep (5000); + continue; + } } in_buf = fifo_remove_int (this->out_fifo); @@ -1303,24 +1300,27 @@ static int ao_open(xine_audio_port_t *this_gen, xine_stream_t *stream, return 0; } - pthread_mutex_lock(&this->streams_lock); - xine_list_append_content(this->streams, stream); - pthread_mutex_unlock(&this->streams_lock); - /* * set metainfo */ - channels = _x_ao_mode2channels( mode ); - if( channels == 0 ) - channels = 255; /* unknown */ + if (stream) { + channels = _x_ao_mode2channels( mode ); + if( channels == 0 ) + channels = 255; /* unknown */ - _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_MODE, mode); - _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_CHANNELS, channels); - _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_BITS, bits); - _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, rate); + _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_MODE, mode); + _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_CHANNELS, channels); + _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_BITS, bits); + _x_stream_info_set(stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, rate); - stream->metronom->set_audio_rate(stream->metronom, this->audio_step); + stream->metronom->set_audio_rate(stream->metronom, this->audio_step); + } + if (stream == NULL) stream = NULL_STREAM; + pthread_mutex_lock(&this->streams_lock); + xine_list_append_content(this->streams, stream); + pthread_mutex_unlock(&this->streams_lock); + return this->output.rate; } @@ -1381,6 +1381,7 @@ static void ao_close(xine_audio_port_t *this_gen, xine_stream_t *stream) { xprintf (this->xine, XINE_VERBOSITY_DEBUG, "ao_close\n"); /* unregister stream */ + if (stream == NULL) stream = NULL_STREAM; pthread_mutex_lock(&this->streams_lock); for (cur = xine_list_first_content(this->streams); cur; cur = xine_list_next_content(this->streams)) diff --git a/src/xine-engine/audio_out.h b/src/xine-engine/audio_out.h index bef0bd409..23591e60d 100644 --- a/src/xine-engine/audio_out.h +++ b/src/xine-engine/audio_out.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: audio_out.h,v 1.65 2003/12/31 23:29:06 jcdutton Exp $ + * $Id: audio_out.h,v 1.66 2004/01/07 19:52:42 mroi Exp $ */ #ifndef HAVE_AUDIO_OUT_H #define HAVE_AUDIO_OUT_H @@ -185,6 +185,8 @@ struct xine_audio_port_s { /* open audio driver for audio output * return value: 0:failure, >0:output sample rate */ + /* when you are not a full-blown stream, but still need to open the port + * (e.g. you are a post plugin) it is legal to pass a NULL stream */ int (*open) (xine_audio_port_t *, xine_stream_t *stream, uint32_t bits, uint32_t rate, int mode); @@ -201,6 +203,8 @@ struct xine_audio_port_s { void (*put_buffer) (xine_audio_port_t *, audio_buffer_t *buf, xine_stream_t *stream); /* audio driver is no longer used by decoder => close */ + /* when you are not a full-blown stream, but still need to close the port + * (e.g. you are a post plugin) it is legal to pass a NULL stream */ void (*close) (xine_audio_port_t *self, xine_stream_t *stream); /* called on xine exit */ diff --git a/src/xine-engine/post.c b/src/xine-engine/post.c index a5bb90afa..58771dbb9 100644 --- a/src/xine-engine/post.c +++ b/src/xine-engine/post.c @@ -17,152 +17,354 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: post.c,v 1.20 2003/12/09 00:02:36 f1rmb Exp $ + * $Id: post.c,v 1.21 2004/01/07 19:52:43 mroi Exp $ */ /* * some helper functions for post plugins */ +#define POST_INTERNAL +#define XINE_ENGINE_INTERNAL + #include "post.h" #include <stdarg.h> + +void _x_post_init(post_plugin_t *post, int num_audio_inputs, int num_video_inputs) { + int audio_inputs_size = (num_audio_inputs + 1) * sizeof(xine_audio_port_t *); + int video_inputs_size = (num_video_inputs + 1) * sizeof(xine_video_port_t *); + + post->input = xine_list_new(); + post->output = xine_list_new(); + post->xine_post.audio_input = (xine_audio_port_t **)xine_xmalloc(audio_inputs_size); + post->xine_post.video_input = (xine_video_port_t **)xine_xmalloc(video_inputs_size); +} + + /* dummy intercept functions that just pass the call on to the original port */ static uint32_t post_video_get_capabilities(xine_video_port_t *port_gen) { post_video_port_t *port = (post_video_port_t *)port_gen; - return port->original_port->get_capabilities(port->original_port); + uint32_t caps; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + caps = port->original_port->get_capabilities(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return caps; } static void post_video_open(xine_video_port_t *port_gen, xine_stream_t *stream) { post_video_port_t *port = (post_video_port_t *)port_gen; + + _x_post_rewire_video(port); + _x_post_inc_usage(port); + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->open(port->original_port, stream); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + if (stream) + port->stream = stream; + else + port->stream = POST_NULL_STREAM; } static vo_frame_t *post_video_get_frame(xine_video_port_t *port_gen, uint32_t width, uint32_t height, double ratio, int format, int flags) { post_video_port_t *port = (post_video_port_t *)port_gen; - return port->original_port->get_frame(port->original_port, + vo_frame_t *frame; + + _x_post_rewire_video(port); + if (port->port_lock) pthread_mutex_lock(port->port_lock); + frame = port->original_port->get_frame(port->original_port, width, height, ratio, format, flags); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + + if (frame && (!port->intercept_frame || port->intercept_frame(port, frame))) { + _x_post_inc_usage(port); + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + frame = _x_post_intercept_video_frame(frame, port); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); + } + + return frame; } static vo_frame_t *post_video_get_last_frame(xine_video_port_t *port_gen) { post_video_port_t *port = (post_video_port_t *)port_gen; - return port->original_port->get_last_frame(port->original_port); -} + vo_frame_t *frame; + if (port->port_lock) pthread_mutex_lock(port->port_lock); + frame = port->original_port->get_last_frame(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return frame; +} + static void post_video_enable_ovl(xine_video_port_t *port_gen, int ovl_enable) { post_video_port_t *port = (post_video_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->enable_ovl(port->original_port, ovl_enable); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); } static void post_video_close(xine_video_port_t *port_gen, xine_stream_t *stream) { post_video_port_t *port = (post_video_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->close(port->original_port, stream); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + port->stream = NULL; + _x_post_dec_usage(port); } static void post_video_exit(xine_video_port_t *port_gen) { post_video_port_t *port = (post_video_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->exit(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); } static video_overlay_manager_t *post_video_get_overlay_manager(xine_video_port_t *port_gen) { post_video_port_t *port = (post_video_port_t *)port_gen; - return port->original_port->get_overlay_manager(port->original_port); + video_overlay_manager_t *manager; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + manager = port->original_port->get_overlay_manager(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + + if (port->intercept_ovl && port->intercept_ovl(port)) { + if (manager && !port->original_manager) + /* this is the first access to overlay manager */ + _x_post_intercept_overlay_manager(manager, port); + else + /* the original port might have changed */ + port->original_manager = manager; + return port->new_manager; + } else + return manager; } static void post_video_flush(xine_video_port_t *port_gen) { post_video_port_t *port = (post_video_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->flush(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); } static int post_video_status(xine_video_port_t *port_gen, xine_stream_t *stream, int *width, int *height, int64_t *img_duration) { post_video_port_t *port = (post_video_port_t *)port_gen; - return port->original_port->status(port->original_port, stream, width, height, img_duration); + int status; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + status = port->original_port->status(port->original_port, stream, width, height, img_duration); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return status; } static int post_video_get_property(xine_video_port_t *port_gen, int property) { post_video_port_t *port = (post_video_port_t *)port_gen; - return port->original_port->get_property(port->original_port, property); + int prop; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + prop = port->original_port->get_property(port->original_port, property); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return prop; } static int post_video_set_property(xine_video_port_t *port_gen, int property, int value) { post_video_port_t *port = (post_video_port_t *)port_gen; - return port->original_port->set_property(port->original_port, property, value); + int val; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + val = port->original_port->set_property(port->original_port, property, value); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return val; } -post_video_port_t *_x_post_intercept_video_port(post_plugin_t *post, xine_video_port_t *original) { - post_video_port_t *post_port = (post_video_port_t *)malloc(sizeof(post_video_port_t)); + +static int post_video_rewire(xine_post_out_t *output_gen, void *data) { + post_out_t *output = (post_out_t *)output_gen; + xine_video_port_t *new_port = (xine_video_port_t *)data; + post_video_port_t *input_port = (post_video_port_t *)output->user_data; - if (!post_port) + if (!new_port) + return 0; + pthread_mutex_lock(&input_port->next_port_lock); + pthread_mutex_lock(&input_port->usage_lock); + input_port->next_port = new_port; + while (input_port->next_port) { + if (input_port->usage_count == 0) { + /* we can safely rewire right here, the plugin is not in use */ + input_port->original_port = new_port; + input_port->next_port = NULL; + } + pthread_mutex_unlock(&input_port->usage_lock); + if (input_port->next_port) + pthread_cond_wait(&input_port->next_port_wire, &input_port->next_port_lock); + pthread_mutex_lock(&input_port->usage_lock); + } + pthread_mutex_unlock(&input_port->usage_lock); + pthread_mutex_unlock(&input_port->next_port_lock); + return 1; +} + + +post_video_port_t *_x_post_intercept_video_port(post_plugin_t *post, xine_video_port_t *original, + post_in_t **input, post_out_t **output) { + post_video_port_t *port = (post_video_port_t *)xine_xmalloc(sizeof(post_video_port_t)); + + if (!port) return NULL; - post_port->port.get_capabilities = post_video_get_capabilities; - post_port->port.open = post_video_open; - post_port->port.get_frame = post_video_get_frame; - post_port->port.get_last_frame = post_video_get_last_frame; - post_port->port.enable_ovl = post_video_enable_ovl; - post_port->port.close = post_video_close; - post_port->port.exit = post_video_exit; - post_port->port.get_overlay_manager = post_video_get_overlay_manager; - post_port->port.flush = post_video_flush; - post_port->port.status = post_video_status; - post_port->port.get_property = post_video_get_property; - post_port->port.set_property = post_video_set_property; - post_port->port.driver = original->driver; + port->new_port.get_capabilities = post_video_get_capabilities; + port->new_port.open = post_video_open; + port->new_port.get_frame = post_video_get_frame; + port->new_port.get_last_frame = post_video_get_last_frame; + port->new_port.enable_ovl = post_video_enable_ovl; + port->new_port.close = post_video_close; + port->new_port.exit = post_video_exit; + port->new_port.get_overlay_manager = post_video_get_overlay_manager; + port->new_port.flush = post_video_flush; + port->new_port.status = post_video_status; + port->new_port.get_property = post_video_get_property; + port->new_port.set_property = post_video_set_property; + port->new_port.driver = original->driver; + + port->original_port = original; + port->new_frame = &port->frame_storage; + port->new_manager = &port->manager_storage; + port->post = post; + + pthread_mutex_init(&port->next_port_lock, NULL); + pthread_cond_init(&port->next_port_wire, NULL); + pthread_mutex_init(&port->usage_lock, NULL); + pthread_mutex_init(&port->free_frames_lock, NULL); - post_port->original_port = original; - post_port->post = post; + if (input) { + *input = (post_in_t *)xine_xmalloc(sizeof(post_in_t)); + if (!*input) return port; + (*input)->xine_in.name = "video in"; + (*input)->xine_in.type = XINE_POST_DATA_VIDEO; + (xine_video_port_t *)(*input)->xine_in.data = &port->new_port; + (*input)->post = post; + xine_list_append_content(post->input, *input); + } - return post_port; + if (output) { + *output = (post_out_t *)xine_xmalloc(sizeof(post_out_t)); + if (!*output) return port; + (*output)->xine_out.name = "video out"; + (*output)->xine_out.type = XINE_POST_DATA_VIDEO; + (xine_video_port_t **)(*output)->xine_out.data = &port->original_port; + (*output)->xine_out.rewire = post_video_rewire; + (*output)->post = post; + (*output)->user_data = port; + xine_list_append_content(post->output, *output); + } + + return port; +} + + +void _x_post_rewire_video(post_video_port_t *port) { + if (port->next_port) { + pthread_mutex_lock(&port->next_port_lock); + if (port->next_port) { + port->next_port->open(port->next_port, (port->stream == POST_NULL_STREAM) ? NULL : port->stream); + port->original_port->close(port->original_port, (port->stream == POST_NULL_STREAM) ? NULL : port->stream); + port->original_port = port->next_port; + port->next_port = NULL; + pthread_mutex_unlock(&port->next_port_lock); + pthread_cond_broadcast(&port->next_port_wire); + } else + pthread_mutex_unlock(&port->next_port_lock); + } } /* dummy intercept functions for frames */ static void post_frame_free(vo_frame_t *vo_img) { - post_video_port_t *port = (post_video_port_t *)vo_img->port; - _x_post_restore_video_frame(vo_img, port); - vo_img->free(vo_img); -} + post_video_port_t *port = _x_post_video_frame_to_port(vo_img); + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + if (--vo_img->lock_counter == 0) { + /* this frame is free */ + vo_img = _x_post_restore_video_frame(vo_img, port); + vo_img->free(vo_img); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); + _x_post_dec_usage(port); + } else { + /* this frame is still in use */ + _x_post_frame_copy_up(vo_img, vo_img->next); + vo_img->next->free(vo_img->next); + _x_post_frame_copy_down(vo_img, vo_img->next); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); + } +} + static void post_frame_proc_slice(vo_frame_t *vo_img, uint8_t **src) { - post_video_port_t *port = (post_video_port_t *)vo_img->port; - vo_img->port = port->original_port; - port->original_frame.proc_slice(vo_img, src); - vo_img->port = &port->port; + post_video_port_t *port = _x_post_video_frame_to_port(vo_img); + + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + _x_post_frame_copy_up(vo_img, vo_img->next); + vo_img->next->proc_slice(vo_img->next, src); + _x_post_frame_copy_down(vo_img, vo_img->next); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); } static void post_frame_proc_frame(vo_frame_t *vo_img) { - post_video_port_t *port = (post_video_port_t *)vo_img->port; - vo_img->port = port->original_port; - port->original_frame.proc_frame(vo_img); - vo_img->port = &port->port; + post_video_port_t *port = _x_post_video_frame_to_port(vo_img); + + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + _x_post_frame_copy_up(vo_img, vo_img->next); + vo_img->next->proc_frame(vo_img->next); + _x_post_frame_copy_down(vo_img, vo_img->next); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); } static void post_frame_field(vo_frame_t *vo_img, int which_field) { - post_video_port_t *port = (post_video_port_t *)vo_img->port; - vo_img->port = port->original_port; - port->original_frame.field(vo_img, which_field); - vo_img->port = &port->port; + post_video_port_t *port = _x_post_video_frame_to_port(vo_img); + + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + _x_post_frame_copy_up(vo_img, vo_img->next); + vo_img->next->field(vo_img->next, which_field); + _x_post_frame_copy_down(vo_img, vo_img->next); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); } static int post_frame_draw(vo_frame_t *vo_img, xine_stream_t *stream) { - post_video_port_t *port = (post_video_port_t *)vo_img->port; - _x_post_restore_video_frame(vo_img, port); - return vo_img->draw(vo_img, stream); + post_video_port_t *port = _x_post_video_frame_to_port(vo_img); + int skip; + + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + _x_post_frame_copy_up(vo_img, vo_img->next); + skip = vo_img->next->draw(vo_img->next, stream); + _x_post_frame_copy_down(vo_img, vo_img->next); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); + return skip; } static void post_frame_lock(vo_frame_t *vo_img) { - post_video_port_t *port = (post_video_port_t *)vo_img->port; - vo_img->port = port->original_port; - port->original_frame.lock(vo_img); - vo_img->port = &port->port; + post_video_port_t *port = _x_post_video_frame_to_port(vo_img); + + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + _x_post_frame_copy_up(vo_img, vo_img->next); + vo_img->lock_counter++; + vo_img->next->lock(vo_img->next); + _x_post_frame_copy_down(vo_img, vo_img->next); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); } static void post_frame_dispose(vo_frame_t *vo_img) { - post_video_port_t *port = (post_video_port_t *)vo_img->port; - _x_post_restore_video_frame(vo_img, port); + post_video_port_t *port = _x_post_video_frame_to_port(vo_img); + + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + vo_img = _x_post_restore_video_frame(vo_img, port); vo_img->dispose(vo_img); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); + _x_post_dec_usage(port); } static void post_frame_proc_macro_block(int x, @@ -180,164 +382,308 @@ static void post_frame_proc_macro_block(int x, int second_field, int (*f_mot_pmv)[2], int (*b_mot_pmv)[2]) { - post_video_port_t *port = (post_video_port_t *)current_frame->port; - _x_post_restore_video_frame(current_frame, port); - _x_post_restore_video_frame(forward_ref_frame, port); - _x_post_restore_video_frame(backward_ref_frame, port); - current_frame->proc_macro_block(x, y, mb_type, motion_type, mv_field_sel, - dmvector, cbp, dct_type, current_frame, - forward_ref_frame, backward_ref_frame, - picture_structure, second_field, - f_mot_pmv, b_mot_pmv); -} - - - -void _x_post_intercept_video_frame(vo_frame_t *frame, post_video_port_t *port) { - port->original_frame.port = frame->port; - port->original_frame.free = frame->free; - port->original_frame.proc_slice = frame->proc_slice; - port->original_frame.proc_frame = frame->proc_frame; - port->original_frame.proc_macro_block = frame->proc_macro_block; - port->original_frame.field = frame->field; - port->original_frame.draw = frame->draw; - port->original_frame.lock = frame->lock; - port->original_frame.dispose = frame->dispose; - - frame->port = &port->port; - frame->free = post_frame_free; - frame->proc_slice = frame->proc_slice ? post_frame_proc_slice : NULL; - frame->proc_frame = frame->proc_frame ? post_frame_proc_frame : NULL; - frame->proc_macro_block = frame->proc_macro_block ? post_frame_proc_macro_block : NULL; - frame->field = post_frame_field; - frame->draw = post_frame_draw; - frame->lock = post_frame_lock; - frame->dispose = post_frame_dispose; -} - -void _x_post_restore_video_frame(vo_frame_t *frame, post_video_port_t *port) { - frame->port = port->original_port; - frame->free = port->original_frame.free; - frame->proc_slice = port->original_frame.proc_slice; - frame->proc_frame = port->original_frame.proc_frame; - frame->proc_macro_block = port->original_frame.proc_macro_block; - frame->field = port->original_frame.field; - frame->draw = port->original_frame.draw; - frame->lock = port->original_frame.lock; - frame->dispose = port->original_frame.dispose; + post_video_port_t *port = _x_post_video_frame_to_port(current_frame); + + if (port->frame_lock) pthread_mutex_lock(port->frame_lock); + _x_post_frame_copy_up(current_frame, current_frame->next); + current_frame->next->proc_macro_block(x, y, mb_type, motion_type, mv_field_sel, + dmvector, cbp, dct_type, current_frame->next, + forward_ref_frame, backward_ref_frame, + picture_structure, second_field, + f_mot_pmv, b_mot_pmv); + _x_post_frame_copy_down(current_frame, current_frame->next); + if (port->frame_lock) pthread_mutex_unlock(port->frame_lock); +} + + +vo_frame_t *_x_post_intercept_video_frame(vo_frame_t *frame, post_video_port_t *port) { + vo_frame_t *new_frame; + + /* get a free frame slot */ + pthread_mutex_lock(&port->free_frames_lock); + if (port->free_frame_slots) { + new_frame = port->free_frame_slots; + port->free_frame_slots = new_frame->next; + } else { + new_frame = (vo_frame_t *)xine_xmalloc(sizeof(vo_frame_t)); + } + pthread_mutex_unlock(&port->free_frames_lock); + + /* make a copy and attach the original */ + xine_fast_memcpy(new_frame, frame, sizeof(vo_frame_t)); + new_frame->next = frame; + + /* modify the frame with the intercept functions */ + new_frame->port = &port->new_port; + new_frame->proc_frame = + port->new_frame->proc_frame ? port->new_frame->proc_frame : NULL; + new_frame->proc_slice = + port->new_frame->proc_slice ? port->new_frame->proc_slice : NULL; + new_frame->proc_macro_block = + port->new_frame->proc_macro_block ? port->new_frame->proc_macro_block : NULL; + new_frame->field = + port->new_frame->field ? port->new_frame->field : post_frame_field; + new_frame->draw = + port->new_frame->draw ? port->new_frame->draw : post_frame_draw; + new_frame->lock = + port->new_frame->lock ? port->new_frame->lock : post_frame_lock; + new_frame->free = + port->new_frame->free ? port->new_frame->free : post_frame_free; + new_frame->dispose = + port->new_frame->dispose ? port->new_frame->dispose : post_frame_dispose; + + if (!port->new_frame->draw) { + /* draw will most likely modify the frame, so the decoder + * should only request preprocessing when there is no new draw */ + if (frame->proc_frame && !new_frame->proc_frame) + new_frame->proc_frame = post_frame_proc_frame; + if (frame->proc_slice && !new_frame->proc_slice) + new_frame->proc_slice = post_frame_proc_slice; + if (frame->proc_macro_block && !new_frame->proc_macro_block) + new_frame->proc_macro_block = post_frame_proc_macro_block; + } + + return new_frame; +} + +vo_frame_t *_x_post_restore_video_frame(vo_frame_t *frame, post_video_port_t *port) { + /* the first attched context is the original frame */ + vo_frame_t *original = frame->next; + + /* propagate any changes */ + _x_post_frame_copy_up(frame, original); + + /* put the now free slot into the free frames list */ + pthread_mutex_lock(&port->free_frames_lock); + frame->next = port->free_frame_slots; + port->free_frame_slots = frame; + pthread_mutex_unlock(&port->free_frames_lock); + + return original; +} + +void _x_post_frame_copy_up(vo_frame_t *from, vo_frame_t *to) { + /* propagate changes upwards (from decoders to video out) */ + to->pts = from->pts; + to->bad_frame = from->bad_frame; + to->duration = from->duration; + to->top_field_first = from->top_field_first; + to->repeat_first_field = from->repeat_first_field; + to->progressive_frame = from->progressive_frame; + to->picture_coding_type = from->picture_coding_type; + to->drawn = from->drawn; + to->macroblocks = from->macroblocks; + to->stream = from->stream; + + if (to->extra_info != from->extra_info) + _x_extra_info_merge(to->extra_info, from->extra_info); +} + +void _x_post_frame_copy_down(vo_frame_t *to, vo_frame_t *from) { + /* propagate changes downwards (from video out to decoders) */ + to->vpts = from->vpts; + to->duration = from->duration; + to->stream = from->stream; + + if (to->extra_info != from->extra_info) + _x_extra_info_merge(to->extra_info, from->extra_info); +} + +void _x_post_frame_u_turn(vo_frame_t *frame, xine_stream_t *stream) { + /* frame's travel will end here => do the housekeeping */ + frame->stream = stream; + if (stream) { + _x_extra_info_merge(frame->extra_info, stream->video_decoder_extra_info); + stream->metronom->got_video_frame(stream->metronom, frame); + } } /* dummy intercept functions that just pass the call on to the original overlay manager */ static void post_overlay_init(video_overlay_manager_t *ovl_gen) { - post_overlay_manager_t *ovl = (post_overlay_manager_t *)ovl_gen; - ovl->original_manager->init(ovl->original_manager); + post_video_port_t *port = _x_post_ovl_manager_to_port(ovl_gen); + + if (port->manager_lock) pthread_mutex_lock(port->manager_lock); + port->original_manager->init(port->original_manager); + if (port->manager_lock) pthread_mutex_unlock(port->manager_lock); } static void post_overlay_dispose(video_overlay_manager_t *ovl_gen) { - post_overlay_manager_t *ovl = (post_overlay_manager_t *)ovl_gen; - ovl->original_manager->dispose(ovl->original_manager); + post_video_port_t *port = _x_post_ovl_manager_to_port(ovl_gen); + + if (port->manager_lock) pthread_mutex_lock(port->manager_lock); + port->original_manager->dispose(port->original_manager); + if (port->manager_lock) pthread_mutex_unlock(port->manager_lock); } static int32_t post_overlay_get_handle(video_overlay_manager_t *ovl_gen, int object_type) { - post_overlay_manager_t *ovl = (post_overlay_manager_t *)ovl_gen; - return ovl->original_manager->get_handle(ovl->original_manager, object_type); + post_video_port_t *port = _x_post_ovl_manager_to_port(ovl_gen); + int32_t handle; + + if (port->manager_lock) pthread_mutex_lock(port->manager_lock); + handle = port->original_manager->get_handle(port->original_manager, object_type); + if (port->manager_lock) pthread_mutex_unlock(port->manager_lock); + return handle; } static void post_overlay_free_handle(video_overlay_manager_t *ovl_gen, int32_t handle) { - post_overlay_manager_t *ovl = (post_overlay_manager_t *)ovl_gen; - ovl->original_manager->free_handle(ovl->original_manager, handle); + post_video_port_t *port = _x_post_ovl_manager_to_port(ovl_gen); + + if (port->manager_lock) pthread_mutex_lock(port->manager_lock); + port->original_manager->free_handle(port->original_manager, handle); + if (port->manager_lock) pthread_mutex_unlock(port->manager_lock); } static int32_t post_overlay_add_event(video_overlay_manager_t *ovl_gen, void *event) { - post_overlay_manager_t *ovl = (post_overlay_manager_t *)ovl_gen; - return ovl->original_manager->add_event(ovl->original_manager, event); + post_video_port_t *port = _x_post_ovl_manager_to_port(ovl_gen); + int32_t result; + + if (port->manager_lock) pthread_mutex_lock(port->manager_lock); + result = port->original_manager->add_event(port->original_manager, event); + if (port->manager_lock) pthread_mutex_unlock(port->manager_lock); + return result; } static void post_overlay_flush_events(video_overlay_manager_t *ovl_gen) { - post_overlay_manager_t *ovl = (post_overlay_manager_t *)ovl_gen; - ovl->original_manager->flush_events(ovl->original_manager); + post_video_port_t *port = _x_post_ovl_manager_to_port(ovl_gen); + + if (port->manager_lock) pthread_mutex_lock(port->manager_lock); + port->original_manager->flush_events(port->original_manager); + if (port->manager_lock) pthread_mutex_unlock(port->manager_lock); } static int post_overlay_redraw_needed(video_overlay_manager_t *ovl_gen, int64_t vpts) { - post_overlay_manager_t *ovl = (post_overlay_manager_t *)ovl_gen; - return ovl->original_manager->redraw_needed(ovl->original_manager, vpts); + post_video_port_t *port = _x_post_ovl_manager_to_port(ovl_gen); + int redraw; + + if (port->manager_lock) pthread_mutex_lock(port->manager_lock); + redraw = port->original_manager->redraw_needed(port->original_manager, vpts); + if (port->manager_lock) pthread_mutex_unlock(port->manager_lock); + return redraw; } static void post_overlay_multiple_overlay_blend(video_overlay_manager_t *ovl_gen, int64_t vpts, vo_driver_t *output, vo_frame_t *vo_img, int enabled) { - post_overlay_manager_t *ovl = (post_overlay_manager_t *)ovl_gen; - ovl->original_manager->multiple_overlay_blend(ovl->original_manager, vpts, output, vo_img, enabled); -} - - -post_overlay_manager_t *_x_post_intercept_overlay_manager(post_plugin_t *post, - video_overlay_manager_t *original) { - post_overlay_manager_t *post_ovl = (post_overlay_manager_t *)malloc(sizeof(post_overlay_manager_t)); + post_video_port_t *port = _x_post_ovl_manager_to_port(ovl_gen); - if (!post_ovl) - return NULL; + if (port->manager_lock) pthread_mutex_lock(port->manager_lock); + port->original_manager->multiple_overlay_blend(port->original_manager, vpts, output, vo_img, enabled); + if (port->manager_lock) pthread_mutex_unlock(port->manager_lock); +} + + +void _x_post_intercept_overlay_manager(video_overlay_manager_t *original, post_video_port_t *port) { + if (!port->new_manager->init) + port->new_manager->init = post_overlay_init; + if (!port->new_manager->dispose) + port->new_manager->dispose = post_overlay_dispose; + if (!port->new_manager->get_handle) + port->new_manager->get_handle = post_overlay_get_handle; + if (!port->new_manager->free_handle) + port->new_manager->free_handle = post_overlay_free_handle; + if (!port->new_manager->add_event) + port->new_manager->add_event = post_overlay_add_event; + if (!port->new_manager->flush_events) + port->new_manager->flush_events = post_overlay_flush_events; + if (!port->new_manager->redraw_needed) + port->new_manager->redraw_needed = post_overlay_redraw_needed; + if (!port->new_manager->multiple_overlay_blend) + port->new_manager->multiple_overlay_blend = post_overlay_multiple_overlay_blend; - post_ovl->manager.init = post_overlay_init; - post_ovl->manager.dispose = post_overlay_dispose; - post_ovl->manager.get_handle = post_overlay_get_handle; - post_ovl->manager.free_handle = post_overlay_free_handle; - post_ovl->manager.add_event = post_overlay_add_event; - post_ovl->manager.flush_events = post_overlay_flush_events; - post_ovl->manager.redraw_needed = post_overlay_redraw_needed; - post_ovl->manager.multiple_overlay_blend = post_overlay_multiple_overlay_blend; - - post_ovl->original_manager = original; - post_ovl->post = post; - - return post_ovl; + port->original_manager = original; } /* dummy intercept functions that just pass the call on to the original port */ static uint32_t post_audio_get_capabilities(xine_audio_port_t *port_gen) { post_audio_port_t *port = (post_audio_port_t *)port_gen; - return port->original_port->get_capabilities(port->original_port); + uint32_t caps; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + caps = port->original_port->get_capabilities(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return caps; } static int post_audio_get_property(xine_audio_port_t *port_gen, int property) { post_audio_port_t *port = (post_audio_port_t *)port_gen; - return port->original_port->get_property(port->original_port, property); + int prop; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + prop = port->original_port->get_property(port->original_port, property); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return prop; } static int post_audio_set_property(xine_audio_port_t *port_gen, int property, int value) { post_audio_port_t *port = (post_audio_port_t *)port_gen; - return port->original_port->set_property(port->original_port, property, value); + int val; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + val = port->original_port->set_property(port->original_port, property, value); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return val; } static int post_audio_open(xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bits, uint32_t rate, int mode) { post_audio_port_t *port = (post_audio_port_t *)port_gen; - return port->original_port->open(port->original_port, stream, bits, rate, mode); -} - -static audio_buffer_t * post_audio_get_buffer(xine_audio_port_t *port_gen) { + int result; + + _x_post_rewire_audio(port); + _x_post_inc_usage(port); + if (port->port_lock) pthread_mutex_lock(port->port_lock); + result = port->original_port->open(port->original_port, stream, bits, rate, mode); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + if (stream) + port->stream = stream; + else + port->stream = POST_NULL_STREAM; + port->bits = bits; + port->rate = rate; + port->mode = mode; + return result; +} + +static audio_buffer_t *post_audio_get_buffer(xine_audio_port_t *port_gen) { post_audio_port_t *port = (post_audio_port_t *)port_gen; - return port->original_port->get_buffer(port->original_port); + audio_buffer_t *buf; + + _x_post_rewire_audio(port); + if (port->port_lock) pthread_mutex_lock(port->port_lock); + buf = port->original_port->get_buffer(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return buf; } static void post_audio_put_buffer(xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_stream_t *stream) { post_audio_port_t *port = (post_audio_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->put_buffer(port->original_port, buf, stream); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); } static void post_audio_close(xine_audio_port_t *port_gen, xine_stream_t *stream) { post_audio_port_t *port = (post_audio_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->close(port->original_port, stream); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + port->stream = NULL; + _x_post_dec_usage(port); } static void post_audio_exit(xine_audio_port_t *port_gen) { post_audio_port_t *port = (post_audio_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->exit(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); } -static int post_audio_control (xine_audio_port_t *port_gen, int cmd, ...) { +static int post_audio_control(xine_audio_port_t *port_gen, int cmd, ...) { post_audio_port_t *port = (post_audio_port_t *)port_gen; va_list args; void *arg; @@ -345,7 +691,9 @@ static int post_audio_control (xine_audio_port_t *port_gen, int cmd, ...) { va_start(args, cmd); arg = va_arg(args, void*); + if (port->port_lock) pthread_mutex_lock(port->port_lock); rval = port->original_port->control(port->original_port, cmd, arg); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); va_end(args); return rval; @@ -353,36 +701,229 @@ static int post_audio_control (xine_audio_port_t *port_gen, int cmd, ...) { static void post_audio_flush(xine_audio_port_t *port_gen) { post_audio_port_t *port = (post_audio_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); port->original_port->flush(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); } static int post_audio_status(xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t *bits, uint32_t *rate, int *mode) { post_audio_port_t *port = (post_audio_port_t *)port_gen; - return port->original_port->status(port->original_port, stream, bits, rate, mode); + int result; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + result = port->original_port->status(port->original_port, stream, bits, rate, mode); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); + return result; } -post_audio_port_t *_x_post_intercept_audio_port(post_plugin_t *post, xine_audio_port_t *original) { - post_audio_port_t *post_port = (post_audio_port_t *)malloc(sizeof(post_audio_port_t)); +static int post_audio_rewire(xine_post_out_t *output_gen, void *data) { + post_out_t *output = (post_out_t *)output_gen; + xine_audio_port_t *new_port = (xine_audio_port_t *)data; + post_audio_port_t *input_port = (post_audio_port_t *)output->user_data; + + if (!new_port) + return 0; + pthread_mutex_lock(&input_port->next_port_lock); + pthread_mutex_lock(&input_port->usage_lock); + input_port->next_port = new_port; + while (input_port->next_port) { + if (input_port->usage_count == 0) { + /* we can safely rewire right here, the plugin is not in use */ + input_port->original_port = new_port; + input_port->next_port = NULL; + } + pthread_mutex_unlock(&input_port->usage_lock); + if (input_port->next_port) + pthread_cond_wait(&input_port->next_port_wire, &input_port->next_port_lock); + pthread_mutex_lock(&input_port->usage_lock); + } + pthread_mutex_unlock(&input_port->usage_lock); + pthread_mutex_unlock(&input_port->next_port_lock); + return 1; +} + +post_audio_port_t *_x_post_intercept_audio_port(post_plugin_t *post, xine_audio_port_t *original, + post_in_t **input, post_out_t **output) { + post_audio_port_t *port = (post_audio_port_t *)xine_xmalloc(sizeof(post_audio_port_t)); - if (!post_port) + if (!port) return NULL; - post_port->port.open = post_audio_open; - post_port->port.get_buffer = post_audio_get_buffer; - post_port->port.put_buffer = post_audio_put_buffer; - post_port->port.close = post_audio_close; - post_port->port.exit = post_audio_exit; - post_port->port.get_capabilities = post_audio_get_capabilities; - post_port->port.get_property = post_audio_get_property; - post_port->port.set_property = post_audio_set_property; - post_port->port.control = post_audio_control; - post_port->port.flush = post_audio_flush; - post_port->port.status = post_audio_status; + port->new_port.open = post_audio_open; + port->new_port.get_buffer = post_audio_get_buffer; + port->new_port.put_buffer = post_audio_put_buffer; + port->new_port.close = post_audio_close; + port->new_port.exit = post_audio_exit; + port->new_port.get_capabilities = post_audio_get_capabilities; + port->new_port.get_property = post_audio_get_property; + port->new_port.set_property = post_audio_set_property; + port->new_port.control = post_audio_control; + port->new_port.flush = post_audio_flush; + port->new_port.status = post_audio_status; + + port->original_port = original; + port->post = post; + + pthread_mutex_init(&port->next_port_lock, NULL); + pthread_cond_init(&port->next_port_wire, NULL); + pthread_mutex_init(&port->usage_lock, NULL); + + if (input) { + *input = (post_in_t *)xine_xmalloc(sizeof(post_in_t)); + if (!*input) return port; + (*input)->xine_in.name = "audio in"; + (*input)->xine_in.type = XINE_POST_DATA_AUDIO; + (xine_audio_port_t *)(*input)->xine_in.data = &port->new_port; + (*input)->post = post; + xine_list_append_content(post->input, *input); + } + + if (output) { + *output = (post_out_t *)xine_xmalloc(sizeof(post_out_t)); + if (!*output) return port; + (*output)->xine_out.name = "audio out"; + (*output)->xine_out.type = XINE_POST_DATA_AUDIO; + (xine_audio_port_t **)(*output)->xine_out.data = &port->original_port; + (*output)->xine_out.rewire = post_audio_rewire; + (*output)->post = post; + (*output)->user_data = port; + xine_list_append_content(post->output, *output); + } + + return port; +} + + +void _x_post_rewire_audio(post_audio_port_t *port) { + if (port->next_port) { + pthread_mutex_lock(&port->next_port_lock); + if (port->next_port) { + port->next_port->open(port->next_port, (port->stream == POST_NULL_STREAM) ? NULL : port->stream, + port->bits, port->rate, port->mode); + port->original_port->close(port->original_port, (port->stream == POST_NULL_STREAM) ? NULL : port->stream); + port->original_port = port->next_port; + port->next_port = NULL; + pthread_mutex_unlock(&port->next_port_lock); + pthread_cond_broadcast(&port->next_port_wire); + } else + pthread_mutex_unlock(&port->next_port_lock); + } +} + + +int _x_post_dispose(post_plugin_t *this) { + int i, in_use = 0; + + /* acquire all usage locks */ + for (i = 0; this->xine_post.audio_input[i]; i++) { + post_audio_port_t *port = (post_audio_port_t *)this->xine_post.audio_input[i]; + pthread_mutex_lock(&port->usage_lock); + } + for (i = 0; this->xine_post.video_input[i]; i++) { + post_video_port_t *port = (post_video_port_t *)this->xine_post.video_input[i]; + pthread_mutex_lock(&port->usage_lock); + } + + /* we can set this witout harm, because it is always checked with + * usage lock held */ + this->dispose_pending = 1; + + /* check counters */ + for (i = 0; this->xine_post.audio_input[i]; i++) { + post_audio_port_t *port = (post_audio_port_t *)this->xine_post.audio_input[i]; + if (port->usage_count > 0) { + in_use = 1; + break; + } + } + for (i = 0; this->xine_post.video_input[i]; i++) { + post_video_port_t *port = (post_video_port_t *)this->xine_post.video_input[i]; + if (port->usage_count > 0) { + in_use = 1; + break; + } + } + + /* free the locks */ + for (i = 0; this->xine_post.audio_input[i]; i++) { + post_audio_port_t *port = (post_audio_port_t *)this->xine_post.audio_input[i]; + pthread_mutex_unlock(&port->usage_lock); + } + for (i = 0; this->xine_post.video_input[i]; i++) { + post_video_port_t *port = (post_video_port_t *)this->xine_post.video_input[i]; + pthread_mutex_unlock(&port->usage_lock); + } + + if (!in_use) { + xine_post_in_t *input; + xine_post_out_t *output; + + /* we can really dispose it */ + + free(this->xine_post.audio_input); + free(this->xine_post.video_input); + + for (input = xine_list_first_content(this->input); input; + input = xine_list_next_content(this->input)) { + switch (input->type) { + case XINE_POST_DATA_VIDEO: + { + post_video_port_t *port = (post_video_port_t *)input->data; + vo_frame_t *first, *second; + + pthread_mutex_destroy(&port->next_port_lock); + pthread_cond_destroy(&port->next_port_wire); + pthread_mutex_destroy(&port->usage_lock); + pthread_mutex_destroy(&port->free_frames_lock); + + second = NULL; + for (first = port->free_frame_slots; first; + second = first, first = first->next) + free(second); + free(second); + + free(port); + free(input); + } + break; + case XINE_POST_DATA_AUDIO: + { + post_audio_port_t *port = (post_audio_port_t *)input->data; + + pthread_mutex_destroy(&port->next_port_lock); + pthread_cond_destroy(&port->next_port_wire); + pthread_mutex_destroy(&port->usage_lock); + + free(port); + free(input); + } + break; + } + } + for (output = xine_list_first_content(this->output); output; + output = xine_list_next_content(this->output)) { + switch (output->type) { + case XINE_POST_DATA_VIDEO: + if (output->rewire == post_video_rewire) + /* we allocated it, we free it */ + free(output); + break; + case XINE_POST_DATA_AUDIO: + if (output->rewire == post_audio_rewire) + /* we allocated it, we free it */ + free(output); + break; + } + } + + xine_list_free(this->input); + xine_list_free(this->output); - post_port->original_port = original; - post_port->post = post; + return 1; + } - return post_port; + return 0; } diff --git a/src/xine-engine/post.h b/src/xine-engine/post.h index 5c3055929..1ddc1b580 100644 --- a/src/xine-engine/post.h +++ b/src/xine-engine/post.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: post.h,v 1.16 2003/12/14 22:13:26 siggi Exp $ + * $Id: post.h,v 1.17 2004/01/07 19:52:43 mroi Exp $ * * post plugin definitions * @@ -30,20 +30,25 @@ # include "xine.h" # include "video_out.h" # include "audio_out.h" +# include "xine_internal.h" # include "xineutils.h" #else # include <xine.h> # include <xine/video_out.h> # include <xine/audio_out.h> +# include <xine/xine_internal.h> # include <xine/xineutils.h> #endif -#define POST_PLUGIN_IFACE_VERSION 7 +#define POST_PLUGIN_IFACE_VERSION 8 + +#define POST_NULL_STREAM (xine_stream_t *)-1 typedef struct post_class_s post_class_t; typedef struct post_plugin_s post_plugin_t; - +typedef struct post_in_s post_in_t; +typedef struct post_out_s post_out_t; struct post_class_s { @@ -89,6 +94,9 @@ struct post_plugin_s { */ void (*dispose) (post_plugin_t *this); + /* has dispose been called */ + int dispose_pending; + /* plugins don't have to care for the stuff below */ /* used when the user requests a list of all inputs/outputs */ @@ -99,6 +107,33 @@ struct post_plugin_s { void *node; }; +/* helper function to initialize a post_plugin_t */ +void _x_post_init(post_plugin_t *post, int num_audio_inputs, int num_video_inputs); + +struct post_in_s { + + /* public part of the input */ + xine_post_in_t xine_in; + + /* backward reference so that you have access to the post plugin */ + post_plugin_t *post; + + /* you can fill this to your liking */ + void *user_data; +}; + +struct post_out_s { + + /* public part of the output */ + xine_post_out_t xine_out; + + /* backward reference so that you have access to the post plugin */ + post_plugin_t *post; + + /* you can fill this to your liking */ + void *user_data; +}; + /* Post plugins work by intercepting calls to video or audio ports * in the sense of the decorator design pattern. They reuse the @@ -117,48 +152,130 @@ typedef struct post_video_port_s post_video_port_t; struct post_video_port_s { /* the new public port with replaced function pointers */ - xine_video_port_t port; + xine_video_port_t new_port; /* the original port to call its functions from inside yours */ - xine_video_port_t *original_port; + xine_video_port_t *original_port; - /* here you can keep information about the frames */ - vo_frame_t original_frame; + /* when we are rewiring, next port points to the new port */ + xine_video_port_t *next_port; + + /* rewiring synchronization */ + pthread_mutex_t next_port_lock; + pthread_cond_t next_port_wire; + + /* if you want to decide yourself, whether a given frame should + * be intercepted, fill in this function; get_frame() acts as + * a template method and asks your function; return a boolean; + * the default is to intercept all frames */ + int (*intercept_frame)(post_video_port_t *self, vo_frame_t *frame); + + /* the new frame function pointers */ + vo_frame_t *new_frame; + + /* if you want to decide yourself, whether the overlay manager should + * be intercepted, fill in this function; get_overlay_manager() acts as + * a template method and asks your function; return a boolean; + * the default is _not_ to intercept the overlay manager */ + int (*intercept_ovl)(post_video_port_t *self); - /* backward reference so that you have access to the post plugin - * when the call only gives you the port */ - post_plugin_t *post; -}; - -/* use this to create a new, trivially decorated video port in which - * port functions can be replaced with own implementations */ -post_video_port_t *_x_post_intercept_video_port(post_plugin_t *post, xine_video_port_t *port); - -/* use this to decorate and to undecorate a frame so that its functions - * can be replaced with own implementations */ -void _x_post_intercept_video_frame(vo_frame_t *frame, post_video_port_t *port); -void _x_post_restore_video_frame(vo_frame_t *frame, post_video_port_t *port); - - -/* helper structure for intercepting overlay manager calls */ -typedef struct post_overlay_manager_s post_overlay_manager_t; -struct post_overlay_manager_s { - /* the new public overlay manager with replaced function pointers */ - video_overlay_manager_t manager; + video_overlay_manager_t *new_manager; /* the original manager to call its functions from inside yours */ video_overlay_manager_t *original_manager; + /* usage counter: how many objects are floating around that need + * these pointers to exist */ + int usage_count; + pthread_mutex_t usage_lock; + + /* the stream we are being fed by; NULL means no stream is connected, + * POST_NULL_STREAM means a NULL stream is connected */ + xine_stream_t *stream; + + /* point to a mutex here, if you need some synchronization */ + pthread_mutex_t *port_lock; + pthread_mutex_t *frame_lock; + pthread_mutex_t *manager_lock; + /* backward reference so that you have access to the post plugin - * when the call only gives you the overlay manager */ + * when the call only gives you the port */ post_plugin_t *post; + + /* you can fill this to your liking */ + void *user_data; + +#ifdef POST_INTERNAL + /* some of the above members are to be directly included here, but + * adding the structures would mean that post_video_port_t becomes + * depended of the sizes of these structs; solution: we add pointers + * above and have them point into the memory provided here; + * note that the overlay manager needs to be first so that we can + * reconstruct the post_video_port_t* from overlay manager calls */ + + /* any change here requires a change in _x_post_ovl_manager_to_port() + * below! */ + + video_overlay_manager_t manager_storage; + vo_frame_t frame_storage; + + /* this is used to keep a linked list of free vo_frame_t's */ + vo_frame_t *free_frame_slots; + pthread_mutex_t free_frames_lock; +#endif }; +/* use this to create a new decorated video port in which + * port functions will be replaced with own implementations; + * for convenience, this can also create a related post_in_t and post_out_t */ +post_video_port_t *_x_post_intercept_video_port(post_plugin_t *post, xine_video_port_t *port, + post_in_t **input, post_out_t **output); + +/* this will execute pending rewire operations, calling this at the beginning + * of decoder-called functions like get_frame() and open() is a good idea + * (if you do not intercept get_frame() or open(), this will be done automatically) */ +void _x_post_rewire_video(post_video_port_t *port); + +/* use this to decorate and to undecorate a frame so that its functions + * can be replaced with own implementations, decoration is usually done in + * get_frame(), undecoration in frame->free() */ +vo_frame_t *_x_post_intercept_video_frame(vo_frame_t *frame, post_video_port_t *port); +vo_frame_t *_x_post_restore_video_frame(vo_frame_t *frame, post_video_port_t *port); + +/* when you want to pass a frame call on to the original issuer of the frame, + * you need to propagate potential changes up and down the pipe, so the usual + * procedure for this situation would be: + * + * _x_post_frame_copy_up(frame, frame->next); + * frame->next->function(frame->next); + * _x_post_frame_copy_down(frame, frame->next); + */ +void _x_post_frame_copy_up(vo_frame_t *from, vo_frame_t *to); +void _x_post_frame_copy_down(vo_frame_t *to, vo_frame_t *from); + +/* when you shortcut a frames usual draw() travel so that it will never reach + * the draw() function of the original issuer, you still have to do some + * housekeeping on the frame, before returning control down the pipe */ +void _x_post_frame_u_turn(vo_frame_t *frame, xine_stream_t *stream); + /* use this to create a new, trivially decorated overlay manager in which * port functions can be replaced with own implementations */ -post_overlay_manager_t *_x_post_intercept_overlay_manager(post_plugin_t *post, - video_overlay_manager_t *original); +void _x_post_intercept_overlay_manager(video_overlay_manager_t *manager, post_video_port_t *port); + +/* pointer retrieval functions */ +static inline post_video_port_t *_x_post_video_frame_to_port(vo_frame_t *frame) { + return (post_video_port_t *)frame->port; +} + +static inline post_video_port_t *_x_post_ovl_manager_to_port(video_overlay_manager_t *manager) { +#ifdef POST_INTERNAL + return (post_video_port_t *)( (uint8_t *)manager - + (unsigned)&(((post_video_port_t *)NULL)->manager_storage) ); +#else + return (post_video_port_t *)( (uint8_t *)manager - sizeof(post_video_port_t) ); +#endif +} /* helper structure for intercepting audio port calls */ @@ -166,19 +283,86 @@ typedef struct post_audio_port_s post_audio_port_t; struct post_audio_port_s { /* the new public port with replaced function pointers */ - xine_audio_port_t port; + xine_audio_port_t new_port; /* the original port to call its functions from inside yours */ xine_audio_port_t *original_port; + /* when we are rewiring, next port points to the new port */ + xine_audio_port_t *next_port; + + /* rewiring synchronization */ + pthread_mutex_t next_port_lock; + pthread_cond_t next_port_wire; + + /* usage counter: how many objects are floating around that need + * these pointers to exist */ + int usage_count; + pthread_mutex_t usage_lock; + + /* the stream we are being fed by; NULL means no stream is connected, + * POST_NULL_STREAM means a NULL stream is connected */ + xine_stream_t *stream; + + /* some values remembered by port->open() */ + uint32_t bits; + uint32_t rate; + uint32_t mode; + + /* point to a mutex here, if you need some synchronization */ + pthread_mutex_t *port_lock; + /* backward reference so that you have access to the post plugin * when the call only gives you the port */ post_plugin_t *post; + + /* you can fill this to your liking */ + void *user_data; }; -/* use this to create a new, trivially decorated audio port in which - * port functions can be replaced with own implementations */ -post_audio_port_t *_x_post_intercept_audio_port(post_plugin_t *post, xine_audio_port_t *port); +/* use this to create a new decorated audio port in which + * port functions will be replaced with own implementations */ +post_audio_port_t *_x_post_intercept_audio_port(post_plugin_t *post, xine_audio_port_t *port, + post_in_t **input, post_out_t **output); + +/* this will execute pending rewire operations, calling this at the beginning + * of decoder-called functions like get_buffer() and open() is a good idea + * (if you do not intercept get_buffer() or open(), this will be done automatically) */ +void _x_post_rewire_audio(post_audio_port_t *port); + + +/* the standard disposal operation; returns 1 if the plugin is really + * disposed and you should free everything you malloc()ed yourself */ +int _x_post_dispose(post_plugin_t *post); + + +/* macros to handle usage counter */ + +/* WARNING! + * note that _x_post_dec_usage() can call dispose, so be sure to + * not use any potentially already freed memory after this */ + +#define _x_post_inc_usage(port) \ +do { \ + pthread_mutex_lock(&(port)->usage_lock); \ + (port)->usage_count++; \ + pthread_mutex_unlock(&(port)->usage_lock); \ +} while(0) + +#define _x_post_dec_usage(port) \ +do { \ + pthread_mutex_lock(&(port)->usage_lock); \ + (port)->usage_count--; \ + if ((port)->usage_count == 0) { \ + pthread_cond_broadcast(&(port)->next_port_wire); \ + if ((port)->post->dispose_pending) { \ + pthread_mutex_unlock(&(port)->usage_lock); \ + (port)->post->dispose((port)->post); \ + } else \ + pthread_mutex_unlock(&(port)->usage_lock); \ + } else \ + pthread_mutex_unlock(&(port)->usage_lock); \ +} while(0) /* macros to create parameter descriptors */ diff --git a/src/xine-engine/video_out.c b/src/xine-engine/video_out.c index 7c835288e..594a8199a 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.183 2003/12/05 15:55:05 f1rmb Exp $ + * $Id: video_out.c,v 1.184 2004/01/07 19:52:43 mroi Exp $ * * frame allocation / queuing / scheduling / output functions */ @@ -52,6 +52,8 @@ #define NUM_FRAME_BUFFERS 15 +#define NULL_STREAM (xine_stream_t *)-1 + typedef struct { vo_frame_t *first; vo_frame_t *last; @@ -405,6 +407,7 @@ static int vo_frame_draw (vo_frame_t *img, xine_stream_t *stream) { pthread_mutex_lock(&this->streams_lock); for (stream = xine_list_first_content(this->streams); stream; stream = xine_list_next_content(this->streams)) { + if (stream == NULL_STREAM) continue; pthread_mutex_lock (&stream->first_frame_lock); if (stream->first_frame_flag == 2) { stream->first_frame_flag = (this->grab_only)?0:1; @@ -456,6 +459,7 @@ static int vo_frame_draw (vo_frame_t *img, xine_stream_t *stream) { pthread_mutex_lock(&this->streams_lock); for (stream = xine_list_first_content(this->streams); stream; stream = xine_list_next_content(this->streams)) { + if (stream == NULL_STREAM) continue; _x_stream_info_set(stream, XINE_STREAM_INFO_SKIPPED_FRAMES, 1000 * this->num_frames_skipped / this->num_frames_delivered); _x_stream_info_set(stream, XINE_STREAM_INFO_DISCARDED_FRAMES, @@ -809,6 +813,7 @@ static void overlay_and_display_frame (vos_t *this, pthread_mutex_lock(&this->streams_lock); for (stream = xine_list_first_content(this->streams); stream; stream = xine_list_next_content(this->streams)) { + if (stream == NULL_STREAM) continue; pthread_mutex_lock (&stream->first_frame_lock); if (stream->first_frame_flag) { stream->first_frame_flag = 0; @@ -955,6 +960,7 @@ static void *video_out_loop (void *this_gen) { pthread_mutex_lock(&this->streams_lock); for (stream = xine_list_first_content(this->streams); stream; stream = xine_list_next_content(this->streams)) { + if (stream == NULL_STREAM) continue; if (stream->video_decoder_plugin && stream->video_fifo) { buf_element_t *buf; @@ -1041,44 +1047,30 @@ static void *video_out_loop (void *this_gen) { int xine_get_next_video_frame (xine_video_port_t *this_gen, xine_video_frame_t *frame) { - vos_t *this = (vos_t *) this_gen; - vo_frame_t *img; - xine_stream_t *stream=NULL; + vos_t *this = (vos_t *) this_gen; + vo_frame_t *img = NULL; + xine_stream_t *stream = NULL; - while (!stream) { + while (!img || !stream) { stream = xine_list_first_content(this->streams); - if (!stream) - xine_usec_sleep (1000); - } - - pthread_mutex_lock(&this->display_img_buf_queue->mutex); - - img = this->display_img_buf_queue->first; - - /* FIXME: ugly, use conditions and locks instead */ - - lprintf ("get_next_video_frame demux status = %d, fifo_size=%d\n", - stream->demux_plugin->get_status (stream->demux_plugin), - stream->video_fifo->fifo_size); - - while ( !img && (stream->video_fifo->fifo_size - || (stream->demux_plugin->get_status (stream->demux_plugin)==DEMUX_OK))) { + if (!stream) { + xine_usec_sleep (5000); + continue; + } - pthread_mutex_unlock(&this->display_img_buf_queue->mutex); - xine_usec_sleep (1000); + /* FIXME: ugly, use conditions and locks instead? */ + pthread_mutex_lock(&this->display_img_buf_queue->mutex); - img = this->display_img_buf_queue->first; - xprintf (this->xine, XINE_VERBOSITY_DEBUG, - "video_out: get_next_video_frame demux status = %d, fifo_size=%d\n", - stream->demux_plugin->get_status (stream->demux_plugin), stream->video_fifo->fifo_size); - - - } - - if (!img) { - pthread_mutex_unlock(&this->display_img_buf_queue->mutex); - return 0; + if (!img) { + pthread_mutex_unlock(&this->display_img_buf_queue->mutex); + if (stream != NULL_STREAM && stream->video_fifo->fifo_size == 0 && + stream->demux_plugin->get_status(stream->demux_plugin) != DEMUX_OK) + /* no further data can be expected here */ + return 0; + xine_usec_sleep (5000); + continue; + } } /* @@ -1126,9 +1118,10 @@ static void vo_open (xine_video_port_t *this_gen, xine_stream_t *stream) { this->discard_frames = 0; this->last_delivery_pts = 0; this->warn_threshold_event_sent = this->warn_threshold_exceeded = 0; - if (!this->overlay_enabled && stream->spu_channel_user > -2) + if (!this->overlay_enabled && (stream == NULL || stream->spu_channel_user > -2)) /* enable overlays if our new stream might want to show some */ this->overlay_enabled = 1; + if (stream == NULL) stream = NULL_STREAM; pthread_mutex_lock(&this->streams_lock); xine_list_append_content(this->streams, stream); pthread_mutex_unlock(&this->streams_lock); @@ -1146,6 +1139,7 @@ static void vo_close (xine_video_port_t *this_gen, xine_stream_t *stream) { this->video_opened = 0; /* unregister stream */ + if (stream == NULL) stream = NULL_STREAM; pthread_mutex_lock(&this->streams_lock); for (cur = xine_list_first_content(this->streams); cur; cur = xine_list_next_content(this->streams)) @@ -1374,7 +1368,7 @@ static void vo_enable_overlay (xine_video_port_t *this_gen, int overlay_enabled) pthread_mutex_lock(&this->streams_lock); for (stream = xine_list_first_content(this->streams) ; stream ; stream = xine_list_next_content(this->streams)) { - if (stream->spu_channel_user > -2) { + if (stream == NULL_STREAM || stream->spu_channel_user > -2) { pthread_mutex_unlock(&this->streams_lock); return; } diff --git a/src/xine-engine/video_out.h b/src/xine-engine/video_out.h index 4a3372113..3a7db490d 100644 --- a/src/xine-engine/video_out.h +++ b/src/xine-engine/video_out.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: video_out.h,v 1.104 2003/12/14 22:13:26 siggi Exp $ + * $Id: video_out.h,v 1.105 2004/01/07 19:52:43 mroi Exp $ * * * xine version of video_out.h @@ -190,6 +190,8 @@ struct xine_video_port_s { uint32_t (*get_capabilities) (xine_video_port_t *self); /* for constants see below */ /* open display driver for video output */ + /* when you are not a full-blown stream, but still need to open the port + * (e.g. you are a post plugin) it is legal to pass a NULL stream */ void (*open) (xine_video_port_t *self, xine_stream_t *stream); /* @@ -224,11 +226,13 @@ struct xine_video_port_s { int (*get_property) (xine_video_port_t *self, int property); int (*set_property) (xine_video_port_t *self, int property, int value); - /* return true if port is opened for this stream */ + /* return true if port is opened for this stream, stream can be NULL */ int (*status) (xine_video_port_t *self, xine_stream_t *stream, int *width, int *height, int64_t *img_duration); /* video driver is no longer used by decoder => close */ + /* when you are not a full-blown stream, but still need to close the port + * (e.g. you are a post plugin) it is legal to pass a NULL stream */ void (*close) (xine_video_port_t *self, xine_stream_t *stream); /* called on xine exit */ |