diff options
author | Miguel Freitas <miguelfreitas@users.sourceforge.net> | 2001-11-28 22:19:10 +0000 |
---|---|---|
committer | Miguel Freitas <miguelfreitas@users.sourceforge.net> | 2001-11-28 22:19:10 +0000 |
commit | d48b3bf8769a8ac1741d819289ed9ea117764bc5 (patch) | |
tree | b90a868f2118128d8b0bcff135f9b0ac3d46ae73 /src/xine-engine/video_overlay.c | |
parent | 157c020ba6d577c45678b7b59f96b3ca646fa525 (diff) | |
download | xine-lib-d48b3bf8769a8ac1741d819289ed9ea117764bc5.tar.gz xine-lib-d48b3bf8769a8ac1741d819289ed9ea117764bc5.tar.bz2 |
* OSD (On Screen Display) for rendering text and graphics into overlays
* reworked spu and overlay manager (multiple overlays supported)
CVS patchset: 1126
CVS date: 2001/11/28 22:19:10
Diffstat (limited to 'src/xine-engine/video_overlay.c')
-rw-r--r-- | src/xine-engine/video_overlay.c | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/src/xine-engine/video_overlay.c b/src/xine-engine/video_overlay.c new file mode 100644 index 000000000..099f0416b --- /dev/null +++ b/src/xine-engine/video_overlay.c @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2000-2001 the xine project + * + * This file is part of xine, a unix video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: video_overlay.c,v 1.1 2001/11/28 22:19:11 miguelfreitas Exp $ + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "buffer.h" +#include "events.h" +#include "xine_internal.h" +#include "video_out/alphablend.h" +#include "xine-engine/bswap.h" +#include "xineutils.h" +#include "video_overlay.h" + +/* +#define LOG_DEBUG 1 +*/ + + +typedef struct video_overlay_events_s { + video_overlay_event_t *event; + uint32_t next_event; +} video_overlay_events_t; + +typedef struct video_overlay_showing_s { + int32_t handle; /* -1 means not allocated */ +} video_overlay_showing_t; + + +typedef struct video_overlay_s { + video_overlay_instance_t video_overlay; + + pthread_mutex_t video_overlay_events_mutex; + video_overlay_events_t video_overlay_events[MAX_EVENTS]; + pthread_mutex_t video_overlay_objects_mutex; + video_overlay_object_t video_overlay_objects[MAX_OBJECTS]; + pthread_mutex_t video_overlay_showing_mutex; + video_overlay_showing_t video_overlay_showing[MAX_SHOWING]; + +} video_overlay_t; + + +static void add_showing_handle( video_overlay_t *this, int32_t handle ) +{ + int i; + + pthread_mutex_lock( &this->video_overlay_showing_mutex ); + + for( i = 0; i < MAX_SHOWING; i++ ) + if( this->video_overlay_showing[i].handle == handle ) + break; /* already showing */ + + if( i == MAX_SHOWING ) { + for( i = 0; i < MAX_SHOWING && this->video_overlay_showing[i].handle >= 0; i++ ) + ; + + if( i != MAX_SHOWING ) + this->video_overlay_showing[i].handle = handle; + else + fprintf(stderr,"video_overlay: error: no showing slots available\n"); + } + + pthread_mutex_unlock( &this->video_overlay_showing_mutex ); +} + +static void remove_showing_handle( video_overlay_t *this, int32_t handle ) +{ + int i; + + pthread_mutex_lock( &this->video_overlay_showing_mutex ); + + for( i = 0; i < MAX_SHOWING; i++ ) { + if( this->video_overlay_showing[i].handle == handle ) { + this->video_overlay_showing[i].handle = -1; + } + } + + pthread_mutex_unlock( &this->video_overlay_showing_mutex ); +} + +static void remove_events_handle( video_overlay_t *this, int32_t handle ) +{ + uint32_t last_event,this_event; + + pthread_mutex_lock( &this->video_overlay_events_mutex ); + + this_event=0; + do { + last_event=this_event; + this_event=this->video_overlay_events[last_event].next_event; + + while( this_event && + this->video_overlay_events[this_event].event->object.handle == handle ) { + /* remove event from pts list */ + this->video_overlay_events[last_event].next_event= + this->video_overlay_events[this_event].next_event; + + /* free its overlay */ + if( this->video_overlay_events[this_event].event->object.overlay ) { + if( this->video_overlay_events[this_event].event->object.overlay->rle ) + free( this->video_overlay_events[this_event].event->object.overlay->rle ); + free(this->video_overlay_events[this_event].event->object.overlay); + this->video_overlay_events[this_event].event->object.overlay = NULL; + } + + /* mark as free */ + this->video_overlay_events[this_event].next_event = 0; + this->video_overlay_events[this_event].event->event_type = EVENT_NULL; + + this_event=this->video_overlay_events[last_event].next_event; + } + } while ( this_event ); + + pthread_mutex_unlock( &this->video_overlay_events_mutex ); +} + + +/* + allocate a handle from the object pool (exported function) + */ +static int32_t video_overlay_get_handle(video_overlay_instance_t *this_gen, int object_type ) { + video_overlay_t *this = (video_overlay_t *) this_gen; + int n; + + pthread_mutex_lock( &this->video_overlay_objects_mutex ); + + for( n=0; n < MAX_OBJECTS && this->video_overlay_objects[n].handle > -1; n++ ) + ; + + if (n == MAX_OBJECTS) { + n = -1; + } else { + this->video_overlay_objects[n].handle = n; + this->video_overlay_objects[n].object_type = object_type; + } + + pthread_mutex_unlock( &this->video_overlay_objects_mutex ); + return n; +} + +/* + free a handle from the object pool (internal function) + */ +static void internal_video_overlay_free_handle(video_overlay_t *this, int32_t handle) { + + pthread_mutex_lock( &this->video_overlay_objects_mutex ); + + if( this->video_overlay_objects[handle].overlay ) { + if( this->video_overlay_objects[handle].overlay->rle ) + free( this->video_overlay_objects[handle].overlay->rle ); + free( this->video_overlay_objects[handle].overlay ); + this->video_overlay_objects[handle].overlay = NULL; + } + this->video_overlay_objects[handle].handle = -1; + + pthread_mutex_unlock( &this->video_overlay_objects_mutex ); +} + +/* + exported free handle function. must take care of removing the object + from showing and events lists. +*/ +static void video_overlay_free_handle(video_overlay_instance_t *this_gen, int32_t handle) { + video_overlay_t *this = (video_overlay_t *) this_gen; + + remove_showing_handle(this,handle); + remove_events_handle(this,handle); + internal_video_overlay_free_handle(this,handle); +} + + + +static void video_overlay_reset (video_overlay_t *this) { + int i; + + pthread_mutex_lock (&this->video_overlay_events_mutex); + for (i=0; i < MAX_EVENTS; i++) { + if (this->video_overlay_events[i].event == NULL) { + this->video_overlay_events[i].event = xine_xmalloc (sizeof(video_overlay_event_t)); +#ifdef LOG_DEBUG + printf ("video_overlay: MALLOC2: this->video_overlay_events[%d].event %p, len=%d\n", + i, + this->video_overlay_events[i].event, + sizeof(video_overlay_event_t)); +#endif + } + this->video_overlay_events[i].event->event_type = 0; /* Empty slot */ + this->video_overlay_events[i].next_event = 0; + } + pthread_mutex_unlock (&this->video_overlay_events_mutex); + + for (i=0; i < MAX_OBJECTS; i++) { + internal_video_overlay_free_handle(this, i); + } + + for( i = 0; i < MAX_SHOWING; i++ ) + this->video_overlay_showing[i].handle = -1; +} + + +static void video_overlay_init (video_overlay_instance_t *this_gen) { + + video_overlay_t *this = (video_overlay_t *) this_gen; + + pthread_mutex_init (&this->video_overlay_events_mutex,NULL); + pthread_mutex_init (&this->video_overlay_objects_mutex,NULL); + pthread_mutex_init (&this->video_overlay_showing_mutex,NULL); + + video_overlay_reset(this); +} + + +/* add an event to the events queue, sort the queue based on vpts. + * This can be the API entry point for DVD subtitles. + * One calls this function with an event, the event contains an overlay + * and a vpts when to action/process it. vpts of 0 means action the event now. + * One also has a handle, so one can match show and hide events. + * + * note: on success event->object.overlay is "taken" (caller will not have access + * to overlay data including rle). + * note2: handle will be freed on HIDE events + */ +static int32_t video_overlay_add_event(video_overlay_instance_t *this_gen, void *event_gen ) { + video_overlay_event_t *event = (video_overlay_event_t *) event_gen; + video_overlay_t *this = (video_overlay_t *) this_gen; + uint32_t last_event,this_event,new_event; + + pthread_mutex_lock (&this->video_overlay_events_mutex); + + /* We skip the 0 entry because that is used as a pointer to the first event.*/ + /* Find a free event slot */ + for( new_event = 1; new_event<MAX_EVENTS && + this->video_overlay_events[new_event].event->event_type > 0; new_event++ ) + ; + + if (new_event < MAX_EVENTS) { + /* Find position in event queue to be added. */ + this_event=0; + /* Find where in the current queue to insert the event. I.E. Sort it. */ + do { + last_event=this_event; + this_event=this->video_overlay_events[last_event].next_event; + } while ( this_event && this->video_overlay_events[this_event].event->vpts < event->vpts ); + + this->video_overlay_events[last_event].next_event=new_event; + this->video_overlay_events[new_event].next_event=this_event; + + /* memcpy everything except the actual image */ + if ( this->video_overlay_events[new_event].event == NULL ) { + fprintf(stderr,"video_overlay: error: event slot is NULL!\n"); + } + this->video_overlay_events[new_event].event->event_type=event->event_type; + this->video_overlay_events[new_event].event->vpts=event->vpts; + this->video_overlay_events[new_event].event->object.handle=event->object.handle; + + if ( this->video_overlay_events[new_event].event->object.overlay ) { + fprintf(stderr,"video_overlay: error: event->object.overlay was not freed!\n"); + } + + this->video_overlay_events[new_event].event->object.overlay = xine_xmalloc (sizeof(vo_overlay_t)); + memcpy(this->video_overlay_events[new_event].event->object.overlay, + event->object.overlay, sizeof(vo_overlay_t)); + + /* We took the callers rle and data, therefore it will be our job to free it */ + /* clear callers overlay so it will not be freed twice */ + memset(event->object.overlay,0,sizeof(vo_overlay_t)); + } else { + fprintf(stderr, "No spare subtitle event slots\n"); + new_event = -1; + } + + pthread_mutex_unlock (&this->video_overlay_events_mutex); + + return new_event; +} + + +/* not currently used. James might need this for debugging menu stuff */ +static void video_overlay_print_overlay( vo_overlay_t *ovl ) { +#ifdef LOG_DEBUG + printf ("video_overlay: OVERLAY to show\n"); + printf ("video_overlay: \tx = %d y = %d width = %d height = %d\n", + ovl->x, ovl->y, ovl->width, ovl->height ); + printf ("video_overlay: \tclut [%x %x %x %x]\n", + ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]); + printf ("video_overlay: \ttrans [%d %d %d %d]\n", + ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]); + printf ("video_overlay: \tclip top=%d bottom=%d left=%d right=%d\n", + ovl->clip_top, ovl->clip_bottom, ovl->clip_left, ovl->clip_right); +#endif + return; +} + +/* + process overlay events + if vpts == 0 will process everything now (used in flush) +*/ +static void video_overlay_event( video_overlay_t *this, int vpts ) { + int32_t handle; + uint32_t this_event; + + pthread_mutex_lock (&this->video_overlay_events_mutex); + + this_event=this->video_overlay_events[0].next_event; + while ( this_event && (vpts > this->video_overlay_events[this_event].event->vpts || + vpts == 0) ) { + handle=this->video_overlay_events[this_event].event->object.handle; + switch( this->video_overlay_events[this_event].event->event_type ) { + case EVENT_SHOW_SPU: +#ifdef LOG_DEBUG + printf ("video_overlay: SHOW SPU NOW\n"); +#endif + if (this->video_overlay_events[this_event].event->object.overlay != NULL) { + internal_video_overlay_free_handle( this, handle ); + + this->video_overlay_objects[handle].handle = handle; + if( this->video_overlay_objects[handle].overlay ) { + fprintf(stderr,"video_overlay: error: object->overlay was not freed!\n"); + } + this->video_overlay_objects[handle].overlay = + this->video_overlay_events[this_event].event->object.overlay; + this->video_overlay_events[this_event].event->object.overlay = NULL; + + add_showing_handle( this, handle ); + } + break; + + /* implementation for HIDE_SPU and HIDE_MENU is the same. + i will keep them separated in case we need something special... + */ + case EVENT_HIDE_SPU: +#ifdef LOG_DEBUG + printf ("video_overlay: HIDE SPU NOW\n"); +#endif + free(this->video_overlay_events[this_event].event->object.overlay); + this->video_overlay_events[this_event].event->object.overlay = NULL; + remove_showing_handle( this, handle ); + internal_video_overlay_free_handle( this, handle ); + break; + + case EVENT_HIDE_MENU: +#ifdef LOG_DEBUG + printf ("video_overlay: HIDE MENU NOW %d\n",handle); +#endif + free(this->video_overlay_events[this_event].event->object.overlay); + this->video_overlay_events[this_event].event->object.overlay = NULL; + remove_showing_handle( this, handle ); + internal_video_overlay_free_handle( this, handle ); + break; + + case EVENT_MENU_SPU: + /* mixes palette and copy rle */ +#ifdef LOG_DEBUG + printf ("MENU SPU NOW\n"); +#endif + if (this->video_overlay_events[this_event].event->object.overlay != NULL) { + vo_overlay_t *event_overlay = this->video_overlay_events[this_event].event->object.overlay; + vo_overlay_t *overlay; + + /* we need to allocate overlay on first EVENT_MENU_SPU */ + if( !this->video_overlay_objects[handle].overlay ) { + this->video_overlay_objects[handle].overlay + = xine_xmalloc( sizeof(vo_overlay_t) ); + } + overlay = this->video_overlay_objects[handle].overlay; + + this->video_overlay_objects[handle].handle = handle; + + /* If rle is not empty, free it first */ + if(overlay->rle) { + free (overlay->rle); + } + overlay->rle = event_overlay->rle; + + overlay->data_size = event_overlay->data_size; + overlay->num_rle = event_overlay->num_rle; + overlay->x = event_overlay->x; + overlay->y = event_overlay->y; + overlay->width = event_overlay->width; + overlay->height = event_overlay->height; + overlay->rgb_clut = event_overlay->rgb_clut; + if((event_overlay->color[0] + + event_overlay->color[1] + + event_overlay->color[2] + + event_overlay->color[3]) > 0 ) { + overlay->color[0] = event_overlay->color[0]; + overlay->color[1] = event_overlay->color[1]; + overlay->color[2] = event_overlay->color[2]; + overlay->color[3] = event_overlay->color[3]; + } + if((event_overlay->trans[0] + + event_overlay->trans[1] + + event_overlay->trans[2] + + event_overlay->trans[3]) > 0 ) { + overlay->trans[0] = event_overlay->trans[0]; + overlay->trans[1] = event_overlay->trans[1]; + overlay->trans[2] = event_overlay->trans[2]; + overlay->trans[3] = event_overlay->trans[3]; + } + add_showing_handle( this, handle ); + + /* The null test was done at the start of this case statement */ + free (this->video_overlay_events[this_event].event->object.overlay); + this->video_overlay_events[this_event].event->object.overlay = NULL; + } + break; + + case EVENT_MENU_BUTTON: + /* mixes palette and copy clip coords */ +#ifdef LOG_DEBUG + printf ("MENU BUTTON NOW\n"); +#endif + if (this->video_overlay_events[this_event].event->object.overlay != NULL) { + vo_overlay_t *overlay = this->video_overlay_objects[handle].overlay; + vo_overlay_t *event_overlay = this->video_overlay_events[this_event].event->object.overlay; + + if( !this->video_overlay_objects[handle].overlay ) { + fprintf(stderr,"video_overlay: error: button event received and no overlay allocated.\n"); + } + + this->video_overlay_objects[handle].handle = handle; + overlay->clip_top = event_overlay->clip_top; + overlay->clip_bottom = event_overlay->clip_bottom; + overlay->clip_left = event_overlay->clip_left; + overlay->clip_right = event_overlay->clip_right; + + if((event_overlay->color[0] + + event_overlay->color[1] + + event_overlay->color[2] + + event_overlay->color[3]) > 0 ) { + overlay->color[0] = event_overlay->color[0]; + overlay->color[1] = event_overlay->color[1]; + overlay->color[2] = event_overlay->color[2]; + overlay->color[3] = event_overlay->color[3]; + } + if((event_overlay->trans[0] + + event_overlay->trans[1] + + event_overlay->trans[2] + + event_overlay->trans[3]) > 0 ) { + overlay->trans[0] = event_overlay->trans[0]; + overlay->trans[1] = event_overlay->trans[1]; + overlay->trans[2] = event_overlay->trans[2]; + overlay->trans[3] = event_overlay->trans[3]; + } + add_showing_handle( this, handle ); + + if( this->video_overlay_events[this_event].event->object.overlay->rle ) { + printf ("video_overlay: warning EVENT_MENU_BUTTON with rle data\n"); + free( this->video_overlay_events[this_event].event->object.overlay->rle ); + } + + /* The null test was done at the start of this case statement */ + free (this->video_overlay_events[this_event].event->object.overlay); + this->video_overlay_events[this_event].event->object.overlay = NULL; + } + break; + + default: + printf ("video_overlay: unhandled event type\n"); + break; + } + + this->video_overlay_events[0].next_event = this->video_overlay_events[this_event].next_event; + this->video_overlay_events[this_event].next_event = 0; + this->video_overlay_events[this_event].event->event_type = 0; + + this_event=this->video_overlay_events[0].next_event; + } + + pthread_mutex_unlock (&this->video_overlay_events_mutex); +} + +/* This is called from video_out.c + * must call output->overlay_blend for each active overlay. + */ +static void video_overlay_multiple_overlay_blend(video_overlay_instance_t *this_gen, int vpts, + vo_driver_t *output, vo_frame_t *vo_img, int enabled) { + video_overlay_t *this = (video_overlay_t *) this_gen; + int i; + int32_t handle; + + /* Look at next events, if current video vpts > first event on queue, process the event + * else just continue + */ + video_overlay_event( this, vpts ); + + /* Scan through 5 entries and display any present. + */ + pthread_mutex_lock( &this->video_overlay_showing_mutex ); + for( i = 0; enabled && output->overlay_blend && i < MAX_SHOWING; i++ ) { + handle=this->video_overlay_showing[i].handle; + if (handle >= 0 ) { + output->overlay_blend(output, vo_img, this->video_overlay_objects[handle].overlay); + } + } + pthread_mutex_unlock( &this->video_overlay_showing_mutex ); +} + + +/* this should be called on stream end or stop to make sure every + hide event is processed. +*/ +static void video_overlay_flush_events(video_overlay_instance_t *this_gen ) +{ + video_overlay_t *this = (video_overlay_t *) this_gen; + + video_overlay_event( this, 0 ); +} + + +video_overlay_instance_t *video_overlay_new_instance () { + + video_overlay_t *this; + + this = (video_overlay_t *) xine_xmalloc (sizeof (video_overlay_t)); + + this->video_overlay.init = video_overlay_init; + this->video_overlay.get_handle = video_overlay_get_handle; + this->video_overlay.free_handle = video_overlay_free_handle; + this->video_overlay.add_event = video_overlay_add_event; + this->video_overlay.flush_events = video_overlay_flush_events; + this->video_overlay.multiple_overlay_blend = video_overlay_multiple_overlay_blend; + + return (video_overlay_instance_t *) &this->video_overlay; +} + |