summaryrefslogtreecommitdiff
path: root/src/xine-engine
diff options
context:
space:
mode:
authorMichael Roitzsch <mroi@users.sourceforge.net>2004-01-07 19:52:42 +0000
committerMichael Roitzsch <mroi@users.sourceforge.net>2004-01-07 19:52:42 +0000
commit5e25dd8a73f15e8116da5ce8fda14f8f671473ce (patch)
treed47d0a7c256fe25905f4fcd66302dbee68c5faa9 /src/xine-engine
parent6f75f546fd6e928245911e8ebcea680a7c9046b6 (diff)
downloadxine-lib-5e25dd8a73f15e8116da5ce8fda14f8f671473ce.tar.gz
xine-lib-5e25dd8a73f15e8116da5ce8fda14f8f671473ce.tar.bz2
the new, refined post plugin architecture
* post plugins are now much safer (fewer races/inconsistencies) and easier to write * all post plugins are ported to the new architecture (and should work) * ports can now be opened and closed with a NULL stream CVS patchset: 6007 CVS date: 2004/01/07 19:52:42
Diffstat (limited to 'src/xine-engine')
-rw-r--r--src/xine-engine/audio_out.c77
-rw-r--r--src/xine-engine/audio_out.h6
-rw-r--r--src/xine-engine/post.c865
-rw-r--r--src/xine-engine/post.h252
-rw-r--r--src/xine-engine/video_out.c66
-rw-r--r--src/xine-engine/video_out.h8
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 */