summaryrefslogtreecommitdiff
path: root/xine
diff options
context:
space:
mode:
Diffstat (limited to 'xine')
-rw-r--r--xine/osd_manager.c698
-rw-r--r--xine/osd_manager.h36
2 files changed, 734 insertions, 0 deletions
diff --git a/xine/osd_manager.c b/xine/osd_manager.c
new file mode 100644
index 00000000..fb419a9a
--- /dev/null
+++ b/xine/osd_manager.c
@@ -0,0 +1,698 @@
+/*
+ * osd_manager.c:
+ *
+ * See the main source file 'xineliboutput.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: osd_manager.c,v 1.1 2008-12-06 16:17:18 phintuka Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+
+#define XINE_ENGINE_INTERNAL
+#include <xine/xine_internal.h>
+#include <xine/video_out.h>
+
+#include "../xine_osd_command.h"
+#include "../xine_input_vdr.h"
+
+#include "../tools/rle.h"
+
+#include "vo_props.h"
+#include "osd_manager.h"
+
+/*#define LOGOSD(x...) LOGMSG(x)*/
+#define LOGOSD(x...)
+
+static const char log_module_input_osd[] = "[input_osd] ";
+#define LOG_MODULENAME log_module_input_osd
+#define SysLogLevel iSysLogLevel
+
+#include "../logdefs.h"
+
+
+typedef struct {
+ int handle; /* xine-lib overlay handle */
+ osd_command_t cmd; /* Full OSD data: last OSD_Set_RLE event */
+
+ uint16_t osd_width; /* output size this OSD was designed for */
+ uint16_t osd_height;
+
+ int64_t last_changed_vpts;
+} osd_data_t;
+
+typedef struct osd_manager_impl_s {
+ osd_manager_t mgr;
+
+ pthread_mutex_t lock;
+ uint8_t ticket_acquired;
+ xine_stream_t *stream;
+
+ uint16_t vdr_osd_width;
+ uint16_t vdr_osd_height;
+ uint16_t video_width;
+ uint16_t video_height;
+
+ osd_data_t osd[MAX_OSD_OBJECT];
+
+} osd_manager_impl_t;
+
+/************************************* Tools ************************************/
+
+/*
+ * acquire_ticket()
+ */
+static void acquire_ticket(osd_manager_impl_t *this)
+{
+ if (!this->ticket_acquired) {
+ this->stream->xine->port_ticket->acquire(this->stream->xine->port_ticket, 1);
+ this->ticket_acquired = 1;
+ }
+}
+
+static void release_ticket(osd_manager_impl_t *this)
+{
+ if (this->ticket_acquired) {
+ this->stream->xine->port_ticket->release(this->stream->xine->port_ticket, 1);
+ this->ticket_acquired = 0;
+ }
+}
+
+/*
+ * get_overlay_manager()
+ */
+video_overlay_manager_t *get_ovl_manager(osd_manager_impl_t *this)
+{
+ /* Get overlay manager. We need ticket ... */
+ acquire_ticket(this);
+ video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager(this->stream->video_out);
+ if (!ovl_manager) {
+ LOGMSG("Stream has no overlay manager !");
+ return NULL;
+ }
+ return ovl_manager;
+}
+
+/*
+ * palette_argb_to_ayuv()
+ */
+
+#define saturate(x,min,max) ( (x)<(min) ? (min) : (x)>(max) ? (max) : (x))
+
+static void palette_argb_to_ayuv(xine_clut_t *clut, int colors)
+{
+ if (clut && colors>0) {
+ int c;
+ for (c=0; c<colors; c++) {
+ int R = clut[c].r;
+ int G = clut[c].g;
+ int B = clut[c].b;
+ int Y = (( + 66*R + 129*G + 25*B + 0x80) >> 8) + 16;
+ int CR = (( + 112*R - 94*G - 18*B + 0x80) >> 8) + 128;
+ int CB = (( - 38*R - 74*G + 112*B + 0x80) >> 8) + 128;
+ clut[c].y = saturate( Y, 16, 235);
+ clut[c].cb = saturate(CB, 16, 240);
+ clut[c].cr = saturate(CR, 16, 240);
+ }
+ }
+}
+
+/*
+ * clear_osdcmd()
+ *
+ * - free allocated memory from osd_command_t
+ */
+static void clear_osdcmd(osd_command_t *cmd)
+{
+ free(cmd->data);
+ cmd->data = NULL;
+ free(cmd->palette);
+ cmd->palette = NULL;
+}
+
+/*
+ * osdcmd_to_overlay()
+ *
+ * - fill xine-lib vo_overlay_t from osd_command_t
+ */
+static void osdcmd_to_overlay(vo_overlay_t *ovl, osd_command_t *cmd)
+{
+ int i;
+
+ ovl->rle = (rle_elem_t*)cmd->data;
+ ovl->data_size = cmd->datalen;
+ ovl->num_rle = cmd->datalen / 4;
+
+ ovl->x = cmd->x;
+ ovl->y = cmd->y;
+ ovl->width = cmd->w;
+ ovl->height = cmd->h;
+
+ /* palette */
+ for (i=0; i<cmd->colors; i++) {
+ ovl->color[i] = (*(uint32_t*)(cmd->palette + i)) & 0x00ffffff;
+ ovl->trans[i] = (cmd->palette[i].alpha + 0x7)/0xf;
+ }
+ ovl->rgb_clut = cmd->flags & OSDFLAG_YUV_CLUT ? 0 : 1;
+
+ ovl->unscaled = cmd->flags & OSDFLAG_UNSCALED ? 1 : 0;
+
+ ovl->hili_top = ovl->hili_bottom = ovl->hili_left = ovl->hili_right = -1;
+}
+
+/*
+ * osdcmd_scale()
+ *
+ * Scale OSD_Set_RLE data
+ * - modified fields: x, y, w, h, (RLE) data and datalen
+ * - old RLE data is not stored, freed or returned !
+ */
+static void osdcmd_scale(osd_command_t *osdcmd, int new_w, int new_h /*, scale_mode_t mode*/)
+{
+ xine_rle_elem_t *old_rle = osdcmd->data;
+ int rle_elems = osdcmd->datalen / sizeof(xine_rle_elem_t);
+
+ /*if (mode == scale_fast)*/
+ osdcmd->data = rle_scale_nearest(old_rle, &rle_elems, osdcmd->w, osdcmd->h,
+ new_w, new_h);
+ osdcmd->datalen = rle_elems * sizeof(xine_rle_elem_t);
+
+ if (osdcmd->w != new_w) {
+ osdcmd->x = osdcmd->x * new_w / osdcmd->w;
+ osdcmd->w = new_w;
+ }
+ if (osdcmd->h != new_h) {
+ osdcmd->y = osdcmd->y * new_h / osdcmd->h;
+ osdcmd->h = new_h;
+ }
+}
+
+/*
+ * osd_exec_vpts()
+ *
+ * - calculate execution time (vpts) for OSD update
+ */
+static int64_t osd_exec_vpts(osd_manager_impl_t *this, osd_command_t *cmd)
+{
+ int64_t vpts = 0; /* now */
+
+ /* calculate exec time */
+ if (cmd->pts || cmd->delay_ms) {
+ int64_t now = xine_get_current_vpts(this->stream);
+
+ if (cmd->pts)
+ vpts = cmd->pts + this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET);
+ else
+ vpts = this->osd[cmd->wnd].last_changed_vpts + cmd->delay_ms*90;
+
+ /* execution time must be in future */
+ if (vpts < now)
+ vpts = 0;
+
+ /* limit delay to 5 seconds */
+ if (vpts > now + 5*90000)
+ vpts = vpts + 5*90000;
+
+ LOGOSD("OSD Command %d scheduled to +%dms", cmd->cmd, (int)(vpts>now ? vpts-now : 0)/90);
+ }
+
+ return vpts;
+}
+
+/***************************** OSD command handlers *****************************/
+
+/*
+ * exec_osd_size()
+ *
+ * - set the assumed full OSD size
+ */
+static int exec_osd_size(osd_manager_impl_t *this, osd_command_t *cmd)
+{
+ this->vdr_osd_width = cmd->w;
+ this->vdr_osd_height = cmd->h;
+
+ acquire_ticket(this);
+
+ xine_video_port_t *video_out = this->stream->video_out;
+ if (video_out->get_capabilities(video_out) & VO_CAP_OSDSCALING) {
+ video_out->set_property(video_out, VO_PROP_OSD_WIDTH, cmd->w);
+ video_out->set_property(video_out, VO_PROP_OSD_HEIGHT, cmd->h);
+ }
+
+ return CONTROL_OK;
+}
+
+/*
+ * exec_osd_nop()
+ *
+ * - update last changed time of an OSD window
+ */
+static int exec_osd_nop(osd_manager_impl_t *this, osd_command_t *cmd)
+{
+ this->osd[cmd->wnd].last_changed_vpts = xine_get_current_vpts(this->stream);
+ return CONTROL_OK;
+}
+
+/*
+ * exec_osd_flush()
+ *
+ * - commit all pending OSD events immediately
+ */
+static int exec_osd_flush(osd_manager_impl_t *this, osd_command_t *cmd)
+{
+ video_overlay_manager_t *ovl_manager = get_ovl_manager(this);
+ if (!ovl_manager)
+ return CONTROL_PARAM_ERROR;
+
+ ovl_manager->flush_events(ovl_manager);
+
+ return CONTROL_OK;
+}
+
+/*
+ * exec_osd_close()
+ *
+ */
+static int exec_osd_close(osd_manager_impl_t *this, osd_command_t *cmd)
+{
+ video_overlay_manager_t *ovl_manager = get_ovl_manager(this);
+ osd_data_t *osd = &this->osd[cmd->wnd];
+ int handle = osd->handle;
+
+ if (handle < 0) {
+ LOGMSG("OSD_Close(%d): non-existing OSD !", cmd->wnd);
+ return CONTROL_PARAM_ERROR;
+ }
+ if (!ovl_manager)
+ return CONTROL_PARAM_ERROR;
+
+ video_overlay_event_t ov_event = {0};
+ ov_event.event_type = OVERLAY_EVENT_FREE_HANDLE;
+ ov_event.vpts = osd_exec_vpts(this, cmd);
+ ov_event.object.handle = handle;
+
+ while (ovl_manager->add_event(ovl_manager, (void *)&ov_event) < 0) {
+ LOGMSG("OSD_Close(%d): overlay manager queue full !", cmd->wnd);
+ ovl_manager->flush_events(ovl_manager);
+ }
+
+ osd->handle = -1;
+ clear_osdcmd(&osd->cmd);
+ osd->last_changed_vpts = 0;
+
+ return CONTROL_OK;
+}
+
+/*
+ * exec_osd_set_rle()
+ *
+ */
+static int exec_osd_set_rle(osd_manager_impl_t *this, osd_command_t *cmd)
+{
+ video_overlay_manager_t *ovl_manager = get_ovl_manager(this);
+ video_overlay_event_t ov_event = {0};
+ vo_overlay_t ov_overlay = {0};
+ osd_data_t *osd = &this->osd[cmd->wnd];
+ int use_unscaled = 0;
+ int rle_scaled = 0;
+ int unscaled_supported = 1;
+ int vo_scaling = 0;
+ int handle = osd->handle;
+
+ if (!ovl_manager)
+ return CONTROL_PARAM_ERROR;
+
+ this->stream->video_out->enable_ovl(this->stream->video_out, 1);
+
+ /* get / allocate OSD handle */
+ if (handle < 0)
+ handle = osd->handle = ovl_manager->get_handle(ovl_manager,0);
+
+ /* fill SHOW event */
+ ov_event.event_type = OVERLAY_EVENT_SHOW;
+ ov_event.vpts = osd_exec_vpts(this, cmd);
+ ov_event.object.handle = handle;
+ ov_event.object.overlay = &ov_overlay;
+ ov_event.object.object_type = 1; /* menu */
+
+ /* check for unscaled OSD capability and request */
+ xine_video_port_t *video_out = this->stream->video_out;
+ if (! (video_out->get_capabilities(video_out) & VO_CAP_UNSCALED_OVERLAY))
+ unscaled_supported = 0;
+ else if (cmd->flags & OSDFLAG_UNSCALED)
+ use_unscaled = 1;
+
+ /* store osd for later rescaling (done if video size changes) */
+
+ clear_osdcmd(&osd->cmd);
+
+ memcpy(&osd->cmd, cmd, sizeof(osd_command_t));
+ osd->cmd.data = NULL;
+ if (cmd->palette) {
+ /* RGB -> YUV */
+ if(!(cmd->flags & OSDFLAG_YUV_CLUT))
+ palette_argb_to_ayuv(cmd->palette, cmd->colors);
+ cmd->flags &= ~OSDFLAG_YUV_CLUT;
+
+ osd->cmd.palette = malloc(sizeof(xine_clut_t)*cmd->colors);
+ memcpy(osd->cmd.palette, cmd->palette, 4*cmd->colors);
+ }
+
+ /* request OSD scaling from video_out layer */
+ if (video_out->get_capabilities(video_out) & VO_CAP_OSDSCALING) {
+ video_out->set_property(video_out, VO_PROP_OSD_SCALING, cmd->scaling ? 1 : 0);
+ vo_scaling = 1;
+ }
+
+ /* if video size differs from expected (VDR osd is designed for 720x576),
+ scale osd to video size or use unscaled (display resolution)
+ blending */
+
+ /* scale OSD ? */
+ if (!vo_scaling && !use_unscaled) {
+ int w_diff = (this->video_width < ((this->vdr_osd_width *242) >> 8) /* 95% */) ? -1 :
+ (this->video_width > ((this->vdr_osd_width *280) >> 8) /* 110% */) ? 1 : 0;
+ int h_diff = (this->video_height < ((this->vdr_osd_height*242) >> 8) /* 95% */) ? -1 :
+ (this->video_height > ((this->vdr_osd_height*280) >> 8) /* 110% */) ? 1 : 0;
+
+ if (w_diff || h_diff) {
+
+ /* unscaled OSD instead of downscaling ? */
+ if (w_diff < 0 || h_diff < 0)
+ if (cmd->flags & OSDFLAG_UNSCALED_LOWRES)
+ if (0 < (use_unscaled = unscaled_supported))
+ LOGOSD("Size out of margins, using unscaled overlay");
+
+ if (!use_unscaled && cmd->scaling > 0) {
+ int new_w = cmd->w * this->video_width / this->vdr_osd_width;
+ int new_h = cmd->h * this->video_height / this->vdr_osd_height;
+ LOGOSD("Size out of margins, rescaling rle image");
+
+ /* store original RLE data */
+ osd->cmd.data = cmd->data;
+ osd->cmd.datalen = cmd->datalen;
+
+ osdcmd_scale(cmd, new_w, new_h /*, this->class->fast_osd_scaling ? 0 : 1*/);
+ rle_scaled = 1;
+ }
+ }
+ }
+
+ /* Scale unscaled OSD ? */
+ if (!vo_scaling && use_unscaled && cmd->scaling > 0) {
+ int win_width = video_out->get_property(video_out, VO_PROP_WINDOW_WIDTH);
+ int win_height = video_out->get_property(video_out, VO_PROP_WINDOW_HEIGHT);
+
+ if (win_width >= 360 && win_height >= 288) {
+ LOGOSD("Scaling unscaled OSD to %dx%d", win_width, win_height);
+ if (win_width != this->vdr_osd_width || win_height != this->vdr_osd_height) {
+ int new_w = cmd->w * win_width / this->vdr_osd_width;
+ int new_h = cmd->h * win_height / this->vdr_osd_height;
+
+ /* store original RLE data */
+ osd->cmd.data = cmd->data;
+ osd->cmd.datalen = cmd->datalen;
+
+ osdcmd_scale(cmd, new_w, new_h /*, this->class->fast_osd_scaling ? 0 : 1*/);
+ rle_scaled = 1;
+ }
+ }
+ }
+
+ /* fill ov_overlay */
+ osdcmd_to_overlay(&ov_overlay, cmd);
+ ov_overlay.unscaled = use_unscaled;
+
+ /* tag this overlay */
+ ov_overlay.hili_rgb_clut = VDR_OSD_MAGIC;
+
+ /* if no scaling was required, we may still need to re-center OSD */
+ if (!vo_scaling && !rle_scaled) {
+ if (this->video_width != this->vdr_osd_width)
+ ov_overlay.x += (this->video_width - this->vdr_osd_width)/2;
+ if (this->video_height != this->vdr_osd_height)
+ ov_overlay.y += (this->video_height - this->vdr_osd_height)/2;
+ }
+
+ /* store rle for later scaling (done if video size changes) */
+ if (!rle_scaled /* if scaled, we already have a copy (original data) */ ) {
+ osd->cmd.data = malloc(cmd->datalen);
+ memcpy(osd->cmd.data, cmd->data, cmd->datalen);
+ }
+ cmd->data = NULL; /* we 'consume' data (ownership goes for xine-lib osd manager) */
+
+ /* send event to overlay manager */
+ while (ovl_manager->add_event(ovl_manager, (void *)&ov_event) < 0) {
+ LOGMSG("OSD_Set_RLE(%d): overlay manager queue full !", cmd->wnd);
+ ovl_manager->flush_events(ovl_manager);
+ continue;
+ }
+
+ osd->last_changed_vpts = ov_event.vpts ?: xine_get_current_vpts(this->stream);
+
+ return CONTROL_OK;
+}
+
+/*
+ * exec_osd_set_palette()
+ *
+ * - replace palette of an already existing OSD
+ */
+static int exec_osd_set_palette(osd_manager_impl_t *this, osd_command_t *cmd)
+{
+ osd_data_t *osd = &this->osd[cmd->wnd];
+
+ if (!osd->cmd.data) {
+ LOGMSG("OSD_SetPalette(%d): old RLE data missing !", cmd->wnd);
+ return CONTROL_PARAM_ERROR;
+ }
+ if (!cmd->palette) {
+ LOGMSG("OSD_SetPalette(%d): new palette missing !", cmd->wnd);
+ return CONTROL_PARAM_ERROR;
+ }
+
+ /* use cached event to re-create Set_RLE command with modified palette */
+ osd_command_t tmp;
+ /* steal the original command */
+ memcpy(&tmp, &osd->cmd, sizeof(osd_command_t));
+ memset(&osd->cmd, 0, sizeof(osd_command_t));
+
+ /* replace palette */
+ tmp.cmd = OSD_Set_RLE;
+ free(tmp.palette);
+ tmp.palette = malloc(cmd->colors*sizeof(xine_rle_elem_t));
+ memcpy(tmp.palette, cmd->palette, cmd->colors*sizeof(xine_rle_elem_t));
+ tmp.colors = cmd->colors;
+ tmp.pts = cmd->pts;
+ tmp.delay_ms = cmd->delay_ms;
+ tmp.flags |= cmd->flags & OSDFLAG_YUV_CLUT;
+
+ /* redraw */
+ int r = exec_osd_set_rle(this, &tmp);
+ clear_osdcmd(&tmp);
+ return r;
+}
+
+/*
+ * exec_osd_move()
+ *
+ * - move existing OSD to new position
+ */
+static int exec_osd_move(osd_manager_impl_t *this, osd_command_t *cmd)
+{
+ osd_data_t *osd = &this->osd[cmd->wnd];
+
+ if (!osd->cmd.data) {
+ LOGMSG("OSD_Move(%d): old RLE data missing !", cmd->wnd);
+ return CONTROL_PARAM_ERROR;
+ }
+ if (!osd->cmd.palette) {
+ LOGMSG("OSD_Move(%d): old palette missing !", cmd->wnd);
+ return CONTROL_PARAM_ERROR;
+ }
+
+ /* use cached event to re-create Set_RLE command with modified palette */
+ osd_command_t tmp;
+ /* steal the original command */
+ memcpy(&tmp, &osd->cmd, sizeof(osd_command_t));
+ memset(&osd->cmd, 0, sizeof(osd_command_t));
+
+ /* replace position */
+ tmp.cmd = OSD_Set_RLE;
+ tmp.x = cmd->x;
+ tmp.y = cmd->y;
+ tmp.pts = cmd->pts;
+ tmp.delay_ms = cmd->delay_ms;
+
+ /* redraw */
+ int r = exec_osd_set_rle(this, &tmp);
+ clear_osdcmd(&tmp);
+ return r;
+}
+
+/*
+ * exec_osd_command_internal()
+ */
+static int exec_osd_command_internal(osd_manager_impl_t *this, struct osd_command_s *cmd)
+{
+ LOGOSD("exec_osd_command %d", cmd->cmd);
+
+ switch (cmd->cmd) {
+ case OSD_Nop: return exec_osd_nop(this, cmd);
+ case OSD_Size: return exec_osd_size(this, cmd);
+ case OSD_SetPalette: return exec_osd_set_palette(this, cmd);
+ case OSD_Move: return exec_osd_move(this, cmd);
+ case OSD_Flush: return exec_osd_flush(this, cmd);
+ case OSD_Set_RLE: return exec_osd_set_rle(this, cmd);
+ case OSD_Close: return exec_osd_close(this, cmd);
+ case OSD_Set_YUV:
+ /* TODO */
+ LOGMSG("OSD_Set_YUV not implemented !");
+ return CONTROL_PARAM_ERROR;
+ }
+
+ LOGMSG("Unknown OSD command %d", cmd->cmd);
+ return CONTROL_PARAM_ERROR;
+}
+
+/****************************** Interface handlers ******************************/
+
+/*
+ * exec_osd_command()
+ *
+ * - handler for VDR-based osd_command_t events
+ */
+static int exec_osd_command(osd_manager_t *this_gen,
+ struct osd_command_s *cmd, xine_stream_t *stream)
+{
+ osd_manager_impl_t *this = (osd_manager_impl_t*)this_gen;
+ int result;
+
+ if (!cmd || !stream) {
+ LOGMSG("exec_osd_command: Stream not initialized !");
+ return CONTROL_DISCONNECTED;
+ }
+ if (cmd->wnd < 0 || cmd->wnd >= MAX_OSD_OBJECT) {
+ LOGMSG("exec_osd_command: OSD window handle %d out of range !", cmd->wnd);
+ return CONTROL_PARAM_ERROR;
+ }
+
+ if (pthread_mutex_lock(&this->lock)) {
+ LOGERR("exec_osd_command: mutex lock failed");
+ return CONTROL_DISCONNECTED;
+ }
+
+ this->stream = stream;
+ result = exec_osd_command_internal(this, cmd);
+
+ release_ticket(this);
+
+ pthread_mutex_unlock(&this->lock);
+
+ return result;
+}
+
+/*
+ * video_size_changed()
+ */
+static void video_size_changed(osd_manager_t *this_gen, xine_stream_t *stream, int width, int height)
+{
+ osd_manager_impl_t *this = (osd_manager_impl_t*)this_gen;
+ int i;
+
+ if (this->video_width == width && this->video_height == height)
+ return;
+ if (width < 1 || height < 1)
+ return;
+
+ LOGOSD("New video size (%dx%d->%dx%d)", this->video_width, this->video_height, width, height);
+
+ if (pthread_mutex_lock(&this->lock)) {
+ LOGERR("video_size_changed: mutex lock failed");
+ return;
+ }
+
+ this->stream = stream;
+ this->video_width = width;
+ this->video_height = height;
+
+ /* just call exec_osd_command for all stored osd's.
+ scaling is done automatically if required. */
+ for (i = 0; i < MAX_OSD_OBJECT; i++)
+ if (this->osd[i].handle >= 0 &&
+ this->osd[i].cmd.data &&
+ this->osd[i].cmd.scaling > 0) {
+ osd_command_t tmp;
+ memcpy(&tmp, &this->osd[i].cmd, sizeof(osd_command_t));
+ memset(&this->osd[i].cmd, 0, sizeof(osd_command_t));
+
+ acquire_ticket(this);
+ exec_osd_command(this_gen, &tmp, this->stream);
+
+ clear_osdcmd(&tmp);
+ }
+
+ release_ticket(this);
+ pthread_mutex_unlock(&this->lock);
+}
+
+/*
+ * osd_manager_dispose()
+ */
+static void osd_manager_dispose(osd_manager_t *this_gen, xine_stream_t *stream)
+{
+ osd_manager_impl_t *this = (osd_manager_impl_t*)this_gen;
+ int i;
+
+ while (pthread_mutex_destroy(&this->lock) == EBUSY) {
+ LOGMSG("osd_manager_dispose: lock busy ...");
+ pthread_mutex_lock(&this->lock);
+ pthread_mutex_unlock(&this->lock);
+ }
+
+ /* close all */
+ for (i=0; i<MAX_OSD_OBJECT; i++) {
+ if (this->osd[i].handle >= 0) {
+ osd_command_t cmd = {
+ .cmd = OSD_Close,
+ .wnd = i,
+ };
+ LOGOSD("Closing osd %d", i);
+ exec_osd_close(this, &cmd);
+ }
+ }
+
+ if (this->ticket_acquired) {
+ stream->xine->port_ticket->release(stream->xine->port_ticket, 1);
+ this->ticket_acquired = 0;
+ }
+
+ free(this);
+}
+
+/*
+ * init_osd_manager()
+ *
+ * - exported
+ */
+osd_manager_t *init_osd_manager(void)
+{
+ osd_manager_impl_t *this = calloc(1, sizeof(osd_manager_impl_t));
+ int i;
+
+ this->mgr.command = exec_osd_command;
+ this->mgr.dispose = osd_manager_dispose;
+ this->mgr.video_size_changed = video_size_changed;
+
+ pthread_mutex_init(&this->lock, NULL);
+
+ this->video_width = this->vdr_osd_width = 720;
+ this->video_height = this->vdr_osd_height = 576;
+
+ for (i = 0; i < MAX_OSD_OBJECT; i++)
+ this->osd[i].handle = -1;
+
+ return &this->mgr;
+}
diff --git a/xine/osd_manager.h b/xine/osd_manager.h
new file mode 100644
index 00000000..d71336ed
--- /dev/null
+++ b/xine/osd_manager.h
@@ -0,0 +1,36 @@
+/*
+ * osd_manager.h:
+ *
+ * See the main source file 'xineliboutput.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: osd_manager.h,v 1.1 2008-12-06 16:17:18 phintuka Exp $
+ *
+ */
+
+#ifndef XINELIBOUTPUT_OSD_MANAGER_H_
+#define XINELIBOUTPUT_OSD_MANAGER_H_
+
+/*
+ * OSD manager executes OSD control messages from VDR.
+ * - cache OSD data
+ * - scale OSD when required
+ * - re-scale OSD when video size changes
+ * - generate xine overlay events
+ */
+
+struct osd_command_s;
+
+typedef struct osd_manager_s osd_manager_t;
+
+struct osd_manager_s {
+ int (*command)(osd_manager_t *, struct osd_command_s *, xine_stream_t *);
+ void (*dispose)(osd_manager_t *, xine_stream_t *);
+
+ void (*video_size_changed)(osd_manager_t *, xine_stream_t *, int width, int height);
+};
+
+osd_manager_t *init_osd_manager(void);
+
+
+#endif /* XINELIBOUTPUT_OSD_MANAGER_H_ */