* wall.c : Wall video plugin for vlc
*****************************************************************************
* Copyright (C) 2000, 2001 VideoLAN
- * $Id: wall.c,v 1.1 2001/12/17 03:38:21 sam Exp $
+ * $Id: wall.c,v 1.6 2002/01/04 14:01:34 sam Exp $
*
* Authors: Samuel Hocevar <sam@zoy.org>
*
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
-#define MODULE_NAME filter_wall
-#include "modules_inner.h"
-
/*****************************************************************************
* Preamble
*****************************************************************************/
-#include "defs.h"
-
#include <errno.h>
#include <stdlib.h> /* malloc(), free() */
#include <string.h>
-#include "common.h" /* boolean_t, byte_t */
-#include "intf_msg.h"
-#include "threads.h"
-#include "mtime.h"
-#include "tests.h"
+#include <videolan/vlc.h>
#include "video.h"
#include "video_output.h"
-#include "modules.h"
-#include "modules_export.h"
-
-#define WALL_MAX_DIRECTBUFFERS 8
+#include "filter_common.h"
/*****************************************************************************
* Capabilities defined in the other files.
* Build configuration tree.
*****************************************************************************/
MODULE_CONFIG_START
-ADD_WINDOW( "Configuration for Wall module" )
- ADD_COMMENT( "Ha, ha -- nothing to configure yet" )
MODULE_CONFIG_STOP
MODULE_INIT_START
- p_module->i_capabilities = MODULE_CAPABILITY_NULL
- | MODULE_CAPABILITY_VOUT;
- p_module->psz_longname = "image wall video module";
+ SET_DESCRIPTION( "image wall video module" )
+ /* Capability score set to 0 because we don't want to be spawned
+ * as a video output unless explicitly requested to */
+ ADD_CAPABILITY( VOUT, 0 )
+ ADD_SHORTCUT( "wall" )
MODULE_INIT_STOP
MODULE_ACTIVATE_START
*****************************************************************************/
typedef struct vout_sys_s
{
- struct vout_thread_s *p_vout_top;
- struct vout_thread_s *p_vout_bottom;
+ int i_col;
+ int i_row;
+ int i_vout;
+ struct vout_list_s
+ {
+ int i_width;
+ int i_height;
+ struct vout_thread_s *p_vout;
+ } *pp_vout;
} vout_sys_t;
* Local prototypes
*****************************************************************************/
static int vout_Probe ( probedata_t *p_data );
-static int vout_Create ( struct vout_thread_s * );
-static int vout_Init ( struct vout_thread_s * );
-static void vout_End ( struct vout_thread_s * );
-static void vout_Destroy ( struct vout_thread_s * );
-static int vout_Manage ( struct vout_thread_s * );
-static void vout_Display ( struct vout_thread_s *, struct picture_s * );
+static int vout_Create ( vout_thread_t * );
+static int vout_Init ( vout_thread_t * );
+static void vout_End ( vout_thread_t * );
+static void vout_Destroy ( vout_thread_t * );
+static int vout_Manage ( vout_thread_t * );
+static void vout_Render ( vout_thread_t *, struct picture_s * );
+static void vout_Display ( vout_thread_t *, struct picture_s * );
-static int WallNewPicture ( struct vout_thread_s *, struct picture_s * );
+static void RemoveAllVout ( vout_thread_t *p_vout );
/*****************************************************************************
* Functions exported as capabilities. They are declared as static so that
p_function_list->functions.vout.pf_end = vout_End;
p_function_list->functions.vout.pf_destroy = vout_Destroy;
p_function_list->functions.vout.pf_manage = vout_Manage;
+ p_function_list->functions.vout.pf_render = vout_Render;
p_function_list->functions.vout.pf_display = vout_Display;
p_function_list->functions.vout.pf_setpalette = NULL;
}
*****************************************************************************/
static int vout_Probe( probedata_t *p_data )
{
- if( TestMethod( VOUT_FILTER_VAR, "wall" ) )
- {
- return( 999 );
- }
-
- /* If we weren't asked to filter, don't filter. */
return( 0 );
}
return( 1 );
}
+ p_vout->p_sys->i_col = 3;
+ p_vout->p_sys->i_row = 3;
+
+ p_vout->p_sys->pp_vout = malloc( p_vout->p_sys->i_row *
+ p_vout->p_sys->i_col *
+ sizeof(struct vout_list_s) );
+ if( p_vout->p_sys->pp_vout == NULL )
+ {
+ intf_ErrMsg("error: %s", strerror(ENOMEM) );
+ free( p_vout->p_sys );
+ return( 1 );
+ }
+
+
return( 0 );
}
*****************************************************************************/
static int vout_Init( vout_thread_t *p_vout )
{
- int i_index;
+ int i_index, i_row, i_col, i_width, i_height;
char *psz_filter;
picture_t *p_pic;
I_OUTPUTPICTURES = 0;
+ /* Initialize the output structure */
+ p_vout->output.i_chroma = p_vout->render.i_chroma;
+ p_vout->output.i_width = p_vout->render.i_width;
+ p_vout->output.i_height = p_vout->render.i_height;
+ p_vout->output.i_aspect = p_vout->render.i_aspect;
+
/* Try to open the real video output */
- psz_filter = main_GetPszVariable( VOUT_FILTER_VAR, "" );
+ psz_filter = main_GetPszVariable( VOUT_FILTER_VAR, NULL );
main_PutPszVariable( VOUT_FILTER_VAR, "" );
intf_WarnMsg( 1, "filter: spawning the real video outputs" );
- p_vout->p_sys->p_vout_top =
- vout_CreateThread( NULL,
- p_vout->render.i_width, p_vout->render.i_height / 2,
- p_vout->render.i_chroma, p_vout->render.i_aspect * 2);
-
- /* Everything failed */
- if( p_vout->p_sys->p_vout_top == NULL )
- {
- intf_ErrMsg( "filter error: can't open top vout, aborting" );
-
- return( 0 );
- }
-
- p_vout->p_sys->p_vout_bottom =
- vout_CreateThread( NULL,
- p_vout->render.i_width, p_vout->render.i_height / 2,
- p_vout->render.i_chroma, p_vout->render.i_aspect * 2 );
+ p_vout->p_sys->i_vout = 0;
- /* Everything failed */
- if( p_vout->p_sys->p_vout_bottom == NULL )
+ /* FIXME: use bresenham instead of those ugly divisions */
+ for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
{
- intf_ErrMsg( "filter error: can't open bottom vout, aborting" );
- vout_DestroyThread( p_vout->p_sys->p_vout_top, NULL );
-
- return( 0 );
- }
-
- main_PutPszVariable( VOUT_FILTER_VAR, psz_filter );
-
- /* Initialize the output structure */
- switch( p_vout->render.i_chroma )
- {
- case YUV_420_PICTURE:
- p_vout->output.i_chroma = p_vout->render.i_chroma;
- p_vout->output.i_width = p_vout->render.i_width;
- p_vout->output.i_height = p_vout->render.i_height;
- p_vout->output.i_aspect = p_vout->render.i_aspect;
- break;
-
- default:
- p_vout->output.i_chroma = EMPTY_PICTURE; /* unknown chroma */
- break;
- }
-
- /* Try to initialize WALL_MAX_DIRECTBUFFERS direct buffers */
- while( I_OUTPUTPICTURES < WALL_MAX_DIRECTBUFFERS )
- {
- p_pic = NULL;
-
- /* Find an empty picture slot */
- for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
+ for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
{
- if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
+ if( i_col + 1 < p_vout->p_sys->i_col )
{
- p_pic = p_vout->p_picture + i_index;
- break;
+ i_width = ( p_vout->render.i_width
+ / p_vout->p_sys->i_col ) & ~0x1;
+ }
+ else
+ {
+ i_width = p_vout->render.i_width
+ - ( ( p_vout->render.i_width
+ / p_vout->p_sys->i_col ) & ~0x1 ) * i_col;
}
- }
- /* Allocate the picture */
- if( WallNewPicture( p_vout, p_pic ) )
- {
- break;
- }
+ if( i_row + 1 < p_vout->p_sys->i_row )
+ {
+ i_height = ( p_vout->render.i_height
+ / p_vout->p_sys->i_row ) & ~0x3;
+ }
+ else
+ {
+ i_height = p_vout->render.i_height
+ - ( ( p_vout->render.i_height
+ / p_vout->p_sys->i_row ) & ~0x3 ) * i_row;
+ }
- p_pic->i_status = DESTROYED_PICTURE;
- p_pic->i_type = DIRECT_PICTURE;
+ p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout =
+ vout_CreateThread( NULL, i_width, i_height,
+ p_vout->render.i_chroma,
+ p_vout->render.i_aspect
+ * p_vout->render.i_height / i_height
+ * i_width / p_vout->render.i_width );
+ if( p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout == NULL )
+ {
+ intf_ErrMsg( "vout error: failed to get %ix%i vout threads",
+ p_vout->p_sys->i_col, p_vout->p_sys->i_row );
+ RemoveAllVout( p_vout );
+ return 0;
+ }
- p_pic->i_left_margin =
- p_pic->i_right_margin =
- p_pic->i_top_margin =
- p_pic->i_bottom_margin = 0;
+ p_vout->p_sys->i_vout++;
+ }
+ }
- PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
+ main_PutPszVariable( VOUT_FILTER_VAR, psz_filter );
- I_OUTPUTPICTURES++;
- }
+ ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
return( 0 );
}
for( i_index = I_OUTPUTPICTURES ; i_index ; )
{
i_index--;
- free( PP_OUTPUTPICTURE[ i_index ]->planes[ 0 ].p_data );
+ free( PP_OUTPUTPICTURE[ i_index ]->p_data );
}
}
*****************************************************************************/
static void vout_Destroy( vout_thread_t *p_vout )
{
- vout_DestroyThread( p_vout->p_sys->p_vout_top, NULL );
- vout_DestroyThread( p_vout->p_sys->p_vout_bottom, NULL );
+ RemoveAllVout( p_vout );
+ free( p_vout->p_sys->pp_vout );
free( p_vout->p_sys );
}
}
/*****************************************************************************
- * vout_Display: displays previously rendered output
+ * vout_Render: displays previously rendered output
*****************************************************************************
* This function send the currently rendered image to Wall image, waits
* until it is displayed and switch the two rendering buffers, preparing next
* frame.
*****************************************************************************/
-static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
+static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
{
- picture_t *p_outpic_top, *p_outpic_bottom;
- int i_index;
- mtime_t i_date = mdate() + 50000;
+ picture_t *p_outpic = NULL;
+ int i_col, i_row, i_vout, i_plane;
+ int pi_left_skip[VOUT_MAX_PLANES], pi_top_skip[VOUT_MAX_PLANES];
- while( ( p_outpic_top
- = vout_CreatePicture( p_vout->p_sys->p_vout_top, 0, 0, 0 ) )
- == NULL )
+ i_vout = 0;
+
+ for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
{
- if( p_vout->b_die || p_vout->b_error )
- {
- return;
- }
- msleep( VOUT_OUTMEM_SLEEP );
- }
+ pi_top_skip[i_plane] = 0;
+ }
- while( ( p_outpic_bottom
- = vout_CreatePicture( p_vout->p_sys->p_vout_bottom, 0, 0, 0 ) )
- == NULL )
+ for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
{
- if( p_vout->b_die || p_vout->b_error )
+ for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
{
- vout_DestroyPicture( p_vout->p_sys->p_vout_top, p_outpic_top );
- return;
+ pi_left_skip[i_plane] = 0;
}
- msleep( VOUT_OUTMEM_SLEEP );
- }
-
- vout_DatePicture( p_vout->p_sys->p_vout_top, p_outpic_top, i_date );
- vout_DatePicture( p_vout->p_sys->p_vout_bottom, p_outpic_bottom, i_date );
-
- vout_LinkPicture( p_vout->p_sys->p_vout_top, p_outpic_top );
- vout_LinkPicture( p_vout->p_sys->p_vout_bottom, p_outpic_bottom );
-
- for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
- {
- p_main->fast_memcpy( p_outpic_top->planes[ i_index ].p_data,
- p_pic->planes[ i_index ].p_data,
- p_pic->planes[ i_index ].i_bytes / 2 );
-
- p_main->fast_memcpy( p_outpic_bottom->planes[ i_index ].p_data,
- p_pic->planes[ i_index ].p_data
- + p_pic->planes[ i_index ].i_bytes / 2,
- p_pic->planes[ i_index ].i_bytes / 2 );
- }
-
- vout_UnlinkPicture( p_vout->p_sys->p_vout_top, p_outpic_top );
- vout_UnlinkPicture( p_vout->p_sys->p_vout_bottom, p_outpic_bottom );
-
- vout_DisplayPicture( p_vout->p_sys->p_vout_top, p_outpic_top );
- vout_DisplayPicture( p_vout->p_sys->p_vout_bottom, p_outpic_bottom );
-}
+ for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
+ {
+ while( ( p_outpic =
+ vout_CreatePicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
+ 0, 0, 0 )
+ ) == NULL )
+ {
+ if( p_vout->b_die || p_vout->b_error )
+ {
+ vout_DestroyPicture(
+ p_vout->p_sys->pp_vout[ i_vout ].p_vout, p_outpic );
+ return;
+ }
+
+ msleep( VOUT_OUTMEM_SLEEP );
+ }
-/*****************************************************************************
- * WallNewPicture: allocate a picture
- *****************************************************************************
- * Returns 0 on success, -1 otherwise
- *****************************************************************************/
-static int WallNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
-{
- int i_luma_bytes, i_chroma_bytes;
+ vout_DatePicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
+ p_outpic, p_pic->date );
+ vout_LinkPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
+ p_outpic );
- int i_width = p_vout->output.i_width;
- int i_height = p_vout->output.i_height;
+ for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
+ {
+ u8 *p_in, *p_in_end, *p_out;
+ int i_in_pitch = p_pic->p[i_plane].i_pitch;
+ int i_out_pitch = p_outpic->p[i_plane].i_pitch;
- switch( p_vout->output.i_chroma )
- {
- /* We know this chroma, allocate a buffer which will be used
- * directly by the decoder */
- case YUV_420_PICTURE:
+ p_in = p_pic->p[i_plane].p_pixels
+ + pi_top_skip[i_plane] + pi_left_skip[i_plane];
- /* Precalculate some values */
- p_pic->i_size = i_width * i_height;
- p_pic->i_chroma_width = i_width / 2;
- p_pic->i_chroma_size = i_height * ( i_width / 2 );
+ p_in_end = p_in + p_outpic->p[i_plane].i_lines
+ * p_pic->p[i_plane].i_pitch;
- /* Allocate the memory buffer */
- i_luma_bytes = p_pic->i_size * sizeof(pixel_data_t);
- i_chroma_bytes = p_pic->i_chroma_size * sizeof(pixel_data_t);
+ p_out = p_outpic->p[i_plane].p_pixels;
- /* Y buffer */
- p_pic->planes[ Y_PLANE ].p_data = malloc( i_luma_bytes + 2 * i_chroma_bytes );
- p_pic->planes[ Y_PLANE ].i_bytes = i_luma_bytes;
- p_pic->planes[ Y_PLANE ].i_line_bytes = i_width * sizeof(pixel_data_t);
+ while( p_in < p_in_end )
+ {
+ FAST_MEMCPY( p_out, p_in, i_out_pitch );
+ p_in += i_in_pitch;
+ p_out += i_out_pitch;
+ }
- /* U buffer */
- p_pic->planes[ U_PLANE ].p_data = p_pic->planes[ Y_PLANE ].p_data + i_height * i_width;
- p_pic->planes[ U_PLANE ].i_bytes = i_chroma_bytes / 2;
- p_pic->planes[ U_PLANE ].i_line_bytes = p_pic->i_chroma_width * sizeof(pixel_data_t);
+ pi_left_skip[i_plane] += p_outpic->p[i_plane].i_pitch;
+ }
- /* V buffer */
- p_pic->planes[ V_PLANE ].p_data = p_pic->planes[ U_PLANE ].p_data + i_height * p_pic->i_chroma_width;
- p_pic->planes[ V_PLANE ].i_bytes = i_chroma_bytes / 2;
- p_pic->planes[ V_PLANE ].i_line_bytes = p_pic->i_chroma_width * sizeof(pixel_data_t);
+ vout_UnlinkPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
+ p_outpic );
+ vout_DisplayPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
+ p_outpic );
- /* We allocated 3 planes */
- p_pic->i_planes = 3;
+ i_vout++;
+ }
- return( 0 );
- break;
+ for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
+ {
+ pi_top_skip[i_plane] += p_outpic->p[i_plane].i_lines
+ * p_pic->p[i_plane].i_pitch;
+ }
+ }
+}
- /* Unknown chroma, do nothing */
- default:
+/*****************************************************************************
+ * vout_Display: displays previously rendered output
+ *****************************************************************************
+ * This function send the currently rendered image to Invert image, waits
+ * until it is displayed and switch the two rendering buffers, preparing next
+ * frame.
+ *****************************************************************************/
+static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
+{
+ ;
+}
- return( 0 );
- break;
+static void RemoveAllVout( vout_thread_t *p_vout )
+{
+ while( p_vout->p_sys->i_vout )
+ {
+ --p_vout->p_sys->i_vout;
+ vout_DestroyThread(
+ p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout, NULL );
}
}