Commit 3ea1b0e9 authored by Gildas Bazin's avatar Gildas Bazin

* ALL: Major rework of the subpictures architecture.

  (expect some breakage).
* modules/video_filter/blend.c: new alpha-blending module.
parent 327a8a92
...@@ -98,6 +98,7 @@ HEADERS_include = \ ...@@ -98,6 +98,7 @@ HEADERS_include = \
include/vlc_error.h \ include/vlc_error.h \
include/vlc_es.h \ include/vlc_es.h \
include/vlc_es_out.h \ include/vlc_es_out.h \
include/vlc_filter.h \
include/vlc_help.h \ include/vlc_help.h \
include/vlc_httpd.h \ include/vlc_httpd.h \
include/vlc_input.h \ include/vlc_input.h \
......
...@@ -952,7 +952,7 @@ VLC_ADD_PLUGINS([trivial_mixer spdif_mixer float32_mixer]) ...@@ -952,7 +952,7 @@ VLC_ADD_PLUGINS([trivial_mixer spdif_mixer float32_mixer])
VLC_ADD_PLUGINS([aout_file equalizer]) VLC_ADD_PLUGINS([aout_file equalizer])
VLC_ADD_PLUGINS([i420_rgb i420_yuy2 i422_yuy2 i420_ymga]) VLC_ADD_PLUGINS([i420_rgb i420_yuy2 i422_yuy2 i420_ymga])
VLC_ADD_PLUGINS([m3u id3 playlist export sgimb]) VLC_ADD_PLUGINS([m3u id3 playlist export sgimb])
VLC_ADD_PLUGINS([rawvideo]) VLC_ADD_PLUGINS([rawvideo blend])
VLC_ADD_PLUGINS([wav araw subtitle adpcm a52sys dtssys au]) VLC_ADD_PLUGINS([wav araw subtitle adpcm a52sys dtssys au])
VLC_ADD_PLUGINS([access_file access_udp access_tcp access_http ipv4 access_mms]) VLC_ADD_PLUGINS([access_file access_udp access_tcp access_http ipv4 access_mms])
VLC_ADD_PLUGINS([access_ftp access_directory sap http]) VLC_ADD_PLUGINS([access_ftp access_directory sap http])
......
...@@ -53,7 +53,7 @@ struct text_style_t ...@@ -53,7 +53,7 @@ struct text_style_t
}; };
static const text_style_t default_text_style = { 22, 0xffffff, VLC_FALSE, VLC_FALSE, VLC_FALSE }; static const text_style_t default_text_style = { 22, 0xffffff, VLC_FALSE, VLC_FALSE, VLC_FALSE };
VLC_EXPORT( subpicture_t *, vout_ShowTextRelative, ( vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t ) ); VLC_EXPORT( int, vout_ShowTextRelative, ( vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t ) );
VLC_EXPORT( int, vout_ShowTextAbsolute, ( vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t, mtime_t ) ); VLC_EXPORT( int, vout_ShowTextAbsolute, ( vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t, mtime_t ) );
VLC_EXPORT( void, __vout_OSDMessage, ( vlc_object_t *, int, char *, ... ) ); VLC_EXPORT( void, __vout_OSDMessage, ( vlc_object_t *, int, char *, ... ) );
/** /**
......
...@@ -123,14 +123,26 @@ struct vout_thread_t ...@@ -123,14 +123,26 @@ struct vout_thread_t
vout_chroma_t chroma; /**< translation tables */ vout_chroma_t chroma; /**< translation tables */
/**@}*/ /**@}*/
/* Picture and subpicture heaps */ /* Picture heap */
picture_t p_picture[2*VOUT_MAX_PICTURES+1]; /**< pictures */ picture_t p_picture[2*VOUT_MAX_PICTURES+1]; /**< pictures */
/* Subpicture properties */
subpicture_t p_subpicture[VOUT_MAX_PICTURES]; /**< subpictures */ subpicture_t p_subpicture[VOUT_MAX_PICTURES]; /**< subpictures */
subpicture_t *p_default_channel; /**< subpicture in the default subpicture_t *p_default_channel; /**< subpicture in the default
channel */ channel */
int i_channel_count; /**< index of last subpicture int i_channel_count; /**< index of last subpicture
channel registered */ channel registered */
filter_t *p_blend; /**< alpha blending module */
filter_t *p_text; /**< text renderer module */
vlc_bool_t b_force_crop; /**< force cropping of subpicture */
int i_crop_x, i_crop_y, i_crop_width, i_crop_height; /**< cropping */
vlc_bool_t b_force_alpha; /**< force alpha palette of subpicture */
uint8_t pi_alpha[4]; /**< forced alpha palette */
/* Statistics */ /* Statistics */
count_t c_loops; count_t c_loops;
count_t c_pictures, c_late_pictures; count_t c_pictures, c_late_pictures;
...@@ -144,15 +156,6 @@ struct vout_thread_t ...@@ -144,15 +156,6 @@ struct vout_thread_t
/* Filter chain */ /* Filter chain */
char *psz_filter_chain; char *psz_filter_chain;
vlc_bool_t b_filter_change; vlc_bool_t b_filter_change;
/* text renderer data */
text_renderer_sys_t * p_text_renderer_data; /**< private data for
the text renderer */
module_t * p_text_renderer_module; /**< text renderer module */
/** callback used when a new string needs to be shown on the vout */
subpicture_t * ( *pf_add_string ) ( vout_thread_t *, int, char *,
text_style_t *, int, int, int, mtime_t,
mtime_t );
}; };
#define I_OUTPUTPICTURES p_vout->output.i_pictures #define I_OUTPUTPICTURES p_vout->output.i_pictures
...@@ -268,9 +271,15 @@ VLC_EXPORT( void, vout_DestroySubPicture, ( vout_thread_t *, subpict ...@@ -268,9 +271,15 @@ VLC_EXPORT( void, vout_DestroySubPicture, ( vout_thread_t *, subpict
VLC_EXPORT( void, vout_DisplaySubPicture, ( vout_thread_t *, subpicture_t * ) ); VLC_EXPORT( void, vout_DisplaySubPicture, ( vout_thread_t *, subpicture_t * ) );
VLC_EXPORT( int, vout_RegisterOSDChannel, ( vout_thread_t * ) ); VLC_EXPORT( int, vout_RegisterOSDChannel, ( vout_thread_t * ) );
VLC_EXPORT( void, vout_ClearOSDChannel, ( vout_thread_t *, int ) ); VLC_EXPORT( void, vout_ClearOSDChannel, ( vout_thread_t *, int ) );
#define spu_CreateRegion(a,b) __spu_CreateRegion(VLC_OBJECT(a),b)
VLC_EXPORT( subpicture_region_t *,__spu_CreateRegion, ( vlc_object_t *, video_format_t * ) );
#define spu_DestroyRegion(a,b) __spu_DestroyRegion(VLC_OBJECT(a),b)
VLC_EXPORT( void, __spu_DestroyRegion, ( vlc_object_t *, subpicture_region_t * ) );
void vout_InitSPU( vout_thread_t * );
void vout_DestroySPU( vout_thread_t * );
subpicture_t * vout_SortSubPictures ( vout_thread_t *, mtime_t ); subpicture_t * vout_SortSubPictures ( vout_thread_t *, mtime_t );
void vout_RenderSubPictures ( vout_thread_t *, picture_t *, void vout_RenderSubPictures( vout_thread_t *, picture_t *,
subpicture_t * ); subpicture_t * );
/** @}*/ /** @}*/
/** /**
......
...@@ -48,7 +48,7 @@ struct decoder_t ...@@ -48,7 +48,7 @@ struct decoder_t
picture_t * ( * pf_decode_video )( decoder_t *, block_t ** ); picture_t * ( * pf_decode_video )( decoder_t *, block_t ** );
aout_buffer_t * ( * pf_decode_audio )( decoder_t *, block_t ** ); aout_buffer_t * ( * pf_decode_audio )( decoder_t *, block_t ** );
void ( * pf_decode_sub) ( decoder_t *, block_t ** ); subpicture_t * ( * pf_decode_sub) ( decoder_t *, block_t ** );
block_t * ( * pf_packetize ) ( decoder_t *, block_t ** ); block_t * ( * pf_packetize ) ( decoder_t *, block_t ** );
/* Some decoders only accept packetized data (ie. not truncated) */ /* Some decoders only accept packetized data (ie. not truncated) */
...@@ -74,6 +74,9 @@ struct decoder_t ...@@ -74,6 +74,9 @@ struct decoder_t
void ( * pf_picture_link) ( decoder_t *, picture_t * ); void ( * pf_picture_link) ( decoder_t *, picture_t * );
void ( * pf_picture_unlink) ( decoder_t *, picture_t * ); void ( * pf_picture_unlink) ( decoder_t *, picture_t * );
/* SPU output callbacks */
subpicture_t * ( * pf_spu_buffer_new) ( decoder_t * );
void ( * pf_spu_buffer_del) ( decoder_t *, subpicture_t * );
/* Private structure for the owner of the decoder */ /* Private structure for the owner of the decoder */
decoder_owner_sys_t *p_owner; decoder_owner_sys_t *p_owner;
......
...@@ -277,6 +277,7 @@ typedef struct picture_sys_t picture_sys_t; ...@@ -277,6 +277,7 @@ typedef struct picture_sys_t picture_sys_t;
typedef struct picture_heap_t picture_heap_t; typedef struct picture_heap_t picture_heap_t;
typedef struct subpicture_t subpicture_t; typedef struct subpicture_t subpicture_t;
typedef struct subpicture_sys_t subpicture_sys_t; typedef struct subpicture_sys_t subpicture_sys_t;
typedef struct subpicture_region_t subpicture_region_t;
typedef struct vout_synchro_t vout_synchro_t; typedef struct vout_synchro_t vout_synchro_t;
typedef struct text_renderer_sys_t text_renderer_sys_t; typedef struct text_renderer_sys_t text_renderer_sys_t;
typedef struct text_style_t text_style_t; typedef struct text_style_t text_style_t;
...@@ -314,6 +315,10 @@ typedef struct decoder_sys_t decoder_sys_t; ...@@ -314,6 +315,10 @@ typedef struct decoder_sys_t decoder_sys_t;
typedef struct encoder_t encoder_t; typedef struct encoder_t encoder_t;
typedef struct encoder_sys_t encoder_sys_t; typedef struct encoder_sys_t encoder_sys_t;
/* Filters */
typedef struct filter_t filter_t;
typedef struct filter_sys_t filter_sys_t;
/* Misc */ /* Misc */
typedef struct data_packet_t data_packet_t; typedef struct data_packet_t data_packet_t;
typedef struct data_buffer_t data_buffer_t; typedef struct data_buffer_t data_buffer_t;
......
...@@ -37,9 +37,7 @@ ...@@ -37,9 +37,7 @@
struct video_palette_t struct video_palette_t
{ {
int i_dummy; /**< to keep the compatibility with ffmpeg's palette */ int i_dummy; /**< to keep the compatibility with ffmpeg's palette */
uint8_t palette[256][4]; /**< 4-byte RGBA/YUVA palette */
uint32_t palette[256]; /**< 4-byte ARGB palette entries, stored in native
* byte order */
}; };
/** /**
...@@ -96,6 +94,7 @@ struct video_format_t ...@@ -96,6 +94,7 @@ struct video_format_t
unsigned int i_frame_rate; /**< frame rate numerator */ unsigned int i_frame_rate; /**< frame rate numerator */
unsigned int i_frame_rate_base; /**< frame rate denominator */ unsigned int i_frame_rate_base; /**< frame rate denominator */
int i_rmask, i_rgmask, i_bmask; /**< color masks for RGB chroma */
video_palette_t *p_palette; /**< video palette from demuxer */ video_palette_t *p_palette; /**< video palette from demuxer */
}; };
......
/*****************************************************************************
* vlc_codec.h: codec related structures
*****************************************************************************
* Copyright (C) 1999-2003 VideoLAN
* $Id$
*
* Authors: Gildas Bazin <gbazin@videolan.org>
*
* This program 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.
*
* This program 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, USA.
*****************************************************************************/
#ifndef _VLC_FILTER_H
#define _VLC_FILTER_H 1
/**
* \file
* This file defines the structure and types used by video and audio filters
*/
typedef struct filter_owner_sys_t filter_owner_sys_t;
/**
* \defgroup filter Filter
*
* The structure describing a filter
*
* @{
*/
struct filter_t
{
VLC_COMMON_MEMBERS
/* Module properties */
module_t * p_module;
filter_sys_t * p_sys;
void ( * pf_video_blend ) ( filter_t *, picture_t *,
picture_t *, picture_t *,
int, int );
picture_t * ( * pf_video_filter ) ( filter_t *, picture_t * );
subpicture_t * ( *pf_render_string ) ( filter_t *, block_t * );
/* Input format */
es_format_t fmt_in;
/* Output format of filter */
es_format_t fmt_out;
/*
* Buffers allocation
*/
/* Audio output callbacks */
aout_buffer_t * ( * pf_aout_buffer_new) ( filter_t *, int );
void ( * pf_aout_buffer_del) ( filter_t *, aout_buffer_t * );
/* Video output callbacks */
picture_t * ( * pf_vout_buffer_new) ( filter_t * );
void ( * pf_vout_buffer_del) ( filter_t *, picture_t * );
void ( * pf_picture_link) ( filter_t *, picture_t * );
void ( * pf_picture_unlink) ( filter_t *, picture_t * );
/* SPU output callbacks */
subpicture_t * ( * pf_spu_buffer_new) ( filter_t * );
void ( * pf_spu_buffer_del) ( filter_t *, subpicture_t * );
/* Private structure for the owner of the decoder */
filter_owner_sys_t *p_owner;
};
/**
* @}
*/
#endif /* _VLC_FILTER_H */
...@@ -175,6 +175,8 @@ struct picture_heap_t ...@@ -175,6 +175,8 @@ struct picture_heap_t
#define U_PITCH p[U_PLANE].i_pitch #define U_PITCH p[U_PLANE].i_pitch
#define V_PIXELS p[V_PLANE].p_pixels #define V_PIXELS p[V_PLANE].p_pixels
#define V_PITCH p[V_PLANE].i_pitch #define V_PITCH p[V_PLANE].i_pitch
#define A_PIXELS p[A_PLANE].p_pixels
#define A_PITCH p[A_PLANE].i_pitch
/** /**
* \defgroup subpicture Video Subpictures * \defgroup subpicture Video Subpictures
...@@ -184,6 +186,28 @@ struct picture_heap_t ...@@ -184,6 +186,28 @@ struct picture_heap_t
* @{ * @{
*/ */
/**
* Video subtitle region
*
* A subtitle region is defined by a picture (graphic) and its rendering
* coordinates.
* Subtitles contain a list of regions.
*/
struct subpicture_region_t
{
/** \name Region properties */
/**@{*/
video_format_t fmt; /**< format of the picture */
picture_t picture; /**< picture comprising this region */
int i_x; /**< position of region */
int i_y; /**< position of region */
subpicture_region_t *p_next; /**< next region in the list */
/**@}*/
};
/** /**
* Video subtitle * Video subtitle
* *
...@@ -216,6 +240,8 @@ struct subpicture_t ...@@ -216,6 +240,8 @@ struct subpicture_t
untill the next one appear */ untill the next one appear */
/**@}*/ /**@}*/
subpicture_region_t *p_region; /**< region list composing this subtitle */
/** \name Display properties /** \name Display properties
* These properties are only indicative and may be * These properties are only indicative and may be
* changed by the video output thread, or simply ignored depending of the * changed by the video output thread, or simply ignored depending of the
...@@ -225,6 +251,8 @@ struct subpicture_t ...@@ -225,6 +251,8 @@ struct subpicture_t
int i_y; /**< offset from alignment position */ int i_y; /**< offset from alignment position */
int i_width; /**< picture width */ int i_width; /**< picture width */
int i_height; /**< picture height */ int i_height; /**< picture height */
int b_absolute; /**< position is absolute */
int i_flags; /**< position flags */
/**@}*/ /**@}*/
/** Pointer to function that renders this subtitle in a picture */ /** Pointer to function that renders this subtitle in a picture */
...@@ -232,6 +260,11 @@ struct subpicture_t ...@@ -232,6 +260,11 @@ struct subpicture_t
/** Pointer to function that cleans up the private data of this subtitle */ /** Pointer to function that cleans up the private data of this subtitle */
void ( *pf_destroy ) ( subpicture_t * ); void ( *pf_destroy ) ( subpicture_t * );
/** Pointer to functions for region management */
subpicture_region_t * ( *pf_create_region ) ( vlc_object_t *,
video_format_t * );
void ( *pf_destroy_region ) ( vlc_object_t *, subpicture_region_t * );
/** Private data - the subtitle plugin might want to put stuff here to /** Private data - the subtitle plugin might want to put stuff here to
* keep track of the subpicture */ * keep track of the subpicture */
subpicture_sys_t *p_sys; /* subpicture data */ subpicture_sys_t *p_sys; /* subpicture data */
......
...@@ -37,8 +37,10 @@ ...@@ -37,8 +37,10 @@
/***************************************************************************** /*****************************************************************************
* Module descriptor. * Module descriptor.
*****************************************************************************/ *****************************************************************************/
static int Open ( vlc_object_t *p_this ); static int Open ( vlc_object_t * );
static void Close( vlc_object_t *p_this ); static void Close( vlc_object_t * );
static subpicture_t *Decode( decoder_t *, block_t ** );
vlc_module_begin(); vlc_module_begin();
set_description( _("DVB subtitles decoder") ); set_description( _("DVB subtitles decoder") );
...@@ -173,32 +175,13 @@ typedef struct ...@@ -173,32 +175,13 @@ typedef struct
} dvbsub_clut_t; } dvbsub_clut_t;
typedef struct dvbsub_render_s
{
uint16_t i_x;
uint16_t i_y;
dvbsub_image_t *p_rle_top;
dvbsub_image_t *p_rle_bot;
struct dvbsub_render_s *p_next;
} dvbsub_render_t;
struct subpicture_sys_t
{
dvbsub_render_t *p_objects; /* Linked list of objects to render */
};
struct decoder_sys_t struct decoder_sys_t
{ {
vout_thread_t *p_vout;
bs_t bs; bs_t bs;
/* Decoder internal data */ /* Decoder internal data */
int i_id; int i_id;
int i_ancillary_id; int i_ancillary_id;
mtime_t i_pts; mtime_t i_pts;
dvbsub_page_t *p_page; dvbsub_page_t *p_page;
...@@ -207,9 +190,6 @@ struct decoder_sys_t ...@@ -207,9 +190,6 @@ struct decoder_sys_t
dvbsub_clut_t *p_clut[256]; dvbsub_clut_t *p_clut[256];
dvbsub_clut_t default_clut; dvbsub_clut_t default_clut;
subpicture_t *p_spu;
int i_subpic_channel;
}; };
...@@ -243,8 +223,6 @@ struct decoder_sys_t ...@@ -243,8 +223,6 @@ struct decoder_sys_t
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
static void Decode( decoder_t *, block_t ** );
static void decode_segment( decoder_t *, bs_t * ); static void decode_segment( decoder_t *, bs_t * );
static void decode_page_composition( decoder_t *, bs_t * ); static void decode_page_composition( decoder_t *, bs_t * );
static void decode_region_composition( decoder_t *, bs_t * ); static void decode_region_composition( decoder_t *, bs_t * );
...@@ -254,7 +232,8 @@ static void decode_clut( decoder_t *, bs_t * ); ...@@ -254,7 +232,8 @@ static void decode_clut( decoder_t *, bs_t * );
static void free_objects( decoder_t * ); static void free_objects( decoder_t * );
static void free_all( decoder_t * ); static void free_all( decoder_t * );
static void render( decoder_t *, vout_thread_t * ); static subpicture_t *render( decoder_t * );
static void default_clut_init( decoder_t * ); static void default_clut_init( decoder_t * );
/***************************************************************************** /*****************************************************************************
...@@ -283,8 +262,6 @@ static int Open( vlc_object_t *p_this ) ...@@ -283,8 +262,6 @@ static int Open( vlc_object_t *p_this )
p_sys->p_page = NULL; p_sys->p_page = NULL;
p_sys->p_regions = NULL; p_sys->p_regions = NULL;
p_sys->p_objects = NULL; p_sys->p_objects = NULL;
p_sys->p_vout = NULL;
p_sys->p_spu = NULL;
for( i = 0; i < 256; i++ ) p_sys->p_clut[i] = NULL; for( i = 0; i < 256; i++ ) p_sys->p_clut[i] = NULL;
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'd','v','b','s' ) ); es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'd','v','b','s' ) );
...@@ -302,22 +279,6 @@ static void Close( vlc_object_t *p_this ) ...@@ -302,22 +279,6 @@ static void Close( vlc_object_t *p_this )
decoder_t *p_dec = (decoder_t*) p_this; decoder_t *p_dec = (decoder_t*) p_this;
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
if( p_sys->p_vout && p_sys->p_vout->p_subpicture != NULL )
{
subpicture_t *p_subpic;
int i_subpic;
for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
{
p_subpic = &p_sys->p_vout->p_subpicture[i_subpic];
if( p_subpic != NULL &&
( p_subpic->i_status == RESERVED_SUBPICTURE ||
p_subpic->i_status == READY_SUBPICTURE ) )
{
vout_DestroySubPicture( p_sys->p_vout, p_subpic );
}
}
}
free_all( p_dec ); free_all( p_dec );
free( p_sys ); free( p_sys );
} }
...@@ -325,16 +286,13 @@ static void Close( vlc_object_t *p_this ) ...@@ -325,16 +286,13 @@ static void Close( vlc_object_t *p_this )
/***************************************************************************** /*****************************************************************************
* Decode: * Decode:
*****************************************************************************/ *****************************************************************************/
static void Decode( decoder_t *p_dec, block_t **pp_block ) static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
{ {
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_block; block_t *p_block;
vout_thread_t *p_last_vout; subpicture_t *p_spu = NULL;
if( pp_block == NULL || *pp_block == NULL ) if( pp_block == NULL || *pp_block == NULL ) return NULL;
{
return;
}
p_block = *pp_block; p_block = *pp_block;
*pp_block = NULL; *pp_block = NULL;
...@@ -343,7 +301,7 @@ static void Decode( decoder_t *p_dec, block_t **pp_block ) ...@@ -343,7 +301,7 @@ static void Decode( decoder_t *p_dec, block_t **pp_block )
{ {
msg_Warn( p_dec, "non dated subtitle" ); msg_Warn( p_dec, "non dated subtitle" );
block_Release( p_block ); block_Release( p_block );
return; return NULL;
} }
bs_init( &p_sys->bs, p_block->p_buffer, p_block->i_buffer ); bs_init( &p_sys->bs, p_block->p_buffer, p_block->i_buffer );
...@@ -352,14 +310,14 @@ static void Decode( decoder_t *p_dec, block_t **pp_block ) ...@@ -352,14 +310,14 @@ static void Decode( decoder_t *p_dec, block_t **pp_block )
{ {
msg_Dbg( p_dec, "invalid data identifier" ); msg_Dbg( p_dec, "invalid data identifier" );
block_Release( p_block ); block_Release( p_block );
return; return NULL;
} }
if( bs_read( &p_sys->bs, 8 ) != 0x20 && 0 ) /* Subtitle stream id */ if( bs_read( &p_sys->bs, 8 ) != 0x20 && 0 ) /* Subtitle stream id */
{ {
msg_Dbg( p_dec, "invalid subtitle stream id" ); msg_Dbg( p_dec, "invalid subtitle stream id" );
block_Release( p_block ); block_Release( p_block );
return; return NULL;
} }
while( bs_show( &p_sys->bs, 8 ) == 0x0f ) /* Sync byte */ while( bs_show( &p_sys->bs, 8 ) == 0x0f ) /* Sync byte */
...@@ -371,29 +329,15 @@ static void Decode( decoder_t *p_dec, block_t **pp_block ) ...@@ -371,29 +329,15 @@ static void Decode( decoder_t *p_dec, block_t **pp_block )
{ {
msg_Warn( p_dec, "end marker not found (corrupted subtitle ?)" ); msg_Warn( p_dec, "end marker not found (corrupted subtitle ?)" );
block_Release( p_block ); block_Release( p_block );
return; return NULL;
}
p_last_vout = p_sys->p_vout;
if( ( p_sys->p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT,
FIND_ANYWHERE ) ) )
{
if( p_last_vout != p_sys->p_vout )
{
p_sys->i_subpic_channel =
vout_RegisterOSDChannel( p_sys->p_vout );
} }
/* Check if the page is to be displayed */ /* Check if the page is to be displayed */
if( p_sys->p_page ) render( p_dec, p_sys->p_vout ); if( p_sys->p_page ) p_spu = render( p_dec );
vlc_object_release( p_sys->p_vout );
}
#ifdef DEBUG_DVBSUB
else if( p_sys->p_page ) render( p_dec, NULL );
#endif
block_Release( p_block ); block_Release( p_block );
return p_spu;
} }
/* following functions are local */ /* following functions are local */
...@@ -1174,27 +1118,6 @@ static uint16_t dvbsub_pdata8bpp( bs_t *s, uint16_t* p, ...@@ -1174,27 +1118,6 @@ static uint16_t dvbsub_pdata8bpp( bs_t *s, uint16_t* p,
return ( i_processed + 7 ) / 8 ; return ( i_processed + 7 ) / 8 ;
} }
static dvbsub_image_t *dup_image( dvbsub_image_t *p_i )
{
dvbsub_image_t *p_image = malloc( sizeof(dvbsub_image_t) );
dvbsub_rle_t *p_rle = p_i->p_codes;
*p_image = *p_i;
p_image->p_last = NULL;
while( p_rle )
{
dvbsub_rle_t *p_last = p_image->p_last;
p_image->p_last = malloc( sizeof(dvbsub_rle_t) );
if( !p_last ) p_image->p_codes = p_image->p_last;
if( p_last ) p_last->p_next = p_image->p_last;
*p_image->p_last = *p_rle;
p_rle = p_rle->p_next;
}
return p_image;
}
static void free_image( dvbsub_image_t *p_i ) static void free_image( dvbsub_image_t *p_i )
{ {
dvbsub_rle_t *p1; dvbsub_rle_t *p1;
...@@ -1210,21 +1133,6 @@ static void free_image( dvbsub_image_t *p_i ) ...@@ -1210,21 +1133,6 @@ static void free_image( dvbsub_image_t *p_i )
free( p_i ); free( p_i );
} }
static void free_spu( subpicture_t *p_spu )
{
dvbsub_render_t *p_obj, *p_obj_next;
for( p_obj = p_spu->p_sys->p_objects; p_obj != NULL; p_obj = p_obj_next )
{
p_obj_next = p_obj->p_next;
free_image( p_obj->p_rle_top );
free_image( p_obj->p_rle_bot );
free( p_obj );
}
free( p_spu->p_sys );
p_spu->p_sys = NULL;
}
static void free_objects( decoder_t *p_dec ) static void free_objects( decoder_t *p_dec )
{ {
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
...@@ -1271,201 +1179,20 @@ static void free_all( decoder_t *p_dec ) ...@@ -1271,201 +1179,20 @@ static void free_all( decoder_t *p_dec )
free_objects( p_dec ); free_objects( p_dec );
} }
static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic, static subpicture_t *render( decoder_t *p_dec )
dvbsub_render_t *p_r )
{
/* Common variables */
uint8_t *p_desty;
uint16_t i,j;
uint16_t i_cnt;
uint16_t x, y;
dvbsub_rle_t* p_c;
dvbsub_image_t* p_im = p_r->p_rle_top;
i=0;
j=0;
p_desty = p_pic->Y_PIXELS;
//let's render the 1st frame
for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
{
//if( p_c->y != 0 && p_c->t < 0x20)
if( p_c->y != 0 && p_c->t < 0x20)
{
x = j+ p_r->i_x;
y = 2*i+p_r->i_y;
//memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
// In YUY2 we have to set pixel per pixel
for( i_cnt = 0; i_cnt < p_c->i_num; i_cnt+=2 )
{
memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt, p_c->y, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+1, p_c->cr, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+2, p_c->y, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+3, p_c->cb, 1);
}
}
j += p_c->i_num;
if(j >= p_im->i_cols[i])
{
i++; j=0;
}
if( i>= p_im->i_rows) break;
}
//idem for the second frame
p_im = p_r->p_rle_bot; i=0; j=0;
for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
{
if( p_c->y != 0 && p_c->t < 0x20)
{
x = j+ p_r->i_x;
y = 2*i+1+p_r->i_y;
//memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
// In YUY2 we have to set pixel per pixel
for( i_cnt = 0; i_cnt < p_c->i_num; i_cnt+=2 )
{
memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt, p_c->y, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+1, p_c->cr, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+2, p_c->y, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+3, p_c->cb, 1);
}
}
j += p_c->i_num;
if(j >= p_im->i_cols[i])
{
i++; j=0;
}
if( i>= p_im->i_rows) break;
}
}
static void RenderI42x( vout_thread_t *p_vout, picture_t *p_pic,
dvbsub_render_t *p_r )
{
/* Common variables */
uint8_t *p_desty = p_pic->Y_PIXELS;
uint8_t *p_destu = p_pic->U_PIXELS;
uint8_t *p_destv = p_pic->V_PIXELS;
dvbsub_image_t* p_im = p_r->p_rle_top;
dvbsub_rle_t* p_c;
uint16_t i, j, x, y;
int i_x_subsampling =
p_vout->output.i_chroma == VLC_FOURCC('I','4','2','2') ? 1 : 2;
/* Let's render the top field */
p_im = p_r->p_rle_bot; i = 0; j = 0;
for( p_c = p_im->p_codes; p_c->p_next != NULL; p_c = p_c->p_next )
{
if( p_c->y != 0 && p_c->t != 0xFF )
{
x = j + p_r->i_x;
y = 2 * i + p_r->i_y;
memset( p_desty + y * p_pic->Y_PITCH + x, p_c->y, p_c->i_num );
memset( p_destu + y/2 * p_pic->U_PITCH + x/i_x_subsampling,
p_c->cr, p_c->i_num/i_x_subsampling );
memset( p_destv + y/2 * p_pic->V_PITCH + x/i_x_subsampling,
p_c->cb, p_c->i_num/i_x_subsampling );
}
j += p_c->i_num;
if( j >= p_im->i_cols[i] )
{
i++; j=0;
}
if( i >= p_im->i_rows) break;
}
/* Idem for the bottom field */
p_im = p_r->p_rle_bot; i = 0; j = 0;
for( p_c = p_im->p_codes; p_c->p_next != NULL; p_c = p_c->p_next )
{
if( p_c->y != 0 && p_c->t != 0xFF )
{
x = j + p_r->i_x;
y = 2*i + 1 + p_r->i_y;
memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
/* No U or V (decimation) */
}
j += p_c->i_num;
if( j >= p_im->i_cols[i] )
{
i++; j=0;
}
if( i >= p_im->i_rows ) break;
}
}
static void RenderDVBSUB( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu )
{
dvbsub_render_t* p_render;
if( p_spu->p_sys == NULL ) return;
p_render = p_spu->p_sys->p_objects;
while( p_render )
{
switch( p_vout->output.i_chroma )
{
/* I420 target, no scaling */
case VLC_FOURCC('I','4','2','2'):
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
/* As long as we just use Y info, I422 and YV12 are just equivalent
* to I420. Remember to change it the day we'll take into account
* U and V info. */
RenderI42x( p_vout, p_pic, p_render );
break;
/* RV16 target, scaling */
case VLC_FOURCC('R','V','1','6'):
msg_Err(p_vout, "unimplemented chroma: RV16");
/* RenderRV16( p_vout, p_pic, p_spu ); */
break;
/* RV32 target, scaling */
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
msg_Err(p_vout, "unimplemented chroma: RV32");
/* RenderRV32( p_vout, p_pic, p_spu ); */
break;
/* NVidia overlay, no scaling */
case VLC_FOURCC('Y','U','Y','2'):
RenderYUY2( p_vout, p_pic, p_render );
break;
default:
msg_Err( p_vout, "unknown chroma, can't render SPU" );
break;
}
p_render = p_render->p_next;
}
}
static void render( decoder_t *p_dec, vout_thread_t *p_vout )
{ {
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
dvbsub_render_t *p_render = NULL, *p_current, *p_last = NULL;
dvbsub_clut_t *p_clut; dvbsub_clut_t *p_clut;
dvbsub_rle_t *p_c; dvbsub_rle_t *p_c;
subpicture_t *p_spu;
subpicture_region_t **pp_spu_region;
int i, j = 0, i_timeout = 0; int i, j = 0, i_timeout = 0;
/* Allocate the subpicture internal data. */ /* Allocate the subpicture internal data. */
#ifdef DEBUG_DVBSUB p_spu = p_dec->pf_spu_buffer_new( p_dec );
if( !p_vout ) p_sys->p_spu = malloc( sizeof(subpicture_t) ); if( !p_spu ) return NULL;
else
#endif pp_spu_region = &p_spu->p_region;
p_sys->p_spu =
vout_CreateSubPicture( p_vout, p_sys->i_subpic_channel,
MEMORY_SUBPICTURE );
if( p_sys->p_spu == NULL ) return;
/* Loop on region definitions */ /* Loop on region definitions */
#ifdef DEBUG_DVBSUB #ifdef DEBUG_DVBSUB
...@@ -1477,6 +1204,10 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout ) ...@@ -1477,6 +1204,10 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout )
{ {
dvbsub_region_t *p_region; dvbsub_region_t *p_region;
dvbsub_regiondef_t *p_regiondef; dvbsub_regiondef_t *p_regiondef;
subpicture_region_t *p_spu_region;
uint8_t *p_y, *p_u, *p_v, *p_a;
video_format_t fmt;
int i_pitch;
i_timeout = p_sys->p_page->i_timeout; i_timeout = p_sys->p_page->i_timeout;
...@@ -1500,11 +1231,37 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout ) ...@@ -1500,11 +1231,37 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout )
continue; continue;
} }
/* Create new SPU region */
memset( &fmt, 0, sizeof(video_format_t) );
fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
fmt.i_aspect = VOUT_ASPECT_FACTOR;
fmt.i_width = fmt.i_visible_width = p_region->i_width;
fmt.i_height = fmt.i_visible_height = p_region->i_height;
fmt.i_x_offset = fmt.i_y_offset = 0;
p_spu_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
if( !p_region )
{
msg_Err( p_dec, "cannot allocate SPU region" );
continue;
}
p_spu_region->i_x = p_regiondef->i_x;
p_spu_region->i_y = p_regiondef->i_y;
*pp_spu_region = p_spu_region;
pp_spu_region = &p_spu_region->p_next;
p_y = p_spu_region->picture.Y_PIXELS;
p_u = p_spu_region->picture.U_PIXELS;
p_v = p_spu_region->picture.V_PIXELS;
p_a = p_spu_region->picture.A_PIXELS;
i_pitch = p_spu_region->picture.Y_PITCH;
memset( p_a, 0, i_pitch * p_region->i_height );
/* Loop on object definitions */ /* Loop on object definitions */
for( j = 0; j < p_region->i_object_defs; j++ ) for( j = 0; j < p_region->i_object_defs; j++ )
{ {
dvbsub_object_t *p_object; dvbsub_object_t *p_object;
dvbsub_objectdef_t *p_objectdef; dvbsub_objectdef_t *p_objectdef;
uint16_t k, l, x, y;
p_objectdef = &p_region->p_object_defs[j]; p_objectdef = &p_region->p_object_defs[j];
...@@ -1526,65 +1283,64 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout ) ...@@ -1526,65 +1283,64 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout )
continue; continue;
} }
/* Allocate the render structure */ /* Draw SPU region */
p_current = malloc( sizeof(dvbsub_render_t) );
p_current->p_next = NULL;
p_current->i_x = p_regiondef->i_x + p_objectdef->i_x;
p_current->i_y = p_regiondef->i_y + p_objectdef->i_y;
p_current->p_rle_top = dup_image( p_object->topfield );
p_current->p_rle_bot = dup_image( p_object->bottomfield );
if( !p_render ) p_render = p_current;
if( p_last ) p_last->p_next = p_current;
p_last = p_current;
p_clut = p_sys->p_clut[p_region->i_clut]; p_clut = p_sys->p_clut[p_region->i_clut];
if( !p_clut ) p_clut = &p_sys->default_clut; if( !p_clut ) p_clut = &p_sys->default_clut;
/* Compute the color datas according to the appropriate CLUT */ for( k = 0, l = 0, p_c = p_object->topfield->p_codes;
for( p_c = p_current->p_rle_top->p_codes; p_c->p_next; p_c = p_c->p_next )
p_c->p_next != NULL; p_c = p_c->p_next )
{ {
/* Compute the color data according to the appropriate CLUT */
dvbsub_color_t *p_color = (p_c->i_bpp == 2) ? p_clut->c_2b : dvbsub_color_t *p_color = (p_c->i_bpp == 2) ? p_clut->c_2b :
(p_c->i_bpp == 4) ? p_clut->c_4b : p_clut->c_8b; (p_c->i_bpp == 4) ? p_clut->c_4b : p_clut->c_8b;
p_c->y = p_color[p_c->i_color_code].Y; x = l + p_objectdef->i_x;
p_c->cr = p_color[p_c->i_color_code].Cr; y = 2 * k + p_objectdef->i_y;
p_c->cb = p_color[p_c->i_color_code].Cb; memset( p_y + y * i_pitch + x, p_color[p_c->i_color_code].Y,
p_c->t = p_color[p_c->i_color_code].T; p_c->i_num );
memset( p_u + y * i_pitch + x, p_color[p_c->i_color_code].Cr,
p_c->i_num );
memset( p_v + y * i_pitch + x, p_color[p_c->i_color_code].Cb,
p_c->i_num );
memset( p_a + y * i_pitch + x,
255 - p_color[p_c->i_color_code].T, p_c->i_num );
l += p_c->i_num;
if( l >= p_object->topfield->i_cols[k] ) { k++; l = 0; }
if( k >= p_object->topfield->i_rows) break;
} }
for( p_c = p_current->p_rle_bot->p_codes; p_c->p_next != NULL;
p_c = p_c->p_next ) for( k = 0, l = 0, p_c = p_object->bottomfield->p_codes;
p_c->p_next; p_c = p_c->p_next )
{ {
/* Compute the color data according to the appropriate CLUT */
dvbsub_color_t *p_color = (p_c->i_bpp == 2) ? p_clut->c_2b : dvbsub_color_t *p_color = (p_c->i_bpp == 2) ? p_clut->c_2b :
(p_c->i_bpp == 4) ? p_clut->c_4b : p_clut->c_8b; (p_c->i_bpp == 4) ? p_clut->c_4b : p_clut->c_8b;
p_c->y = p_color[p_c->i_color_code].Y; x = l + p_objectdef->i_x;
p_c->cr = p_color[p_c->i_color_code].Cr; y = 2 * k + 1 + p_objectdef->i_y;
p_c->cb = p_color[p_c->i_color_code].Cb; memset( p_y + y * i_pitch + x, p_color[p_c->i_color_code].Y,
p_c->t = p_color[p_c->i_color_code].T; p_c->i_num );
memset( p_u + y * i_pitch + x, p_color[p_c->i_color_code].Cr,
p_c->i_num );
memset( p_v + y * i_pitch + x, p_color[p_c->i_color_code].Cb,
p_c->i_num );
memset( p_a + y * i_pitch + x,
255 - p_color[p_c->i_color_code].T, p_c->i_num );
l += p_c->i_num;
if( l >= p_object->bottomfield->i_cols[k] ) { k++; l = 0; }
if( k >= p_object->bottomfield->i_rows) break;
} }
} }
} }
/* Set the pf_render callback */ /* Set the pf_render callback */
p_sys->p_spu->pf_render = RenderDVBSUB; p_spu->i_start = p_sys->i_pts;
p_sys->p_spu->p_sys = malloc( sizeof(subpicture_sys_t) ); p_spu->i_stop = p_spu->i_start + i_timeout * 1000000;
p_sys->p_spu->p_sys->p_objects = p_render; p_spu->b_ephemer = VLC_TRUE;
p_sys->p_spu->pf_destroy = free_spu;
p_sys->p_spu->i_start = p_sys->i_pts;
p_sys->p_spu->i_stop = p_sys->p_spu->i_start + i_timeout * 1000000;
p_sys->p_spu->b_ephemer = VLC_TRUE;
#ifdef DEBUG_DVBSUB
if( !p_vout )
{
free_spu( p_sys->p_spu );
free( p_sys->p_spu );
p_sys->p_spu = NULL;
return;
}
#endif
vout_DisplaySubPicture( p_vout, p_sys->p_spu ); return p_spu;
} }
SOURCES_spudec = \ SOURCES_spudec = \
spudec.c \ spudec.c \
parse.c \ parse.c \
render.c \
spudec.h \ spudec.h \
$(NULL) $(NULL)
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* *
* Authors: Samuel Hocevar <sam@zoy.org> * Authors: Samuel Hocevar <sam@zoy.org>
* Laurent Aimar <fenrir@via.ecp.fr> * Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@videolan.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -34,14 +35,9 @@ ...@@ -34,14 +35,9 @@
/***************************************************************************** /*****************************************************************************
* Local prototypes. * Local prototypes.
*****************************************************************************/ *****************************************************************************/
static int ParseControlSeq ( decoder_t *, subpicture_t * ); static int ParseControlSeq( decoder_t *, subpicture_t *, subpicture_data_t *);
static int ParseRLE ( decoder_t *, subpicture_t * ); static int ParseRLE ( decoder_t *, subpicture_t *, subpicture_data_t *);
static void Render ( decoder_t *, subpicture_t *, subpicture_data_t *);
static void DestroySPU ( subpicture_t * );
static void UpdateSPU ( subpicture_t *, vlc_object_t * );
static int CropCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
/***************************************************************************** /*****************************************************************************
* AddNibble: read a nibble from a source packet and add it to our integer. * AddNibble: read a nibble from a source packet and add it to our integer.
...@@ -65,89 +61,56 @@ static inline unsigned int AddNibble( unsigned int i_code, ...@@ -65,89 +61,56 @@ static inline unsigned int AddNibble( unsigned int i_code,
* This function parses the SPU packet and, if valid, sends it to the * This function parses the SPU packet and, if valid, sends it to the
* video output. * video output.
*****************************************************************************/ *****************************************************************************/
void E_(ParsePacket)( decoder_t *p_dec) subpicture_t * E_(ParsePacket)( decoder_t *p_dec )
{ {
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
subpicture_data_t *p_spu_data;
subpicture_t *p_spu; subpicture_t *p_spu;
int i_spu_channel;
/* Allocate the subpicture internal data. */ /* Allocate the subpicture internal data. */
p_spu = vout_CreateSubPicture( p_sys->p_vout, p_sys->i_subpic_channel, p_spu = p_dec->pf_spu_buffer_new( p_dec );
MEMORY_SUBPICTURE ); if( !p_spu ) return NULL;
if( p_spu == NULL )
{
return;
}
/* Rationale for the "p_spudec->i_rle_size * 4": we are going to /* Rationale for the "p_spudec->i_rle_size * 4": we are going to
* expand the RLE stuff so that we won't need to read nibbles later * expand the RLE stuff so that we won't need to read nibbles later
* on. This will speed things up a lot. Plus, we'll only need to do * on. This will speed things up a lot. Plus, we'll only need to do
* this stupid interlacing stuff once. */ * this stupid interlacing stuff once. */
p_spu->p_sys = malloc( sizeof( subpicture_sys_t ) + 4*p_sys->i_rle_size ); p_spu_data = malloc( sizeof(subpicture_data_t) + 4 * p_sys->i_rle_size );
p_spu_data->p_data = (uint8_t *)p_spu_data + sizeof(subpicture_data_t);
/* Fill the p_spu structure */ p_spu_data->b_palette = VLC_FALSE;
vlc_mutex_init( p_dec, &p_spu->p_sys->lock );
p_spu->pf_render = E_(RenderSPU);
p_spu->pf_destroy = DestroySPU;
p_spu->p_sys->p_data = (uint8_t*)p_spu->p_sys + sizeof( subpicture_sys_t );
p_spu->p_sys->b_palette = VLC_FALSE;
p_spu->p_sys->pi_alpha[0] = 0x00;
p_spu->p_sys->pi_alpha[1] = 0x0f;
p_spu->p_sys->pi_alpha[2] = 0x0f;
p_spu->p_sys->pi_alpha[3] = 0x0f;
p_spu->p_sys->b_crop = VLC_FALSE; p_spu_data->pi_alpha[0] = 0x00;
p_spu_data->pi_alpha[1] = 0x0f;
p_spu_data->pi_alpha[2] = 0x0f;
p_spu_data->pi_alpha[3] = 0x0f;
/* Get display time now. If we do it later, we may miss the PTS. */ /* Get display time now. If we do it later, we may miss the PTS. */
p_spu->p_sys->i_pts = p_sys->i_pts; p_spu_data->i_pts = p_sys->i_pts;
/* Attach to our input thread */
p_spu->p_sys->p_input = vlc_object_find( p_dec,
VLC_OBJECT_INPUT, FIND_PARENT );
if( p_spu->p_sys->p_input )
{
vlc_value_t val;
if( !var_Get( p_spu->p_sys->p_input, "highlight-mutex", &val ) )
{
vlc_mutex_t *p_mutex = val.p_address;
vlc_mutex_lock( p_mutex );
UpdateSPU( p_spu, VLC_OBJECT(p_spu->p_sys->p_input) );
var_AddCallback( p_spu->p_sys->p_input,
"highlight", CropCallback, p_spu );
vlc_mutex_unlock( p_mutex );
}
}
/* Getting the control part */ /* Getting the control part */
if( ParseControlSeq( p_dec, p_spu ) ) if( ParseControlSeq( p_dec, p_spu, p_spu_data ) )
{ {
/* There was a parse error, delete the subpicture */ /* There was a parse error, delete the subpicture */
vout_DestroySubPicture( p_sys->p_vout, p_spu ); p_dec->pf_spu_buffer_del( p_dec, p_spu );
return; return NULL;
} }
/* We try to display it */ /* We try to display it */
if( ParseRLE( p_dec, p_spu ) ) if( ParseRLE( p_dec, p_spu, p_spu_data ) )
{ {
/* There was a parse error, delete the subpicture */ /* There was a parse error, delete the subpicture */
vout_DestroySubPicture( p_sys->p_vout, p_spu ); p_dec->pf_spu_buffer_del( p_dec, p_spu );
return; return NULL;
} }
msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x", msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x",
p_sys->i_spu_size, p_sys->i_spu_size,
p_spu->p_sys->pi_offset[0], p_spu->p_sys->pi_offset[1] ); p_spu_data->pi_offset[0], p_spu_data->pi_offset[1] );
/* SPU is finished - we can ask the video output to display it */ Render( p_dec, p_spu, p_spu_data );
vout_DisplaySubPicture( p_sys->p_vout, p_spu ); free( p_spu_data );
/* TODO: do stuff! */ return p_spu;
} }
/***************************************************************************** /*****************************************************************************
...@@ -157,7 +120,8 @@ void E_(ParsePacket)( decoder_t *p_dec) ...@@ -157,7 +120,8 @@ void E_(ParsePacket)( decoder_t *p_dec)
* information, coordinates, and so on. For more information on the * information, coordinates, and so on. For more information on the
* subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
*****************************************************************************/ *****************************************************************************/
static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu ) static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
subpicture_data_t *p_spu_data )
{ {
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
...@@ -211,17 +175,17 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -211,17 +175,17 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
switch( i_command ) switch( i_command )
{ {
case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */ case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */
p_spu->i_start = p_spu->p_sys->i_pts + date; p_spu->i_start = p_spu_data->i_pts + date;
p_spu->b_ephemer = VLC_TRUE; p_spu->b_ephemer = VLC_TRUE;
break; break;
/* Convert the dates in seconds to PTS values */ /* Convert the dates in seconds to PTS values */
case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */ case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */
p_spu->i_start = p_spu->p_sys->i_pts + date; p_spu->i_start = p_spu_data->i_pts + date;
break; break;
case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */ case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */
p_spu->i_stop = p_spu->p_sys->i_pts + date; p_spu->i_stop = p_spu_data->i_pts + date;
break; break;
case SPU_CMD_SET_PALETTE: case SPU_CMD_SET_PALETTE:
...@@ -231,7 +195,7 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -231,7 +195,7 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
{ {
unsigned int idx[4]; unsigned int idx[4];
p_spu->p_sys->b_palette = VLC_TRUE; p_spu_data->b_palette = VLC_TRUE;
idx[0] = (p_sys->buffer[i_index+0]>>4)&0x0f; idx[0] = (p_sys->buffer[i_index+0]>>4)&0x0f;
idx[1] = (p_sys->buffer[i_index+0])&0x0f; idx[1] = (p_sys->buffer[i_index+0])&0x0f;
...@@ -243,9 +207,9 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -243,9 +207,9 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
uint32_t i_color = p_dec->fmt_in.subs.spu.palette[1+idx[i]]; uint32_t i_color = p_dec->fmt_in.subs.spu.palette[1+idx[i]];
/* FIXME: this job should be done sooner */ /* FIXME: this job should be done sooner */
p_spu->p_sys->pi_yuv[3-i][0] = (i_color>>16) & 0xff; p_spu_data->pi_yuv[3-i][0] = (i_color>>16) & 0xff;
p_spu->p_sys->pi_yuv[3-i][1] = (i_color>>0) & 0xff; p_spu_data->pi_yuv[3-i][1] = (i_color>>0) & 0xff;
p_spu->p_sys->pi_yuv[3-i][2] = (i_color>>8) & 0xff; p_spu_data->pi_yuv[3-i][2] = (i_color>>8) & 0xff;
} }
} }
i_index += 2; i_index += 2;
...@@ -262,10 +226,10 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -262,10 +226,10 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
* alpha palettes are present - dunno why. */ * alpha palettes are present - dunno why. */
if( pi_alpha[0] | pi_alpha[1] | pi_alpha[2] | pi_alpha[3] ) if( pi_alpha[0] | pi_alpha[1] | pi_alpha[2] | pi_alpha[3] )
{ {
p_spu->p_sys->pi_alpha[0] = pi_alpha[0]; p_spu_data->pi_alpha[0] = pi_alpha[0];
p_spu->p_sys->pi_alpha[1] = pi_alpha[1]; p_spu_data->pi_alpha[1] = pi_alpha[1];
p_spu->p_sys->pi_alpha[2] = pi_alpha[2]; p_spu_data->pi_alpha[2] = pi_alpha[2];
p_spu->p_sys->pi_alpha[3] = pi_alpha[3]; p_spu_data->pi_alpha[3] = pi_alpha[3];
} }
else else
{ {
...@@ -290,8 +254,8 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -290,8 +254,8 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
break; break;
case SPU_CMD_SET_OFFSETS: /* 06xxxxyyyy (byte offsets) */ case SPU_CMD_SET_OFFSETS: /* 06xxxxyyyy (byte offsets) */
p_spu->p_sys->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+0]) - 4; p_spu_data->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+0]) - 4;
p_spu->p_sys->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+2]) - 4; p_spu_data->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+2]) - 4;
i_index += 4; i_index += 4;
break; break;
...@@ -335,9 +299,6 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -335,9 +299,6 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
{ {
/* This subtitle will live for 5 seconds or until the next subtitle */ /* This subtitle will live for 5 seconds or until the next subtitle */
p_spu->i_stop = p_spu->i_start + (mtime_t)500 * 11000; p_spu->i_stop = p_spu->i_start + (mtime_t)500 * 11000;
/* FIXME how to access i_rate ?
* p_spudec->bit_stream.p_pes->i_rate / DEFAULT_RATE;
*/
p_spu->b_ephemer = VLC_TRUE; p_spu->b_ephemer = VLC_TRUE;
} }
...@@ -362,7 +323,8 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -362,7 +323,8 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
* convenient structure for later decoding. For more information on the * convenient structure for later decoding. For more information on the
* subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
*****************************************************************************/ *****************************************************************************/
static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu ) static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu,
subpicture_data_t *p_spu_data )
{ {
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p_src = &p_sys->buffer[4]; uint8_t *p_src = &p_sys->buffer[4];
...@@ -373,26 +335,19 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -373,26 +335,19 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
unsigned int i_height = p_spu->i_height; unsigned int i_height = p_spu->i_height;
unsigned int i_x, i_y; unsigned int i_x, i_y;
uint16_t *p_dest = (uint16_t *)p_spu->p_sys->p_data; uint16_t *p_dest = (uint16_t *)p_spu_data->p_data;
/* The subtitles are interlaced, we need two offsets */ /* The subtitles are interlaced, we need two offsets */
unsigned int i_id = 0; /* Start on the even SPU layer */ unsigned int i_id = 0; /* Start on the even SPU layer */
unsigned int pi_table[ 2 ]; unsigned int pi_table[ 2 ];
unsigned int *pi_offset; unsigned int *pi_offset;
#if 0 /* cropping */
vlc_bool_t b_empty_top = VLC_TRUE,
b_empty_bottom = VLC_FALSE;
unsigned int i_skipped_top = 0,
i_skipped_bottom = 0;
#endif
/* Colormap statistics */ /* Colormap statistics */
int i_border = -1; int i_border = -1;
int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0; int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0;
pi_table[ 0 ] = p_spu->p_sys->pi_offset[ 0 ] << 1; pi_table[ 0 ] = p_spu_data->pi_offset[ 0 ] << 1;
pi_table[ 1 ] = p_spu->p_sys->pi_offset[ 1 ] << 1; pi_table[ 1 ] = p_spu_data->pi_offset[ 1 ] << 1;
for( i_y = 0 ; i_y < i_height ; i_y++ ) for( i_y = 0 ; i_y < i_height ; i_y++ )
{ {
...@@ -436,58 +391,25 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -436,58 +391,25 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width ) if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
{ {
msg_Err( p_dec, msg_Err( p_dec, "out of bounds, %i at (%i,%i) is out of %ix%i",
"out of bounds, %i at (%i,%i) is out of %ix%i",
i_code >> 2, i_x, i_y, i_width, i_height ); i_code >> 2, i_x, i_y, i_width, i_height );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
/* Try to find the border color */ /* Try to find the border color */
if( p_spu->p_sys->pi_alpha[ i_code & 0x3 ] != 0x00 ) if( p_spu_data->pi_alpha[ i_code & 0x3 ] != 0x00 )
{ {
i_border = i_code & 0x3; i_border = i_code & 0x3;
stats[i_border] += i_code >> 2; stats[i_border] += i_code >> 2;
} }
#if 0 /* cropping */
if( (i_code >> 2) == i_width
&& p_spu->p_sys->pi_alpha[ i_code & 0x3 ] == 0x00 )
{
if( b_empty_top )
{
/* This is a blank top line, we skip it */
i_skipped_top++;
}
else
{
/* We can't be sure the current lines will be skipped,
* so we store the code just in case. */
*p_dest++ = i_code; *p_dest++ = i_code;
b_empty_bottom = VLC_TRUE;
i_skipped_bottom++;
}
}
else
{
/* We got a valid code, store it */
*p_dest++ = i_code;
/* Valid code means no blank line */
b_empty_top = VLC_FALSE;
b_empty_bottom = VLC_FALSE;
i_skipped_bottom = 0;
}
#else
*p_dest++ = i_code;
#endif
} }
/* Check that we didn't go too far */ /* Check that we didn't go too far */
if( i_x > i_width ) if( i_x > i_width )
{ {
msg_Err( p_dec, "i_x overflowed, %i > %i", msg_Err( p_dec, "i_x overflowed, %i > %i", i_x, i_width );
i_x, i_width );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
...@@ -521,27 +443,15 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -521,27 +443,15 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
msg_Dbg( p_dec, "valid subtitle, size: %ix%i, position: %i,%i", msg_Dbg( p_dec, "valid subtitle, size: %ix%i, position: %i,%i",
p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y ); p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y );
#if 0 /* cropping */
/* Crop if necessary */
if( i_skipped_top || i_skipped_bottom )
{
p_spu->i_y += i_skipped_top;
p_spu->i_height -= i_skipped_top + i_skipped_bottom;
msg_Dbg( p_spudec->p_fifo, "cropped to: %ix%i, position: %i,%i",
p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y );
}
#endif
/* Handle color if no palette was found */ /* Handle color if no palette was found */
if( !p_spu->p_sys->b_palette ) if( !p_spu_data->b_palette )
{ {
int i, i_inner = -1, i_shade = -1; int i, i_inner = -1, i_shade = -1;
/* Set the border color */ /* Set the border color */
p_spu->p_sys->pi_yuv[i_border][0] = 0x00; p_spu_data->pi_yuv[i_border][0] = 0x00;
p_spu->p_sys->pi_yuv[i_border][1] = 0x80; p_spu_data->pi_yuv[i_border][1] = 0x80;
p_spu->p_sys->pi_yuv[i_border][2] = 0x80; p_spu_data->pi_yuv[i_border][2] = 0x80;
stats[i_border] = 0; stats[i_border] = 0;
/* Find the inner colors */ /* Find the inner colors */
...@@ -572,103 +482,73 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu ) ...@@ -572,103 +482,73 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
/* Set the inner color */ /* Set the inner color */
if( i_inner != -1 ) if( i_inner != -1 )
{ {
p_spu->p_sys->pi_yuv[i_inner][0] = 0xff; p_spu_data->pi_yuv[i_inner][0] = 0xff;
p_spu->p_sys->pi_yuv[i_inner][1] = 0x80; p_spu_data->pi_yuv[i_inner][1] = 0x80;
p_spu->p_sys->pi_yuv[i_inner][2] = 0x80; p_spu_data->pi_yuv[i_inner][2] = 0x80;
} }
/* Set the anti-aliasing color */ /* Set the anti-aliasing color */
if( i_shade != -1 ) if( i_shade != -1 )
{ {
p_spu->p_sys->pi_yuv[i_shade][0] = 0x80; p_spu_data->pi_yuv[i_shade][0] = 0x80;
p_spu->p_sys->pi_yuv[i_shade][1] = 0x80; p_spu_data->pi_yuv[i_shade][1] = 0x80;
p_spu->p_sys->pi_yuv[i_shade][2] = 0x80; p_spu_data->pi_yuv[i_shade][2] = 0x80;
} }
msg_Dbg( p_dec, msg_Dbg( p_dec, "using custom palette (border %i, inner %i, shade %i)",
"using custom palette (border %i, inner %i, shade %i)",
i_border, i_inner, i_shade ); i_border, i_inner, i_shade );
} }
return VLC_SUCCESS; return VLC_SUCCESS;
} }
/***************************************************************************** static void Render( decoder_t *p_dec, subpicture_t *p_spu,
* DestroySPU: subpicture destructor subpicture_data_t *p_spu_data )
*****************************************************************************/
static void DestroySPU( subpicture_t *p_spu )
{ {
if( p_spu->p_sys->p_input ) uint8_t *p_p;
{ int i_x, i_y, i_len, i_color, i_pitch;
/* Detach from our input thread */ uint16_t *p_source = (uint16_t *)p_spu_data->p_data;
var_DelCallback( p_spu->p_sys->p_input, "highlight", video_format_t fmt;
CropCallback, p_spu );
vlc_object_release( p_spu->p_sys->p_input ); /* Create a new subpicture region */
memset( &fmt, 0, sizeof(video_format_t) );
fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
fmt.i_aspect = VOUT_ASPECT_FACTOR;
fmt.i_width = fmt.i_visible_width = p_spu->i_width;
fmt.i_height = fmt.i_visible_height = p_spu->i_height;
fmt.i_x_offset = fmt.i_y_offset = 0;
p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
if( !p_spu->p_region )
{
msg_Err( p_dec, "cannot allocate SPU region" );
return;
} }
vlc_mutex_destroy( &p_spu->p_sys->lock ); p_spu->p_region->i_x = p_spu->p_region->i_y = 0;
free( p_spu->p_sys ); p_p = p_spu->p_region->picture.p->p_pixels;
} i_pitch = p_spu->p_region->picture.p->i_pitch;
/*****************************************************************************
* UpdateSPU: update subpicture settings
*****************************************************************************
* This function is called from CropCallback and at initialization time, to
* retrieve crop information from the input.
*****************************************************************************/
static void UpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
{
vlc_value_t val;
if( var_Get( p_object, "highlight", &val ) ) /* Build palette */
for( i_x = 0; i_x < 4; i_x++ )
{ {
return; fmt.p_palette->palette[i_x][0] = p_spu_data->pi_yuv[i_x][0];
fmt.p_palette->palette[i_x][1] = p_spu_data->pi_yuv[i_x][1];
fmt.p_palette->palette[i_x][2] = p_spu_data->pi_yuv[i_x][2];
fmt.p_palette->palette[i_x][3] =
p_spu_data->pi_alpha[i_x] == 0xf ? 0xff :
p_spu_data->pi_alpha[i_x] << 4;
} }
p_spu->p_sys->b_crop = val.b_bool; /* Draw until we reach the bottom of the subtitle */
if( !p_spu->p_sys->b_crop ) for( i_y = 0; i_y < p_spu->i_height * i_pitch; i_y += i_pitch )
{ {
return; /* Draw until we reach the end of the line */
} for( i_x = 0 ; i_x < p_spu->i_width; i_x += i_len )
var_Get( p_object, "x-start", &val );
p_spu->p_sys->i_x_start = val.i_int;
var_Get( p_object, "y-start", &val );
p_spu->p_sys->i_y_start = val.i_int;
var_Get( p_object, "x-end", &val );
p_spu->p_sys->i_x_end = val.i_int;
var_Get( p_object, "y-end", &val );
p_spu->p_sys->i_y_end = val.i_int;
#if 0
if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
{ {
p_spu->p_sys->pi_color[0] = ((uint8_t *)val.p_address)[0]; /* Get the RLE part, then draw the line */
p_spu->p_sys->pi_color[1] = ((uint8_t *)val.p_address)[1]; i_color = *p_source & 0x3;
p_spu->p_sys->pi_color[2] = ((uint8_t *)val.p_address)[2]; i_len = *p_source++ >> 2;
p_spu->p_sys->pi_color[3] = ((uint8_t *)val.p_address)[3]; memset( p_p + i_x + i_y, i_color, i_len );
} }
#endif
if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
{
p_spu->p_sys->pi_alpha[0] = ((uint8_t *)val.p_address)[0];
p_spu->p_sys->pi_alpha[1] = ((uint8_t *)val.p_address)[1];
p_spu->p_sys->pi_alpha[2] = ((uint8_t *)val.p_address)[2];
p_spu->p_sys->pi_alpha[3] = ((uint8_t *)val.p_address)[3];
} }
} }
/*****************************************************************************
* CropCallback: called when the highlight properties are changed
*****************************************************************************
* This callback is called from the input thread when we need cropping
*****************************************************************************/
static int CropCallback( vlc_object_t *p_object, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
UpdateSPU( (subpicture_t *)p_data, p_object );
return VLC_SUCCESS;
}
/*****************************************************************************
* render.c : SPU renderer
*****************************************************************************
* Copyright (C) 2000-2001 VideoLAN
* $Id$
*
* Authors: Samuel Hocevar <sam@zoy.org>
* Rudolf Cornelissen <rag.cornelissen@inter.nl.net>
* Roine Gustafsson <roine@popstar.com>
*
* This program 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.
*
* This program 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, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "spudec.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
static void RenderRV16( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
static void RenderRV32( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
/*****************************************************************************
* RenderSPU: draw an SPU on a picture
*****************************************************************************
* This is a fast implementation of the subpicture drawing code. The data
* has been preprocessed once, so we don't need to parse the RLE buffer again
* and again. Most sanity checks are already done so that this routine can be
* as fast as possible.
*****************************************************************************/
void E_(RenderSPU)( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu )
{
switch( p_vout->output.i_chroma )
{
/* I420 target, no scaling */
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
RenderI420( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* RV16 target, scaling */
case VLC_FOURCC('R','V','1','6'):
RenderRV16( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* RV32 target, scaling */
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
RenderRV32( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* NVidia overlay, no scaling */
case VLC_FOURCC('Y','U','Y','2'):
RenderYUY2( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
default:
msg_Err( p_vout, "unknown chroma, can't render SPU" );
break;
}
}
/* Following functions are local */
static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint8_t *p_dest, *p_dest_u, *p_dest_v;
uint8_t *p_destptr;
uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
int i_x, i_y, i_y_u, i_y_v;
int i_len, i_color, i_subsample;
uint16_t i_colprecomp, i_destalpha;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
p_dest = p_pic->Y_PIXELS + p_spu->i_x + p_spu->i_width
+ p_pic->Y_PITCH * ( p_spu->i_y + p_spu->i_height );
p_dest_u = p_pic->U_PIXELS + p_spu->i_x / 2 + p_spu->i_width / 2
+ p_pic->U_PITCH * ( p_spu->i_y + p_spu->i_height ) / 2;
p_dest_v = p_pic->V_PIXELS + p_spu->i_x / 2 + p_spu->i_width / 2
+ p_pic->V_PITCH * ( p_spu->i_y + p_spu->i_height ) / 2;
#if 0 /* Fix vouts to handle YV12 properly */
if( p_vout->output.i_chroma == VLC_FOURCC('Y','V','1','2') )
{
/* Assumes U_PITCH == V_PITCH */
uint8_t *p_tmp = p_dest_u;
p_dest_u = p_dest_v;
p_dest_v = p_tmp;
}
#endif
i_subsample = (p_spu->i_y + p_spu->i_height) & 0x1 ? 0 : 1;
i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
i_y_start = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_end );
i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
i_y_end = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_start );
/* Draw until we reach the bottom of the subtitle */
for( i_y = p_spu->i_height * p_pic->Y_PITCH,
i_y_u = (p_spu->i_height + i_subsample) * p_pic->U_PITCH / 2,
i_y_v = (p_spu->i_height + i_subsample) * p_pic->V_PITCH / 2 ;
i_y ;
i_y -= p_pic->Y_PITCH,
i_y_u -= (p_pic->U_PITCH / 2),
i_y_v -= (p_pic->V_PITCH / 2), i_subsample++ )
{
/* Draw until we reach the end of the line */
for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = *p_source++ >> 2;
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[i_color] )
{
case 0x00:
break;
case 0x0f:
memset( p_dest - i_x - i_y,
p_spu->p_sys->pi_yuv[i_color][0], i_len );
if ( i_subsample & 0x1 ) break;
/* U and V */
memset( p_dest_u - i_x/2 - i_y_u,
p_spu->p_sys->pi_yuv[i_color][1], (i_len+1)/2 );
memset( p_dest_v - i_x/2 - i_y_v,
p_spu->p_sys->pi_yuv[i_color][2], (i_len+1)/2 );
break;
default:
/* To be able to divide by 16 (>>4) we add 1 to the alpha.
* This means Alpha 0 won't be completely transparent, but
* that's handled in a special case above anyway. */
i_colprecomp = (uint16_t)p_spu->p_sys->pi_yuv[i_color][0]
* (uint16_t)(p_spu->p_sys->pi_alpha[i_color] + 1);
i_destalpha = 15 - p_spu->p_sys->pi_alpha[i_color];
for ( p_destptr = p_dest - i_x - i_y;
p_destptr < p_dest - i_x - i_y + i_len;
p_destptr++ )
{
*p_destptr = ( i_colprecomp +
(uint16_t)*p_destptr * i_destalpha ) >> 4;
}
if ( i_subsample & 0x1 ) break;
/* U and V */
i_colprecomp = (uint16_t)p_spu->p_sys->pi_yuv[i_color][1]
* (uint16_t)(p_spu->p_sys->pi_alpha[i_color] + 1);
i_destalpha = 15 - p_spu->p_sys->pi_alpha[i_color];
for ( p_destptr = p_dest_u - i_x/2 - i_y_u;
p_destptr < p_dest_u - i_x/2 - i_y_u + (i_len+1)/2;
p_destptr++ )
{
*p_destptr = ( i_colprecomp +
(uint16_t)*p_destptr * i_destalpha ) >> 4;
}
i_colprecomp = (uint16_t)p_spu->p_sys->pi_yuv[i_color][2]
* (uint16_t)(p_spu->p_sys->pi_alpha[i_color] + 1);
i_destalpha = 15 - p_spu->p_sys->pi_alpha[i_color];
for ( p_destptr = p_dest_v - i_x/2 - i_y_v;
p_destptr < p_dest_v - i_x/2 - i_y_v + (i_len+1)/2;
p_destptr++ )
{
*p_destptr = ( i_colprecomp +
(uint16_t)*p_destptr * i_destalpha ) >> 4;
}
break;
}
}
}
}
static void RenderRV16( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint16_t p_clut16[4];
uint8_t *p_dest;
uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
int i_x, i_y;
int i_len, i_color;
/* RGB-specific */
int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
/* XXX: this is a COMPLETE HACK, memcpy is unable to do u16s anyway */
/* FIXME: get this from the DVD */
for( i_color = 0; i_color < 4; i_color++ )
{
p_clut16[i_color] = 0x1111
* ( (uint16_t)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
}
i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
i_width = p_spu->i_width * i_xscale;
i_height = p_spu->i_height * i_yscale;
p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 2
/* Add the picture coordinates and the SPU coordinates */
+ ( (p_spu->i_x * i_xscale) >> 6 ) * 2
+ ( (p_spu->i_y * i_yscale) >> 6 ) * p_pic->p->i_pitch;
i_x_start = i_width - i_xscale * p_spu->p_sys->i_x_end;
i_y_start = i_yscale * p_spu->p_sys->i_y_start;
i_x_end = i_width - i_xscale * p_spu->p_sys->i_x_start;
i_y_end = i_yscale * p_spu->p_sys->i_y_end;
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0 ; i_y < i_height ; )
{
i_ytmp = i_y >> 6;
i_y += i_yscale;
/* Check whether we need to draw one line or more than one */
if( i_ytmp + 1 >= ( i_y >> 6 ) )
{
/* Just one line : we precalculate i_y >> 6 */
i_yreal = p_pic->p->i_pitch * i_ytmp;
/* Draw until we reach the end of the line */
for( i_x = i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = i_xscale * ( *p_source++ >> 2 );
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
p_clut16[ i_color ],
2 * ( ( i_len >> 6 ) + 1 ) );
break;
default:
/* FIXME: we should do transparency */
memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
p_clut16[ i_color ],
2 * ( ( i_len >> 6 ) + 1 ) );
break;
}
}
}
else
{
i_yreal = p_pic->p->i_pitch * i_ytmp;
i_ynext = p_pic->p->i_pitch * i_y >> 6;
/* Draw until we reach the end of the line */
for( i_x = i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw as many lines as needed */
i_color = *p_source & 0x3;
i_len = i_xscale * ( *p_source++ >> 2 );
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
i_ytmp += p_pic->p->i_pitch )
{
memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
p_clut16[ i_color ],
2 * ( ( i_len >> 6 ) + 1 ) );
}
break;
default:
/* FIXME: we should do transparency */
for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
i_ytmp += p_pic->p->i_pitch )
{
memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
p_clut16[ i_color ],
2 * ( ( i_len >> 6 ) + 1 ) );
}
break;
}
}
}
}
}
static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint32_t p_clut32[4];
uint8_t *p_dest;
uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
int i_x, i_y;
int i_len, i_color;
/* RGB-specific */
int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
/* XXX: this is a COMPLETE HACK, memcpy is unable to do u32s anyway */
/* FIXME: get this from the DVD */
for( i_color = 0; i_color < 4; i_color++ )
{
p_clut32[i_color] = 0x11111111
* ( (uint16_t)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
}
i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
i_width = p_spu->i_width * i_xscale;
i_height = p_spu->i_height * i_yscale;
i_x_start = i_width - i_xscale * p_spu->p_sys->i_x_end;
i_y_start = i_yscale * p_spu->p_sys->i_y_start;
i_x_end = i_width - i_xscale * p_spu->p_sys->i_x_start;
i_y_end = i_yscale * p_spu->p_sys->i_y_end;
p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 4
/* Add the picture coordinates and the SPU coordinates */
+ ( (p_spu->i_x * i_xscale) >> 6 ) * 4
+ ( (p_spu->i_y * i_yscale) >> 6 ) * p_pic->p->i_pitch;
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0 ; i_y < i_height ; )
{
i_ytmp = i_y >> 6;
i_y += i_yscale;
/* Check whether we need to draw one line or more than one */
if( i_ytmp + 1 >= ( i_y >> 6 ) )
{
/* Just one line : we precalculate i_y >> 6 */
i_yreal = p_pic->p->i_pitch * i_ytmp;
/* Draw until we reach the end of the line */
for( i_x = i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = i_xscale * ( *p_source++ >> 2 );
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
break;
default:
/* FIXME: we should do transparency */
memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
break;
}
}
}
else
{
i_yreal = p_pic->p->i_pitch * i_ytmp;
i_ynext = p_pic->p->i_pitch * i_y >> 6;
/* Draw until we reach the end of the line */
for( i_x = i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw as many lines as needed */
i_color = *p_source & 0x3;
i_len = i_xscale * ( *p_source++ >> 2 );
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
i_ytmp += p_pic->p->i_pitch )
{
memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
p_clut32[ i_color ],
4 * ( ( i_len >> 6 ) + 1 ) );
}
break;
default:
/* FIXME: we should do transparency */
for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
i_ytmp += p_pic->p->i_pitch )
{
memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
p_clut32[ i_color ],
4 * ( ( i_len >> 6 ) + 1 ) );
}
break;
}
}
}
}
}
static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint8_t *p_dest;
uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
int i_x, i_y;
int i_len, i_color;
uint8_t i_cnt;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
p_dest = p_pic->p->p_pixels +
+ ( p_spu->i_y + p_spu->i_height ) * p_pic->p->i_pitch // * bytes per line
+ ( p_spu->i_x + p_spu->i_width ) * 2; // * bytes per pixel
i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
i_y_start = (p_spu->i_height - p_spu->p_sys->i_y_end)
* p_pic->p->i_pitch / 2;
i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
i_y_end = (p_spu->i_height - p_spu->p_sys->i_y_start)
* p_pic->p->i_pitch / 2;
/* Draw until we reach the bottom of the subtitle */
for( i_y = p_spu->i_height * p_pic->p->i_pitch / 2;
i_y ;
i_y -= p_pic->p->i_pitch / 2 )
{
/* Draw until we reach the end of the line */
for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = *p_source++ >> 2;
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
{
/* draw a pixel */
/* Y */
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
p_spu->p_sys->pi_yuv[i_color][0], 1);
if (!(i_cnt & 0x01))
{
/* U and V */
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
0x80, 1);
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
0x80, 1);
}
}
break;
default:
/* FIXME: we should do transparency */
for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
{
/* draw a pixel */
/* Y */
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
p_spu->p_sys->pi_yuv[i_color][0], 1);
if (!(i_cnt & 0x01))
{
/* U and V */
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
0x80, 1);
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
0x80, 1);
}
}
break;
}
}
}
}
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
* Preamble * Preamble
*****************************************************************************/ *****************************************************************************/
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h> #include <vlc/decoder.h>
#include "spudec.h" #include "spudec.h"
...@@ -36,7 +35,6 @@ ...@@ -36,7 +35,6 @@
*****************************************************************************/ *****************************************************************************/
static int DecoderOpen ( vlc_object_t * ); static int DecoderOpen ( vlc_object_t * );
static int PacketizerOpen( vlc_object_t * ); static int PacketizerOpen( vlc_object_t * );
static void Close ( vlc_object_t * ); static void Close ( vlc_object_t * );
vlc_module_begin(); vlc_module_begin();
...@@ -53,12 +51,9 @@ vlc_module_end(); ...@@ -53,12 +51,9 @@ vlc_module_end();
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
static vout_thread_t *FindVout( decoder_t *); static block_t * Reassemble( decoder_t *, block_t ** );
static subpicture_t * Decode ( decoder_t *, block_t ** );
static block_t *Reassemble( decoder_t *, block_t ** ); static block_t * Packetize ( decoder_t *, block_t ** );
static void Decode ( decoder_t *, block_t ** );
static block_t *Packetize( decoder_t *, block_t ** );
/***************************************************************************** /*****************************************************************************
* DecoderOpen * DecoderOpen
...@@ -83,7 +78,6 @@ static int DecoderOpen( vlc_object_t *p_this ) ...@@ -83,7 +78,6 @@ static int DecoderOpen( vlc_object_t *p_this )
p_sys->i_spu_size = 0; p_sys->i_spu_size = 0;
p_sys->i_spu = 0; p_sys->i_spu = 0;
p_sys->p_block = NULL; p_sys->p_block = NULL;
p_sys->p_vout = NULL;
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) ); es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
...@@ -120,71 +114,39 @@ static void Close( vlc_object_t *p_this ) ...@@ -120,71 +114,39 @@ static void Close( vlc_object_t *p_this )
decoder_t *p_dec = (decoder_t*)p_this; decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
if( !p_sys->b_packetizer ) if( p_sys->p_block ) block_ChainRelease( p_sys->p_block );
{
/* FIXME check if it's ok to not lock vout */
if( p_sys->p_vout != NULL && p_sys->p_vout->p_subpicture != NULL )
{
subpicture_t * p_subpic;
int i_subpic;
for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
{
p_subpic = &p_sys->p_vout->p_subpicture[i_subpic];
if( p_subpic != NULL &&
( ( p_subpic->i_status == RESERVED_SUBPICTURE ) ||
( p_subpic->i_status == READY_SUBPICTURE ) ) )
{
vout_DestroySubPicture( p_sys->p_vout, p_subpic );
}
}
}
}
if( p_sys->p_block )
{
block_ChainRelease( p_sys->p_block );
}
free( p_sys ); free( p_sys );
} }
/***************************************************************************** /*****************************************************************************
* Decode: * Decode:
*****************************************************************************/ *****************************************************************************/
static void Decode( decoder_t *p_dec, block_t **pp_block ) static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
{ {
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_spu = Reassemble( p_dec, pp_block ); block_t *p_spu_block;
vout_thread_t *p_last_vout = p_dec->p_sys->p_vout;
if( p_spu ) if( (p_spu_block = Reassemble( p_dec, pp_block )) )
{ {
p_sys->i_spu = block_ChainExtract( p_spu, p_sys->buffer, 65536 ); subpicture_t *p_spu;
p_sys->i_pts = p_spu->i_pts;
block_ChainRelease( p_spu );
if( ( p_sys->p_vout = FindVout( p_dec ) ) ) p_sys->i_spu = block_ChainExtract( p_spu_block, p_sys->buffer, 65536 );
{ p_sys->i_pts = p_spu_block->i_pts;
if( p_last_vout != p_sys->p_vout ) block_ChainRelease( p_spu_block );
{
p_sys->i_subpic_channel =
vout_RegisterOSDChannel( p_sys->p_vout );
}
/* Parse and decode */ /* Parse and decode */
E_(ParsePacket)( p_dec ); p_spu = E_(ParsePacket)( p_dec );
vlc_object_release( p_sys->p_vout );
}
/* reinit context */ /* reinit context */
p_sys->i_spu_size = 0; p_sys->i_spu_size = 0;
p_sys->i_rle_size = 0; p_sys->i_rle_size = 0;
p_sys->i_spu = 0; p_sys->i_spu = 0;
p_sys->p_block = NULL; p_sys->p_block = NULL;
return p_spu;
} }
return NULL;
} }
/***************************************************************************** /*****************************************************************************
...@@ -208,6 +170,7 @@ static block_t *Packetize( decoder_t *p_dec, block_t **pp_block ) ...@@ -208,6 +170,7 @@ static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
return block_ChainGather( p_spu ); return block_ChainGather( p_spu );
} }
return NULL; return NULL;
} }
...@@ -219,17 +182,16 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block ) ...@@ -219,17 +182,16 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block )
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_block; block_t *p_block;
if( pp_block == NULL || *pp_block == NULL ) if( pp_block == NULL || *pp_block == NULL ) return NULL;
{
return NULL;
}
p_block = *pp_block; p_block = *pp_block;
*pp_block = NULL; *pp_block = NULL;
if( p_sys->i_spu_size <= 0 && ( p_block->i_pts <= 0 || p_block->i_buffer < 4 ) ) if( p_sys->i_spu_size <= 0 &&
( p_block->i_pts <= 0 || p_block->i_buffer < 4 ) )
{ {
msg_Dbg( p_dec, "invalid starting packet (size < 4 or pts <=0)" ); msg_Dbg( p_dec, "invalid starting packet (size < 4 or pts <=0)" );
msg_Dbg( p_dec, "spu size: %d, i_pts: "I64Fd" i_buffer: %d", p_sys->i_spu_size, p_block->i_pts, p_block->i_buffer ); msg_Dbg( p_dec, "spu size: %d, i_pts: "I64Fd" i_buffer: %d",
p_sys->i_spu_size, p_block->i_pts, p_block->i_buffer );
block_Release( p_block ); block_Release( p_block );
return NULL; return NULL;
} }
...@@ -239,11 +201,14 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block ) ...@@ -239,11 +201,14 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block )
if( p_sys->i_spu_size <= 0 ) if( p_sys->i_spu_size <= 0 )
{ {
p_sys->i_spu_size = ( p_block->p_buffer[0] << 8 )| p_block->p_buffer[1]; p_sys->i_spu_size = ( p_block->p_buffer[0] << 8 )|
p_sys->i_rle_size = ( ( p_block->p_buffer[2] << 8 )| p_block->p_buffer[3] ) - 4; p_block->p_buffer[1];
p_sys->i_rle_size = ( ( p_block->p_buffer[2] << 8 )|
p_block->p_buffer[3] ) - 4;
/* msg_Dbg( p_dec, "i_spu_size=%d i_rle=%d", /* msg_Dbg( p_dec, "i_spu_size=%d i_rle=%d",
p_sys->i_spu_size, p_sys->i_rle_size ); */ p_sys->i_spu_size, p_sys->i_rle_size ); */
if( p_sys->i_spu_size <= 0 || p_sys->i_rle_size >= p_sys->i_spu_size ) if( p_sys->i_spu_size <= 0 || p_sys->i_rle_size >= p_sys->i_spu_size )
{ {
p_sys->i_spu_size = 0; p_sys->i_spu_size = 0;
...@@ -266,34 +231,3 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block ) ...@@ -266,34 +231,3 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block )
} }
return NULL; return NULL;
} }
/* following functions are local */
/*****************************************************************************
* FindVout: Find a vout or wait for one to be created.
*****************************************************************************/
static vout_thread_t *FindVout( decoder_t *p_dec )
{
vout_thread_t *p_vout = NULL;
/* Find an available video output */
do
{
if( p_dec->b_die || p_dec->b_error )
{
break;
}
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
break;
}
msleep( VOUT_OUTMEM_SLEEP );
}
while( 1 );
return p_vout;
}
...@@ -30,16 +30,13 @@ struct decoder_sys_t ...@@ -30,16 +30,13 @@ struct decoder_sys_t
int i_rle_size; int i_rle_size;
int i_spu; int i_spu;
int i_subpic_channel;
block_t *p_block; block_t *p_block;
uint8_t buffer[65536 + 20 ]; /* we will never overflow more than 11 bytes if I'm right */ /* We will never overflow more than 11 bytes if I'm right */
uint8_t buffer[65536 + 20 ];
vout_thread_t *p_vout;
}; };
struct subpicture_sys_t typedef struct subpicture_data_t
{ {
mtime_t i_pts; /* presentation timestamp */ mtime_t i_pts; /* presentation timestamp */
...@@ -51,14 +48,7 @@ struct subpicture_sys_t ...@@ -51,14 +48,7 @@ struct subpicture_sys_t
uint8_t pi_alpha[4]; uint8_t pi_alpha[4];
uint8_t pi_yuv[4][3]; uint8_t pi_yuv[4][3];
/* Link to our input */ } subpicture_data_t;
vlc_object_t * p_input;
/* Cropping properties */
vlc_mutex_t lock;
vlc_bool_t b_crop;
unsigned int i_x_start, i_y_start, i_x_end, i_y_end;
};
/***************************************************************************** /*****************************************************************************
* Amount of bytes we GetChunk() in one go * Amount of bytes we GetChunk() in one go
...@@ -80,7 +70,4 @@ struct subpicture_sys_t ...@@ -80,7 +70,4 @@ struct subpicture_sys_t
/***************************************************************************** /*****************************************************************************
* Prototypes * Prototypes
*****************************************************************************/ *****************************************************************************/
void E_(ParsePacket)( decoder_t * ); subpicture_t * E_(ParsePacket)( decoder_t * );
void E_(RenderSPU) ( vout_thread_t *, picture_t *, const subpicture_t * );
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
#include <vlc/vout.h> #include <vlc/vout.h>
#include <vlc/decoder.h> #include <vlc/decoder.h>
#include <osd.h> #include "osd.h"
#include "vlc_filter.h"
#if defined(HAVE_ICONV) #if defined(HAVE_ICONV)
#include <iconv.h> #include <iconv.h>
...@@ -43,13 +44,12 @@ ...@@ -43,13 +44,12 @@
struct decoder_sys_t struct decoder_sys_t
{ {
int i_align; /* Subtitles alignment on the vout */ int i_align; /* Subtitles alignment on the vout */
int i_subpic_channel; /* Subpic channel for subtitles */
vout_thread_t *p_vout; /* last vout used */
#if defined(HAVE_ICONV) #if defined(HAVE_ICONV)
iconv_t iconv_handle; /* handle to iconv instance */ iconv_t iconv_handle; /* handle to iconv instance */
#endif #endif
filter_t *p_render; /* text renderer filter */
}; };
/***************************************************************************** /*****************************************************************************
...@@ -58,11 +58,13 @@ struct decoder_sys_t ...@@ -58,11 +58,13 @@ struct decoder_sys_t
static int OpenDecoder ( vlc_object_t * ); static int OpenDecoder ( vlc_object_t * );
static void CloseDecoder ( vlc_object_t * ); static void CloseDecoder ( vlc_object_t * );
static void DecodeBlock ( decoder_t *, block_t ** ); static subpicture_t *DecodeBlock ( decoder_t *, block_t ** );
static subpicture_t *ParseText ( decoder_t *, block_t * );
static void ParseText ( decoder_t *, block_t *, vout_thread_t * );
static void StripTags ( char * ); static void StripTags ( char * );
static subpicture_t *spu_new_buffer( filter_t * );
static void spu_del_buffer( filter_t *, subpicture_t * );
#define DEFAULT_NAME "System Default" #define DEFAULT_NAME "System Default"
/***************************************************************************** /*****************************************************************************
...@@ -152,8 +154,8 @@ static int OpenDecoder( vlc_object_t *p_this ) ...@@ -152,8 +154,8 @@ static int OpenDecoder( vlc_object_t *p_this )
{ {
msg_Dbg( p_dec, "using character encoding: %s", msg_Dbg( p_dec, "using character encoding: %s",
p_dec->fmt_in.subs.psz_encoding ); p_dec->fmt_in.subs.psz_encoding );
p_sys->iconv_handle = iconv_open( "UTF-8", p_sys->iconv_handle =
p_dec->fmt_in.subs.psz_encoding ); iconv_open( "UTF-8", p_dec->fmt_in.subs.psz_encoding );
} }
else else
{ {
...@@ -186,7 +188,21 @@ static int OpenDecoder( vlc_object_t *p_this ) ...@@ -186,7 +188,21 @@ static int OpenDecoder( vlc_object_t *p_this )
msg_Dbg( p_dec, "no iconv support available" ); msg_Dbg( p_dec, "no iconv support available" );
#endif #endif
p_dec->p_sys->p_vout = NULL; /* Load the text rendering module */
p_sys->p_render = vlc_object_create( p_dec, sizeof(filter_t) );
p_sys->p_render->pf_spu_buffer_new = spu_new_buffer;
p_sys->p_render->pf_spu_buffer_del = spu_del_buffer;
p_sys->p_render->p_owner = (filter_owner_sys_t *)p_dec;
vlc_object_attach( p_sys->p_render, p_dec );
p_sys->p_render->p_module =
module_Need( p_sys->p_render, "text renderer", 0, 0 );
if( p_sys->p_render->p_module == NULL )
{
msg_Warn( p_dec, "no suitable text renderer module" );
vlc_object_detach( p_sys->p_render );
vlc_object_destroy( p_sys->p_render );
p_sys->p_render = NULL;
}
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -196,34 +212,18 @@ static int OpenDecoder( vlc_object_t *p_this ) ...@@ -196,34 +212,18 @@ static int OpenDecoder( vlc_object_t *p_this )
**************************************************************************** ****************************************************************************
* This function must be fed with complete subtitles units. * This function must be fed with complete subtitles units.
****************************************************************************/ ****************************************************************************/
static void DecodeBlock( decoder_t *p_dec, block_t **pp_block ) static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
{ {
vout_thread_t *p_vout; subpicture_t *p_spu;
if( !pp_block || *pp_block == NULL ) if( !pp_block || *pp_block == NULL ) return NULL;
{
return;
}
/* Here we are dealing with text subtitles */ p_spu = ParseText( p_dec, *pp_block );
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
if( p_dec->p_sys->p_vout != p_vout )
{
p_dec->p_sys->i_subpic_channel = vout_RegisterOSDChannel( p_vout );
}
ParseText( p_dec, *pp_block, p_vout );
vlc_object_release( p_vout );
}
else
{
msg_Warn( p_dec, "couldn't find a video output, trashing subtitle" );
}
p_dec->p_sys->p_vout = p_vout;
block_Release( *pp_block ); block_Release( *pp_block );
*pp_block = NULL; *pp_block = NULL;
return p_spu;
} }
/***************************************************************************** /*****************************************************************************
...@@ -233,27 +233,6 @@ static void CloseDecoder( vlc_object_t *p_this ) ...@@ -233,27 +233,6 @@ static void CloseDecoder( vlc_object_t *p_this )
{ {
decoder_t *p_dec = (decoder_t *)p_this; decoder_t *p_dec = (decoder_t *)p_this;
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
vout_thread_t *p_vout;
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout != NULL && p_vout->p_subpicture != NULL )
{
subpicture_t *p_subpic;
int i_subpic;
for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
{
p_subpic = &p_vout->p_subpicture[i_subpic];
if( p_subpic != NULL &&
( p_subpic->i_status == RESERVED_SUBPICTURE
|| p_subpic->i_status == READY_SUBPICTURE ) )
{
vout_DestroySubPicture( p_vout, p_subpic );
}
}
}
if( p_vout ) vlc_object_release( p_vout );
#if defined(HAVE_ICONV) #if defined(HAVE_ICONV)
if( p_sys->iconv_handle != (iconv_t)-1 ) if( p_sys->iconv_handle != (iconv_t)-1 )
...@@ -262,16 +241,25 @@ static void CloseDecoder( vlc_object_t *p_this ) ...@@ -262,16 +241,25 @@ static void CloseDecoder( vlc_object_t *p_this )
} }
#endif #endif
if( p_sys->p_render )
{
if( p_sys->p_render->p_module )
module_Unneed( p_sys->p_render, p_sys->p_render->p_module );
vlc_object_detach( p_sys->p_render );
vlc_object_destroy( p_sys->p_render );
}
free( p_sys ); free( p_sys );
} }
/***************************************************************************** /*****************************************************************************
* ParseText: parse an text subtitle packet and send it to the video output * ParseText: parse an text subtitle packet and send it to the video output
*****************************************************************************/ *****************************************************************************/
static void ParseText( decoder_t *p_dec, block_t *p_block, static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )
vout_thread_t *p_vout )
{ {
decoder_sys_t *p_sys = p_dec->p_sys; decoder_sys_t *p_sys = p_dec->p_sys;
subpicture_t *p_spu = 0;
char *psz_subtitle; char *psz_subtitle;
int i_align_h, i_align_v; int i_align_h, i_align_v;
...@@ -279,14 +267,14 @@ static void ParseText( decoder_t *p_dec, block_t *p_block, ...@@ -279,14 +267,14 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
if( p_block->i_pts == 0 ) if( p_block->i_pts == 0 )
{ {
msg_Warn( p_dec, "subtitle without a date" ); msg_Warn( p_dec, "subtitle without a date" );
return; return NULL;
} }
/* Check validity of packet data */ /* Check validity of packet data */
if( p_block->i_buffer <= 1 || p_block->p_buffer[0] == '\0' ) if( p_block->i_buffer <= 1 || p_block->p_buffer[0] == '\0' )
{ {
msg_Warn( p_dec, "empty subtitle" ); msg_Warn( p_dec, "empty subtitle" );
return; return NULL;
} }
/* Should be resiliant against bad subtitles */ /* Should be resiliant against bad subtitles */
...@@ -314,9 +302,11 @@ static void ParseText( decoder_t *p_dec, block_t *p_block, ...@@ -314,9 +302,11 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
if( inbytes_left ) if( inbytes_left )
{ {
msg_Warn( p_dec, "Failed to convert subtitle encoding, dropping subtitle.\nTry setting a different character-encoding for the subtitle." ); msg_Warn( p_dec, "Failed to convert subtitle encoding, "
"dropping subtitle.\nTry setting a different "
"character-encoding for the subtitle." );
free( psz_subtitle ); free( psz_subtitle );
return; return NULL;
} }
else else
{ {
...@@ -329,7 +319,8 @@ static void ParseText( decoder_t *p_dec, block_t *p_block, ...@@ -329,7 +319,8 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
if( p_dec->fmt_in.i_codec == VLC_FOURCC('s','s','a',' ') ) if( p_dec->fmt_in.i_codec == VLC_FOURCC('s','s','a',' ') )
{ {
/* Decode SSA strings */ /* Decode SSA strings */
/* We expect: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */ /* We expect: ReadOrder, Layer, Style, Name, MarginL, MarginR,
* MarginV, Effect, Text */
char *psz_new_subtitle; char *psz_new_subtitle;
char *psz_buffer_sub; char *psz_buffer_sub;
int i_comma; int i_comma;
...@@ -352,16 +343,19 @@ static void ParseText( decoder_t *p_dec, block_t *p_block, ...@@ -352,16 +343,19 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
i_text = 0; i_text = 0;
while( psz_buffer_sub[0] != '\0' ) while( psz_buffer_sub[0] != '\0' )
{ {
if( psz_buffer_sub[0] == '\\' && ( psz_buffer_sub[1] =='n' || psz_buffer_sub[1] =='N' ) ) if( psz_buffer_sub[0] == '\\' && ( psz_buffer_sub[1] == 'n' ||
psz_buffer_sub[1] == 'N' ) )
{ {
psz_new_subtitle[i_text] = '\n'; psz_new_subtitle[i_text] = '\n';
i_text++; i_text++;
psz_buffer_sub += 2; psz_buffer_sub += 2;
} }
else if( psz_buffer_sub[0] == '{' && psz_buffer_sub[1] == '\\' ) else if( psz_buffer_sub[0] == '{' &&
psz_buffer_sub[1] == '\\' )
{ {
/* SSA control code */ /* SSA control code */
while( psz_buffer_sub[0] != '\0' && psz_buffer_sub[0] != '}' ) while( psz_buffer_sub[0] != '\0' &&
psz_buffer_sub[0] != '}' )
{ {
psz_buffer_sub++; psz_buffer_sub++;
} }
...@@ -380,13 +374,33 @@ static void ParseText( decoder_t *p_dec, block_t *p_block, ...@@ -380,13 +374,33 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
break; break;
} }
} }
StripTags( psz_subtitle ); StripTags( psz_subtitle );
vout_ShowTextAbsolute( p_vout, p_sys->i_subpic_channel, psz_subtitle, NULL,
OSD_ALIGN_BOTTOM | p_sys->i_align, i_align_h, if( p_sys->p_render && p_sys->p_render->p_module &&
i_align_v, p_block->i_pts, p_sys->p_render->pf_render_string )
p_block->i_length ? p_block->i_pts + p_block->i_length : 0 ); {
block_t *p_new_block = block_New( p_dec, strlen(psz_subtitle) + 1 );
if( p_new_block )
{
memcpy( p_new_block->p_buffer, psz_subtitle,
p_new_block->i_buffer );
p_new_block->i_pts = p_new_block->i_dts = p_block->i_pts;
p_new_block->i_length = p_block->i_length;
p_spu = p_sys->p_render->pf_render_string( p_sys->p_render,
p_new_block );
}
}
if( p_spu )
{
p_spu->i_flags = OSD_ALIGN_BOTTOM | p_sys->i_align;
p_spu->i_x = i_align_h;
p_spu->i_y = i_align_v;
}
free( psz_subtitle ); free( psz_subtitle );
return p_spu;
} }
static void StripTags( char *psz_text ) static void StripTags( char *psz_text )
...@@ -431,3 +445,18 @@ static void StripTags( char *psz_text ) ...@@ -431,3 +445,18 @@ static void StripTags( char *psz_text )
} }
psz_text[ i - i_left_moves ] = '\0'; psz_text[ i - i_left_moves ] = '\0';
} }
/*****************************************************************************
* Buffers allocation callbacks for the filters
*****************************************************************************/
static subpicture_t *spu_new_buffer( filter_t *p_filter )
{
decoder_t *p_dec = (decoder_t *)p_filter->p_owner;
return p_dec->pf_spu_buffer_new( p_dec );
}
static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_spu )
{
decoder_t *p_dec = (decoder_t *)p_filter->p_owner;
p_dec->pf_spu_buffer_del( p_dec, p_spu );
}
...@@ -23,22 +23,19 @@ ...@@ -23,22 +23,19 @@
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <vlc/vout.h> #include <vlc/vout.h>
#include "vlc_block.h"
#include "vlc_filter.h"
static subpicture_t * AddText( vout_thread_t *, int, char *, text_style_t *, static subpicture_t *RenderText( filter_t *, block_t * );
int, int, int, mtime_t, mtime_t );
int E_(OpenRenderer)( vlc_object_t *p_this ) int E_(OpenRenderer)( vlc_object_t *p_this )
{ {
vout_thread_t *p_vout = (vout_thread_t *)p_this; filter_t *p_filter = (filter_t *)p_this;
p_vout->pf_add_string = AddText; p_filter->pf_render_string = RenderText;
return VLC_SUCCESS; return VLC_SUCCESS;
} }
static subpicture_t * AddText( vout_thread_t *p_vout, int i_channel, static subpicture_t *RenderText( filter_t *p_filter, block_t *p_block )
char *psz_string, text_style_t *p_style , int i_flags,
int i_x_margin, int i_y_margin, mtime_t i_start,
mtime_t i_stop )
{ {
return VLC_SUCCESS; return NULL;
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* $Id$ * $Id$
* *
* Authors: Sigmund Augdal <sigmunau@idi.ntnu.no> * Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
* Gildas Bazin <gbazin@videolan.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -33,7 +34,10 @@ ...@@ -33,7 +34,10 @@
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <vlc/vout.h> #include <vlc/vout.h>
#include <osd.h> #include "osd.h"
#include "vlc_block.h"
#include "vlc_filter.h"
#include <math.h> #include <math.h>
#ifdef HAVE_ERRNO_H #ifdef HAVE_ERRNO_H
...@@ -67,20 +71,9 @@ typedef struct line_desc_t line_desc_t; ...@@ -67,20 +71,9 @@ typedef struct line_desc_t line_desc_t;
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
static int Create ( vlc_object_t * ); static int Create ( vlc_object_t * );
static void Destroy ( vlc_object_t * ); static void Destroy( vlc_object_t * );
static void Render ( vout_thread_t *, picture_t *,
const subpicture_t * );
static void RenderI420( vout_thread_t *, picture_t *,
const subpicture_t * );
static void RenderYUY2( vout_thread_t *, picture_t *,
const subpicture_t * );
static void RenderRV32( vout_thread_t *, picture_t *,
const subpicture_t * );
static subpicture_t *AddText ( vout_thread_t *, int, char *, text_style_t *,
int, int, int, mtime_t, mtime_t );
static void FreeString( subpicture_t * ); static subpicture_t *RenderText( filter_t *, block_t * );
#if !defined(HAVE_ICONV) #if !defined(HAVE_ICONV)
static int GetUnicodeCharFromUTF8( byte_t ** ); static int GetUnicodeCharFromUTF8( byte_t ** );
...@@ -123,17 +116,15 @@ vlc_module_end(); ...@@ -123,17 +116,15 @@ vlc_module_end();
/** /**
* Private data in a subpicture. Describes a string. * Private data in a subpicture. Describes a string.
*/ */
struct subpicture_sys_t typedef struct subpicture_data_t
{ {
int i_x_margin;
int i_y_margin;
int i_width; int i_width;
int i_height; int i_height;
int i_flags;
/** The string associated with this subpicture */ /** The string associated with this subpicture */
byte_t *psz_text; byte_t *psz_text;
line_desc_t *p_lines; line_desc_t *p_lines;
};
} subpicture_data_t;
struct line_desc_t struct line_desc_t
{ {
...@@ -146,17 +137,19 @@ struct line_desc_t ...@@ -146,17 +137,19 @@ struct line_desc_t
line_desc_t *p_next; line_desc_t *p_next;
}; };
static void Render ( filter_t *, subpicture_t *, subpicture_data_t * );
static void FreeString( subpicture_data_t * );
/***************************************************************************** /*****************************************************************************
* text_renderer_sys_t: freetype local data * filter_sys_t: freetype local data
***************************************************************************** *****************************************************************************
* This structure is part of the video output thread descriptor. * This structure is part of the video output thread descriptor.
* It describes the freetype specific properties of an output thread. * It describes the freetype specific properties of an output thread.
*****************************************************************************/ *****************************************************************************/
struct text_renderer_sys_t struct filter_sys_t
{ {
FT_Library p_library; /* handle to library */ FT_Library p_library; /* handle to library */
FT_Face p_face; /* handle to face object */ FT_Face p_face; /* handle to face object */
vlc_mutex_t *lock;
vlc_bool_t i_use_kerning; vlc_bool_t i_use_kerning;
uint8_t pi_gamma[256]; uint8_t pi_gamma[256];
}; };
...@@ -169,36 +162,39 @@ struct text_renderer_sys_t ...@@ -169,36 +162,39 @@ struct text_renderer_sys_t
#define gamma_value 2.0 #define gamma_value 2.0
static int Create( vlc_object_t *p_this ) static int Create( vlc_object_t *p_this )
{ {
vout_thread_t *p_vout = (vout_thread_t *)p_this; filter_t *p_filter = (filter_t *)p_this;
char *psz_fontfile; filter_sys_t *p_sys;
char *psz_fontfile = NULL;
int i, i_error; int i, i_error;
int i_fontsize = 0; int i_fontsize = 0;
double gamma_inv = 1.0f / gamma_value; double gamma_inv = 1.0f / gamma_value;
vlc_value_t val; vlc_value_t val;
/* Allocate structure */ /* Allocate structure */
p_vout->p_text_renderer_data = malloc( sizeof( text_renderer_sys_t ) ); p_sys = malloc( sizeof( filter_sys_t ) );
if( p_vout->p_text_renderer_data == NULL ) if( !p_sys )
{ {
msg_Err( p_vout, "out of memory" ); msg_Err( p_filter, "out of memory" );
return VLC_ENOMEM; return VLC_ENOMEM;
} }
p_sys->p_face = 0;
p_sys->p_library = 0;
for (i = 0; i < 256; i++) { for( i = 0; i < 256; i++ )
p_vout->p_text_renderer_data->pi_gamma[i] = {
p_sys->pi_gamma[i] =
(uint8_t)( pow( (double)i / 255.0f, gamma_inv) * 255.0f ); (uint8_t)( pow( (double)i / 255.0f, gamma_inv) * 255.0f );
} }
var_Create( p_vout, "freetype-font", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_filter, "freetype-font", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_vout, "freetype-fontsize", var_Create( p_filter, "freetype-fontsize",
VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_vout, "freetype-rel-fontsize", var_Create( p_filter, "freetype-rel-fontsize",
VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
/* Look what method was requested */ /* Look what method was requested */
var_Get( p_vout, "freetype-font", &val ); var_Get( p_filter, "freetype-font", &val );
psz_fontfile = val.psz_string; psz_fontfile = val.psz_string;
if( !psz_fontfile || !*psz_fontfile ) if( !psz_fontfile || !*psz_fontfile )
{ {
if( psz_fontfile ) free( psz_fontfile ); if( psz_fontfile ) free( psz_fontfile );
...@@ -209,78 +205,71 @@ static int Create( vlc_object_t *p_this ) ...@@ -209,78 +205,71 @@ static int Create( vlc_object_t *p_this )
#elif SYS_DARWIN #elif SYS_DARWIN
strcpy( psz_fontfile, DEFAULT_FONT ); strcpy( psz_fontfile, DEFAULT_FONT );
#else #else
msg_Err( p_vout, "user didn't specify a font" ); msg_Err( p_filter, "user didn't specify a font" );
free( p_vout->p_text_renderer_data ); goto error;
return VLC_EGENERIC;
#endif #endif
} }
i_error = FT_Init_FreeType( &p_vout->p_text_renderer_data->p_library ); i_error = FT_Init_FreeType( &p_sys->p_library );
if( i_error ) if( i_error )
{ {
msg_Err( p_vout, "couldn't initialize freetype" ); msg_Err( p_filter, "couldn't initialize freetype" );
free( p_vout->p_text_renderer_data ); goto error;
return VLC_EGENERIC;
} }
i_error = FT_New_Face( p_vout->p_text_renderer_data->p_library, i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
psz_fontfile ? psz_fontfile : "", 0, 0, &p_sys->p_face );
&p_vout->p_text_renderer_data->p_face );
if( i_error == FT_Err_Unknown_File_Format ) if( i_error == FT_Err_Unknown_File_Format )
{ {
msg_Err( p_vout, "file %s have unknown format", psz_fontfile ); msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library ); goto error;
free( p_vout->p_text_renderer_data );
if( psz_fontfile ) free( psz_fontfile );
return VLC_EGENERIC;
} }
else if( i_error ) else if( i_error )
{ {
msg_Err( p_vout, "failed to load font file %s", psz_fontfile ); msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library ); goto error;
free( p_vout->p_text_renderer_data );
if( psz_fontfile ) free( psz_fontfile );
return VLC_EGENERIC;
} }
if( psz_fontfile ) free( psz_fontfile );
i_error = FT_Select_Charmap( p_vout->p_text_renderer_data->p_face, i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
ft_encoding_unicode );
if( i_error ) if( i_error )
{ {
msg_Err( p_vout, "Font has no unicode translation table" ); msg_Err( p_filter, "Font has no unicode translation table" );
FT_Done_Face( p_vout->p_text_renderer_data->p_face ); goto error;
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
free( p_vout->p_text_renderer_data );
return VLC_EGENERIC;
} }
p_vout->p_text_renderer_data->i_use_kerning = p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
FT_HAS_KERNING(p_vout->p_text_renderer_data->p_face);
var_Get( p_vout, "freetype-fontsize", &val );
var_Get( p_filter, "freetype-fontsize", &val );
if( val.i_int ) if( val.i_int )
{ {
i_fontsize = val.i_int; i_fontsize = val.i_int;
} }
else else
{ {
var_Get( p_vout, "freetype-rel-fontsize", &val ); var_Get( p_filter, "freetype-rel-fontsize", &val );
i_fontsize = (int) p_vout->render.i_height / val.i_int; msg_Err( p_filter, "freetype-rel-font currenlty broken, FIXME" );
i_fontsize = (int) /*p_filter->render.i_height*/ 400 / val.i_int;
} }
msg_Dbg( p_vout, "Using fontsize: %i", i_fontsize); msg_Dbg( p_filter, "Using fontsize: %i", i_fontsize);
i_error = FT_Set_Pixel_Sizes( p_vout->p_text_renderer_data->p_face, 0, i_error = FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_fontsize );
i_fontsize );
if( i_error ) if( i_error )
{ {
msg_Err( p_vout, "couldn't set font size to %d", i_fontsize ); msg_Err( p_filter, "couldn't set font size to %d", i_fontsize );
free( p_vout->p_text_renderer_data ); goto error;
return VLC_EGENERIC;
} }
p_vout->pf_add_string = AddText;
if( psz_fontfile ) free( psz_fontfile );
p_filter->pf_render_string = RenderText;
p_filter->p_sys = p_sys;
return VLC_SUCCESS; return VLC_SUCCESS;
error:
if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
if( psz_fontfile ) free( psz_fontfile );
free( p_sys );
return VLC_EGENERIC;
} }
/***************************************************************************** /*****************************************************************************
...@@ -290,10 +279,11 @@ static int Create( vlc_object_t *p_this ) ...@@ -290,10 +279,11 @@ static int Create( vlc_object_t *p_this )
*****************************************************************************/ *****************************************************************************/
static void Destroy( vlc_object_t *p_this ) static void Destroy( vlc_object_t *p_this )
{ {
vout_thread_t *p_vout = (vout_thread_t *)p_this; filter_t *p_filter = (filter_t *)p_this;
FT_Done_Face( p_vout->p_text_renderer_data->p_face ); filter_sys_t *p_sys = p_filter->p_sys;
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library ); FT_Done_Face( p_sys->p_face );
free( p_vout->p_text_renderer_data ); FT_Done_FreeType( p_sys->p_library );
free( p_sys );
} }
/***************************************************************************** /*****************************************************************************
...@@ -301,362 +291,101 @@ static void Destroy( vlc_object_t *p_this ) ...@@ -301,362 +291,101 @@ static void Destroy( vlc_object_t *p_this )
***************************************************************************** *****************************************************************************
* This function merges the previously rendered freetype glyphs into a picture * This function merges the previously rendered freetype glyphs into a picture
*****************************************************************************/ *****************************************************************************/
static void Render( vout_thread_t *p_vout, picture_t *p_pic, static void Render( filter_t *p_filter, subpicture_t *p_spu,
const subpicture_t *p_subpic ) subpicture_data_t *p_string )
{
switch( p_vout->output.i_chroma )
{
/* I420 target, no scaling */
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
RenderI420( p_vout, p_pic, p_subpic );
break;
#if 0
/* RV16 target, scaling */
case VLC_FOURCC('R','V','1','6'):
RenderRV16( p_vout, p_pic, p_subpic );
break;
#endif
/* RV32 target, scaling */
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
RenderRV32( p_vout, p_pic, p_subpic );
break;
/* NVidia or BeOS overlay, no scaling */
case VLC_FOURCC('Y','U','Y','2'):
RenderYUY2( p_vout, p_pic, p_subpic );
break;
default:
msg_Err( p_vout, "unknown chroma, can't render SPU" );
break;
}
}
/**
* Draw a string on a i420 (or similar) picture
*/
static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_subpic )
{ {
subpicture_sys_t *p_string = p_subpic->p_sys; filter_sys_t *p_sys = p_filter->p_sys;
int i_plane, x, y, pen_x, pen_y;
unsigned int i;
line_desc_t *p_line; line_desc_t *p_line;
uint8_t *p_y, *p_u, *p_v, *p_a;
for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; video_format_t fmt;
p_line = p_line->p_next ) int i, x, y, i_pitch;
{
for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ ) /* Create a new subpicture region */
{ memset( &fmt, 0, sizeof(video_format_t) );
uint8_t *p_in = p_pic->p[ i_plane ].p_pixels; fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
int i_pic_pitch = p_pic->p[ i_plane ].i_pitch; fmt.i_aspect = VOUT_ASPECT_FACTOR;
int i_pic_width = p_pic->p[ i_plane ].i_visible_pitch; fmt.i_width = fmt.i_visible_width = p_string->i_width + 2;
fmt.i_height = fmt.i_visible_height = p_string->i_height + 2;
if( i_plane == 0 ) fmt.i_x_offset = fmt.i_y_offset = 0;
{ p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
if( p_string->i_flags & OSD_ALIGN_BOTTOM ) if( !p_spu->p_region )
{ {
pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height - msg_Err( p_filter, "cannot allocate SPU region" );
p_string->i_y_margin; return;
} }
else
{ p_spu->p_region->i_x = p_spu->p_region->i_y = 0;
pen_y = p_string->i_y_margin; p_y = p_spu->p_region->picture.Y_PIXELS;
} p_u = p_spu->p_region->picture.U_PIXELS;
pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6; p_v = p_spu->p_region->picture.V_PIXELS;
p_a = p_spu->p_region->picture.A_PIXELS;
if( p_string->i_flags & OSD_ALIGN_RIGHT ) i_pitch = p_spu->p_region->picture.Y_PITCH;
{
pen_x = i_pic_width - p_line->i_width /* Initialize the region pixels (only the alpha will be changed later) */
- p_string->i_x_margin; memset( p_y, 0x00, i_pitch * p_spu->p_region->fmt.i_height );
} memset( p_u, 0x80, i_pitch * p_spu->p_region->fmt.i_height );
else if( p_string->i_flags & OSD_ALIGN_LEFT ) memset( p_v, 0x80, i_pitch * p_spu->p_region->fmt.i_height );
{ memset( p_a, 0x00, i_pitch * p_spu->p_region->fmt.i_height );
pen_x = p_string->i_x_margin;
} #define pi_gamma p_sys->pi_gamma
else
{ for( p_line = p_string->p_lines; p_line != NULL; p_line = p_line->p_next )
pen_x = i_pic_width / 2 - p_line->i_width / 2 {
+ p_string->i_x_margin; int i_glyph_tmax = 0;
} int i_bitmap_offset, i_offset;
for( i = 0; p_line->pp_glyphs[i] != NULL; i++ ) for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
{ {
FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ]; FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
int i_alpha_offset = -1; i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
int i_offset = ( p_line->p_glyph_pos[ i ].y + pen_y -
p_glyph->top ) * i_pic_pitch +
p_line->p_glyph_pos[ i ].x + p_glyph->left;
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ i_alpha_offset ] ]
#define pixel p_in[ i_offset + x + pen_x ]
for( y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
i_alpha_offset++;
if( alpha == 0 ) continue;
pen_y--; i_offset -= i_pic_pitch;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y++; i_offset += i_pic_pitch; pen_x--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_x += 2;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y++; i_offset += i_pic_pitch; pen_x--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y--; i_offset -= i_pic_pitch;
}
i_offset += i_pic_pitch;
}
i_alpha_offset = -1;
i_offset = ( p_line->p_glyph_pos[ i ].y + pen_y -
p_glyph->top ) * i_pic_pitch +
p_line->p_glyph_pos[ i ].x + p_glyph->left;
for( y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
i_alpha_offset++;
if( alpha == 0 ) continue;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
}
i_offset += i_pic_pitch;
}
#undef alpha
#undef pixel
}
}
else
{
if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
{
pen_y = p_pic->p[i_plane].i_lines - (p_string->i_height>>1)
- (p_string->i_y_margin>>1);
}
else
{
pen_y = p_string->i_y_margin >> 1;
}
pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 7;
if ( p_string->i_flags & OSD_ALIGN_RIGHT )
{
pen_x = i_pic_width - ( p_line->i_width >> 1 )
- ( p_string->i_x_margin >> 1 );
}
else if ( p_string->i_flags & OSD_ALIGN_LEFT )
{
pen_x = p_string->i_x_margin >> 1;
}
else
{
pen_x = i_pic_width / 2 - p_line->i_width / 4
+ p_string->i_x_margin / 2;
} }
for( i = 0; p_line->pp_glyphs[i] != NULL; i++ ) for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
{ {
FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ]; FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ ( x + y * p_glyph->bitmap.width ) ] ]
#define pixel p_in[ ( ( p_line->p_glyph_pos[ i ].y >> 1 ) + pen_y + ( y >> 1 ) - ( p_glyph->top >> 1 ) ) * i_pic_pitch + ( x >> 1 ) + pen_x + ( p_line->p_glyph_pos[ i ].x >> 1 ) + ( p_glyph->left >>1) ]
for( y = 0; y < p_glyph->bitmap.rows; y+=2 )
{
for( x = 0; x < p_glyph->bitmap.width; x+=2 )
{
if( alpha == 0 ) continue;
pixel = ( ( pixel * ( 0xFF - alpha ) ) >> 8 ) +
( 0x80 * alpha >> 8 );
#undef alpha
#undef pixel
}
}
}
}
}
}
}
/** i_offset = ( p_line->p_glyph_pos[ i ].y +
* Draw a string on a YUY2 picture i_glyph_tmax - p_glyph->top + 1 ) *
*/ i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 1;
static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_subpic )
{
subpicture_sys_t *p_string = p_subpic->p_sys;
int x, y, pen_x, pen_y;
unsigned int i;
line_desc_t *p_line;
for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
p_line = p_line->p_next )
{ {
uint8_t *p_in; for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
int i_pic_pitch = p_pic->p[0].i_pitch;
int i_pic_width = p_pic->p[0].i_visible_pitch;
p_in = p_pic->p[0].p_pixels;
if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
{ {
pen_y = p_pic->p[0].i_lines - p_string->i_height - if( !pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]] )
p_string->i_y_margin; continue;
}
else
{
pen_y = p_string->i_y_margin;
}
pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
if ( p_string->i_flags & OSD_ALIGN_RIGHT )
{
pen_x = i_pic_width - p_line->i_width
- p_string->i_x_margin;
}
else if ( p_string->i_flags & OSD_ALIGN_LEFT )
{
pen_x = p_string->i_x_margin;
}
else
{
pen_x = i_pic_width / 2 /2 - p_line->i_width / 2 + p_string->i_x_margin;
}
for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
{
FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
#define pixel p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pic_pitch + 2 * ( x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ) ]
for(y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
if( alpha == 0 ) continue;
pen_y--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y++; pen_x--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_x += 2;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y++; pen_x--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y--;
}
}
for(y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
if( alpha == 0 ) continue;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) + i_offset -= i_pitch;
( 255 * alpha >> 8 ); p_a[i_offset + x] = ((uint16_t)p_a[i_offset + x] +
} pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]])/2;
} i_offset += i_pitch; x--;
#undef alpha p_a[i_offset + x] = ((uint16_t)p_a[i_offset + x] +
#undef pixel pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]])/2;
x += 2;
p_a[i_offset + x] = ((uint16_t)p_a[i_offset + x] +
pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]])/2;
i_offset += i_pitch; x--;
p_a[i_offset + x] = ((uint16_t)p_a[i_offset + x] +
pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]])/2;
i_offset -= i_pitch;
} }
i_offset += i_pitch;
} }
}
/**
* Draw a string on a RV32 picture
*/
static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_subpic )
{
subpicture_sys_t *p_string = p_subpic->p_sys;
int i_plane, x, y, pen_x, pen_y;
unsigned int i;
line_desc_t *p_line;
i_plane = 0;
for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; p_line = p_line->p_next ) i_offset = ( p_line->p_glyph_pos[ i ].y +
{ i_glyph_tmax - p_glyph->top ) *
uint8_t *p_in; i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 1;
int i_pic_pitch = p_pic->p[ i_plane ].i_pitch;
int i_pic_width = p_pic->p[ i_plane ].i_visible_pitch;
p_in = p_pic->p[ i_plane ].p_pixels;
if ( p_string->i_flags & OSD_ALIGN_BOTTOM ) for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
{
pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
p_string->i_y_margin;
}
else
{
pen_y = p_string->i_y_margin;
}
pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
if ( p_string->i_flags & OSD_ALIGN_RIGHT )
{ {
pen_x = i_pic_width - p_line->i_width for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
- p_string->i_x_margin;
}
else if ( p_string->i_flags & OSD_ALIGN_LEFT )
{ {
pen_x = p_string->i_x_margin; p_y[i_offset + x] =
pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]];
} }
else i_offset += i_pitch;
{
pen_x = i_pic_width / 2 / 4 - p_line->i_width / 2
+ p_string->i_x_margin;
} }
for( i = 0; p_line->pp_glyphs[i] != NULL; i++ ) #undef pi_gamma
{
FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
#define pixel( c ) p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pic_pitch + ( x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ) * 4 + c ]
for(y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
if( alpha == 0 ) continue;
pen_y--;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
pen_y++; pen_x--;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
pen_x += 2;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
pen_y++; pen_x--;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
pen_y--;
}
}
for(y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
if( alpha == 0 ) continue;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
}
}
#undef alpha
#undef pixel
} }
} }
} }
...@@ -667,17 +396,16 @@ static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic, ...@@ -667,17 +396,16 @@ static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
* needed glyphs into memory. It is used as pf_add_string callback in * needed glyphs into memory. It is used as pf_add_string callback in
* the vout method by this module * the vout method by this module
*/ */
static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, static subpicture_t *RenderText( filter_t *p_filter, block_t *p_block )
char *psz_string, text_style_t *p_style, int i_flags,
int i_hmargin, int i_vmargin, mtime_t i_start,
mtime_t i_stop )
{ {
subpicture_sys_t *p_string; filter_sys_t *p_sys = p_filter->p_sys;
subpicture_t *p_subpic = 0;
subpicture_data_t *p_string = 0;
line_desc_t *p_line = 0, *p_next = 0;
int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous; int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
subpicture_t *p_subpic;
line_desc_t *p_line, *p_next;
uint32_t *p_unicode_string, i_char; uint32_t *p_unicode_string, i_char;
int i_string_length; int i_string_length;
char *psz_string;
#if defined(HAVE_ICONV) #if defined(HAVE_ICONV)
iconv_t iconv_handle; iconv_t iconv_handle;
...@@ -689,10 +417,9 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -689,10 +417,9 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
FT_Glyph tmp_glyph; FT_Glyph tmp_glyph;
/* Sanity check */ /* Sanity check */
if ( !psz_string || !*psz_string ) if( !p_block ) return NULL;
{ psz_string = p_block->p_buffer;
return NULL; if( !psz_string || !*psz_string ) goto error;
}
result.x = 0; result.x = 0;
result.y = 0; result.y = 0;
...@@ -701,41 +428,22 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -701,41 +428,22 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
line.yMin = 0; line.yMin = 0;
line.yMax = 0; line.yMax = 0;
p_line = 0;
p_string = 0;
p_subpic = 0;
/* Create and initialize a subpicture */ /* Create and initialize a subpicture */
p_subpic = vout_CreateSubPicture( p_vout, i_channel, MEMORY_SUBPICTURE ); p_subpic = p_filter->pf_spu_buffer_new( p_filter );
if ( p_subpic == NULL ) if( !p_subpic ) goto error;
{
return NULL; p_subpic->i_start = p_block->i_pts;
} p_subpic->i_stop = p_block->i_pts + p_block->i_length;
p_subpic->p_sys = 0; p_subpic->b_ephemer = (p_block->i_length == 0);
p_subpic->pf_render = Render; p_subpic->b_absolute = VLC_FALSE;
p_subpic->pf_destroy = FreeString;
p_subpic->i_start = i_start;
p_subpic->i_stop = i_stop;
if( i_stop == 0 )
{
p_subpic->b_ephemer = VLC_TRUE;
}
else
{
p_subpic->b_ephemer = VLC_FALSE;
}
/* Create and initialize private data for the subpicture */ /* Create and initialize private data for the subpicture */
p_string = malloc( sizeof(subpicture_sys_t) ); p_string = malloc( sizeof(subpicture_data_t) );
if ( p_string == NULL ) if( !p_string )
{ {
msg_Err( p_vout, "Out of memory" ); msg_Err( p_filter, "Out of memory" );
goto error; goto error;
} }
p_subpic->p_sys = p_string;
p_string->i_flags = i_flags;
p_string->i_x_margin = i_hmargin;
p_string->i_y_margin = i_vmargin;
p_string->p_lines = 0; p_string->p_lines = 0;
p_string->psz_text = strdup( psz_string ); p_string->psz_text = strdup( psz_string );
...@@ -743,7 +451,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -743,7 +451,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
p_unicode_string = malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) ); p_unicode_string = malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
if( p_unicode_string == NULL ) if( p_unicode_string == NULL )
{ {
msg_Err( p_vout, "Out of memory" ); msg_Err( p_filter, "Out of memory" );
goto error; goto error;
} }
#if defined(WORDS_BIGENDIAN) #if defined(WORDS_BIGENDIAN)
...@@ -753,7 +461,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -753,7 +461,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
#endif #endif
if( iconv_handle == (iconv_t)-1 ) if( iconv_handle == (iconv_t)-1 )
{ {
msg_Warn( p_vout, "Unable to do convertion" ); msg_Warn( p_filter, "Unable to do convertion" );
goto error; goto error;
} }
...@@ -765,24 +473,25 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -765,24 +473,25 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
i_out_bytes_left = i_out_bytes; i_out_bytes_left = i_out_bytes;
p_in_buffer = psz_string; p_in_buffer = psz_string;
p_out_buffer = (char *)p_unicode_string; p_out_buffer = (char *)p_unicode_string;
i_ret = iconv( iconv_handle, &p_in_buffer, &i_in_bytes, &p_out_buffer, &i_out_bytes_left ); i_ret = iconv( iconv_handle, &p_in_buffer, &i_in_bytes,
&p_out_buffer, &i_out_bytes_left );
if( i_in_bytes ) if( i_in_bytes )
{ {
msg_Warn( p_vout, "Failed to convert string to unicode (%s), bytes left %d", strerror(errno), i_in_bytes ); msg_Warn( p_filter, "Failed to convert string to unicode (%s), "
"bytes left %d", strerror(errno), i_in_bytes );
goto error; goto error;
} }
*(uint32_t*)p_out_buffer = 0; *(uint32_t*)p_out_buffer = 0;
i_string_length = ( i_out_bytes - i_out_bytes_left ) / sizeof(uint32_t); i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
} }
#if defined(HAVE_FRIBIDI) #if defined(HAVE_FRIBIDI)
{ {
uint32_t *p_fribidi_string; uint32_t *p_fribidi_string;
FriBidiCharType base_dir = FRIBIDI_TYPE_ON; FriBidiCharType base_dir = FRIBIDI_TYPE_ON;
p_fribidi_string = malloc( ( i_string_length + 1 ) * sizeof(uint32_t) ); p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
fribidi_log2vis( (FriBidiChar*)p_unicode_string, i_string_length, fribidi_log2vis( (FriBidiChar*)p_unicode_string, i_string_length,
&base_dir, (FriBidiChar*)p_fribidi_string, NULL, NULL, &base_dir, (FriBidiChar*)p_fribidi_string, 0, 0, 0 );
NULL );
free( p_unicode_string ); free( p_unicode_string );
p_unicode_string = p_fribidi_string; p_unicode_string = p_fribidi_string;
p_fribidi_string[ i_string_length ] = 0; p_fribidi_string[ i_string_length ] = 0;
...@@ -795,7 +504,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -795,7 +504,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
p_line = NewLine( psz_string ); p_line = NewLine( psz_string );
if( p_line == NULL ) if( p_line == NULL )
{ {
msg_Err( p_vout, "Out of memory" ); msg_Err( p_filter, "Out of memory" );
goto error; goto error;
} }
p_string->p_lines = p_line; p_string->p_lines = p_line;
...@@ -804,23 +513,23 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -804,23 +513,23 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
i_previous = 0; i_previous = 0;
i = 0; i = 0;
#define face p_vout->p_text_renderer_data->p_face #define face p_sys->p_face
#define glyph face->glyph #define glyph face->glyph
while( *p_unicode_string ) while( *p_unicode_string )
{ {
i_char = *p_unicode_string++; i_char = *p_unicode_string++;
if ( i_char == '\r' ) /* ignore CR chars wherever they may be */ if( i_char == '\r' ) /* ignore CR chars wherever they may be */
{ {
continue; continue;
} }
if ( i_char == '\n' ) if( i_char == '\n' )
{ {
p_next = NewLine( psz_string ); p_next = NewLine( psz_string );
if( p_next == NULL ) if( p_next == NULL )
{ {
msg_Err( p_vout, "Out of memory" ); msg_Err( p_filter, "Out of memory" );
goto error; goto error;
} }
p_line->p_next = p_next; p_line->p_next = p_next;
...@@ -836,13 +545,15 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -836,13 +545,15 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
line.yMin = 0; line.yMin = 0;
line.yMax = 0; line.yMax = 0;
i_pen_y += face->size->metrics.height >> 6; i_pen_y += face->size->metrics.height >> 6;
msg_Dbg( p_vout, "Creating new line, i is %d", i ); #if 0
msg_Dbg( p_filter, "Creating new line, i is %d", i );
#endif
i = 0; i = 0;
continue; continue;
} }
i_glyph_index = FT_Get_Char_Index( face, i_char ); i_glyph_index = FT_Get_Char_Index( face, i_char );
if ( p_vout->p_text_renderer_data->i_use_kerning && i_glyph_index if( p_sys->i_use_kerning && i_glyph_index
&& i_previous ) && i_previous )
{ {
FT_Vector delta; FT_Vector delta;
...@@ -854,21 +565,21 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -854,21 +565,21 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
p_line->p_glyph_pos[ i ].x = i_pen_x; p_line->p_glyph_pos[ i ].x = i_pen_x;
p_line->p_glyph_pos[ i ].y = i_pen_y; p_line->p_glyph_pos[ i ].y = i_pen_y;
i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT ); i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
if ( i_error ) if( i_error )
{ {
msg_Err( p_vout, "FT_Load_Glyph returned %d", i_error ); msg_Err( p_filter, "FT_Load_Glyph returned %d", i_error );
goto error; goto error;
} }
i_error = FT_Get_Glyph( glyph, &tmp_glyph ); i_error = FT_Get_Glyph( glyph, &tmp_glyph );
if ( i_error ) if( i_error )
{ {
msg_Err( p_vout, "FT_Get_Glyph returned %d", i_error ); msg_Err( p_filter, "FT_Get_Glyph returned %d", i_error );
goto error; goto error;
} }
FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size ); FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal, i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal,
NULL, 1 ); NULL, 1 );
if ( i_error ) continue; if( i_error ) continue;
p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph; p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
/* Do rest */ /* Do rest */
...@@ -880,6 +591,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -880,6 +591,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
i_pen_x += glyph->advance.x >> 6; i_pen_x += glyph->advance.x >> 6;
i++; i++;
} }
p_line->i_width = line.xMax; p_line->i_width = line.xMax;
p_line->i_height = face->size->metrics.height >> 6; p_line->i_height = face->size->metrics.height >> 6;
p_line->pp_glyphs[ i ] = NULL; p_line->pp_glyphs[ i ] = NULL;
...@@ -887,25 +599,28 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel, ...@@ -887,25 +599,28 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
result.y += line.yMax - line.yMin; result.y += line.yMax - line.yMin;
p_string->i_height = result.y; p_string->i_height = result.y;
p_string->i_width = result.x; p_string->i_width = result.x;
vout_DisplaySubPicture( p_vout, p_subpic );
return p_subpic;
#undef face #undef face
#undef glyph #undef glyph
Render( p_filter, p_subpic, p_string );
FreeString( p_string );
block_Release( p_block );
return p_subpic;
error: error:
FreeString( p_subpic ); FreeString( p_string );
vout_DestroySubPicture( p_vout, p_subpic ); if( p_subpic ) p_filter->pf_spu_buffer_del( p_filter, p_subpic );
block_Release( p_block );
return NULL; return NULL;
} }
static void FreeString( subpicture_t *p_subpic ) static void FreeString( subpicture_data_t *p_string )
{ {
unsigned int i; unsigned int i;
subpicture_sys_t *p_string = p_subpic->p_sys;
line_desc_t *p_line, *p_next; line_desc_t *p_line, *p_next;
if( p_subpic->p_sys == NULL ) return; if( !p_string ) return;
for( p_line = p_string->p_lines; p_line != NULL; p_line = p_next ) for( p_line = p_string->p_lines; p_line != NULL; p_line = p_next )
{ {
......
...@@ -8,4 +8,5 @@ SOURCES_crop = crop.c ...@@ -8,4 +8,5 @@ SOURCES_crop = crop.c
SOURCES_motionblur = motionblur.c SOURCES_motionblur = motionblur.c
SOURCES_logo = logo.c SOURCES_logo = logo.c
SOURCES_deinterlace = deinterlace.c SOURCES_deinterlace = deinterlace.c
SOURCES_blend = blend.c
noinst_HEADERS += filter_common.h noinst_HEADERS += filter_common.h
/*****************************************************************************
* blend.c: alpha blend 2 pictures together
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id$
*
* Author: Gildas Bazin <gbazin@videolan.org>
*
* This program 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.
*
* This program 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, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/decoder.h>
#include "vlc_filter.h"
/*****************************************************************************
* filter_sys_t : filter descriptor
*****************************************************************************/
struct filter_sys_t
{
};
/****************************************************************************
* Local prototypes
****************************************************************************/
static int OpenFilter ( vlc_object_t * );
static void CloseFilter( vlc_object_t * );
static void Blend( filter_t *, picture_t *, picture_t *, picture_t *,
int, int );
static void BlendI420( filter_t *, picture_t *, picture_t *, picture_t *,
int, int );
static void BlendR16( filter_t *, picture_t *, picture_t *, picture_t *,
int, int );
static void BlendPalette( filter_t *, picture_t *, picture_t *, picture_t *,
int, int );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("Video pictures blending") );
set_capability( "video blending", 100 );
set_callbacks( OpenFilter, CloseFilter );
vlc_module_end();
/*****************************************************************************
* OpenFilter: probe the filter and return score
*****************************************************************************/
static int OpenFilter( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t*)p_this;
filter_sys_t *p_sys;
/* Check if we can handle that format.
* We could try to use a chroma filter if we can't. */
if( ( p_filter->fmt_in.video.i_chroma != VLC_FOURCC('Y','U','V','A') &&
p_filter->fmt_in.video.i_chroma != VLC_FOURCC('Y','U','V','P') ) ||
( p_filter->fmt_out.video.i_chroma != VLC_FOURCC('I','4','2','0') &&
p_filter->fmt_out.video.i_chroma != VLC_FOURCC('Y','V','1','2') &&
p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','1','6') ) )
{
return VLC_EGENERIC;
}
/* Allocate the memory needed to store the decoder's structure */
if( ( p_filter->p_sys = p_sys =
(filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
{
msg_Err( p_filter, "out of memory" );
return VLC_EGENERIC;
}
/* Misc init */
p_filter->pf_video_blend = Blend;
msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s",
(char *)&p_filter->fmt_in.video.i_chroma,
(char *)&p_filter->fmt_out.video.i_chroma );
return VLC_SUCCESS;
}
/****************************************************************************
* Blend: the whole thing
****************************************************************************
* This function is called just after the thread is launched.
****************************************************************************/
static void Blend( filter_t *p_filter, picture_t *p_dst,
picture_t *p_dst_orig, picture_t *p_src,
int i_x_offset, int i_y_offset )
{
if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('I','4','2','0') ||
p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ) )
{
BlendI420( p_filter, p_dst, p_dst_orig, p_src,
i_x_offset, i_y_offset );
return;
}
if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','1','6') )
{
BlendR16( p_filter, p_dst, p_dst_orig, p_src,
i_x_offset, i_y_offset );
return;
}
if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','P') &&
( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('I','4','2','0') ||
p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ) )
{
BlendPalette( p_filter, p_dst, p_dst_orig, p_src,
i_x_offset, i_y_offset );
return;
}
msg_Dbg( p_filter, "no matching alpha blending routine" );
}
static void BlendI420( filter_t *p_filter, picture_t *p_dst,
picture_t *p_dst_orig, picture_t *p_src,
int i_x_offset, int i_y_offset )
{
filter_sys_t *p_sys = p_filter->p_sys;
int i_src1_pitch, i_src2_pitch, i_dst_pitch;
uint8_t *p_src1_y, *p_src2_y, *p_dst_y;
uint8_t *p_src1_u, *p_src2_u, *p_dst_u;
uint8_t *p_src1_v, *p_src2_v, *p_dst_v;
uint8_t *p_trans;
int i_width, i_height, i_x, i_y;
vlc_bool_t b_even_scanline = i_y_offset % 2;
i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst->p[Y_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst->p[U_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst->p[V_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst_orig->p[Y_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst_orig->p[U_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst_orig->p[V_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
p_src2_y = p_src->p[Y_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset +
p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
p_src2_u = p_src->p[U_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset/2 +
p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
p_src2_v = p_src->p[V_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset/2 +
p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
p_trans = p_src->p[A_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset +
p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
p_filter->fmt_in.video.i_visible_width );
i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
p_filter->fmt_in.video.i_visible_height );
#define MAX_TRANS 255
#define TRANS_BITS 8
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
p_src2_y += i_src2_pitch,
p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
p_src2_u += i_src2_pitch,
p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0,
p_src2_v += i_src2_pitch )
{
b_even_scanline = !b_even_scanline;
/* Draw until we reach the end of the line */
for( i_x = 0; i_x < i_width; i_x++ )
{
if( !p_trans[i_x] )
{
/* Completely transparent. Don't change pixel */
continue;
}
else if( p_trans[i_x] == MAX_TRANS )
{
/* Completely opaque. Completely overwrite underlying pixel */
p_dst_y[i_x] = p_src2_y[i_x];
if( b_even_scanline && i_x % 2 == 0 )
{
p_dst_u[i_x/2] = p_src2_u[i_x];
p_dst_v[i_x/2] = p_src2_v[i_x];
}
continue;
}
/* Blending */
p_dst_y[i_x] = ( (uint16_t)p_src2_y[i_x] * p_trans[i_x] +
(uint16_t)p_src1_y[i_x] * (MAX_TRANS - p_trans[i_x]) )
>> TRANS_BITS;
if( b_even_scanline && i_x % 2 == 0 )
{
p_dst_u[i_x/2] = ( (uint16_t)p_src2_u[i_x] * p_trans[i_x] +
(uint16_t)p_src1_u[i_x/2] * (MAX_TRANS - p_trans[i_x]) )
>> TRANS_BITS;
p_dst_v[i_x/2] = ( (uint16_t)p_src2_v[i_x] * p_trans[i_x] +
(uint16_t)p_src1_v[i_x/2] * (MAX_TRANS - p_trans[i_x]) )
>> TRANS_BITS;
}
}
}
#undef MAX_TRANS
#undef TRANS_BITS
return;
}
static inline void yuv_to_rgb( int *r, int *g, int *b,
uint8_t y1, uint8_t u1, uint8_t v1 )
{
/* macros used for YUV pixel conversions */
# define SCALEBITS 10
# define ONE_HALF (1 << (SCALEBITS - 1))
# define FIX(x) ((int) ((x) * (1<<SCALEBITS) + 0.5))
# define CLAMP( x ) (((x) > 255) ? 255 : ((x) < 0) ? 0 : (x));
int y, cb, cr, r_add, g_add, b_add;
cb = u1 - 128;
cr = v1 - 128;
r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
g_add = - FIX(0.34414*255.0/224.0) * cb
- FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
y = (y1 - 16) * FIX(255.0/219.0);
*r = CLAMP((y + r_add) >> SCALEBITS);
*g = CLAMP((y + g_add) >> SCALEBITS);
*b = CLAMP((y + b_add) >> SCALEBITS);
}
static void BlendR16( filter_t *p_filter, picture_t *p_dst_pic,
picture_t *p_dst_orig, picture_t *p_src,
int i_x_offset, int i_y_offset )
{
filter_sys_t *p_sys = p_filter->p_sys;
int i_src1_pitch, i_src2_pitch, i_dst_pitch;
uint8_t *p_dst, *p_src1, *p_src2_y;
uint8_t *p_src2_u, *p_src2_v;
uint8_t *p_trans;
int i_width, i_height, i_x, i_y, i_pix_pitch;
int r, g, b;
i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
i_dst_pitch = p_dst_pic->p->i_pitch;
p_dst = p_dst_pic->p->p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst_pic->p->i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
p_src1 = p_dst_orig->p->p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst_orig->p->i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
p_src2_y = p_src->p[Y_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset +
p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
p_src2_u = p_src->p[U_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset/2 +
p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
p_src2_v = p_src->p[V_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset/2 +
p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
p_trans = p_src->p[A_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset +
p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
p_filter->fmt_in.video.i_visible_width );
i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
p_filter->fmt_in.video.i_visible_height );
#define MAX_TRANS 255
#define TRANS_BITS 8
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
p_src2_v += i_src2_pitch )
{
/* Draw until we reach the end of the line */
for( i_x = 0; i_x < i_width; i_x++ )
{
if( !p_trans[i_x] )
{
/* Completely transparent. Don't change pixel */
continue;
}
else if( p_trans[i_x] == MAX_TRANS )
{
/* Completely opaque. Completely overwrite underlying pixel */
yuv_to_rgb( &r, &g, &b,
p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
continue;
}
/* Blending */
yuv_to_rgb( &r, &g, &b,
p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}
}
#undef MAX_TRANS
#undef TRANS_BITS
return;
}
static void BlendPalette( filter_t *p_filter, picture_t *p_dst,
picture_t *p_dst_orig, picture_t *p_src,
int i_x_offset, int i_y_offset )
{
filter_sys_t *p_sys = p_filter->p_sys;
int i_src1_pitch, i_src2_pitch, i_dst_pitch;
uint8_t *p_src1_y, *p_src2, *p_dst_y;
uint8_t *p_src1_u, *p_dst_u;
uint8_t *p_src1_v, *p_dst_v;
int i_width, i_height, i_x, i_y;
vlc_bool_t b_even_scanline = i_y_offset % 2;
i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst->p[Y_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst->p[U_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst->p[V_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst_orig->p[Y_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst_orig->p[U_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst_orig->p[V_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
i_src2_pitch = p_src->p->i_pitch;
p_src2 = p_src->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
i_src2_pitch * p_filter->fmt_in.video.i_y_offset;
i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
p_filter->fmt_in.video.i_visible_width );
i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
p_filter->fmt_in.video.i_visible_height );
#define MAX_TRANS 255
#define TRANS_BITS 8
#define p_trans p_src2
#define p_pal p_filter->fmt_in.video.p_palette->palette
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0; i_y < i_height; i_y++,
p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
p_src2 += i_src2_pitch,
p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0 )
{
b_even_scanline = !b_even_scanline;
/* Draw until we reach the end of the line */
for( i_x = 0; i_x < i_width; i_x++ )
{
if( !p_pal[p_trans[i_x]][3] )
{
/* Completely transparent. Don't change pixel */
continue;
}
else if( p_pal[p_trans[i_x]][3] == MAX_TRANS )
{
/* Completely opaque. Completely overwrite underlying pixel */
p_dst_y[i_x] = p_pal[p_src2[i_x]][0];
if( b_even_scanline && i_x % 2 == 0 )
{
p_dst_u[i_x/2] = p_pal[p_src2[i_x]][1];
p_dst_v[i_x/2] = p_pal[p_src2[i_x]][2];
}
continue;
}
/* Blending */
p_dst_y[i_x] = ( (uint16_t)p_pal[p_src2[i_x]][0] *
p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_y[i_x] *
(MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
if( b_even_scanline && i_x % 2 == 0 )
{
p_dst_u[i_x/2] = ( (uint16_t)p_pal[p_src2[i_x]][1] *
p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_u[i_x/2] *
(MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
p_dst_v[i_x/2] = ( (uint16_t)p_pal[p_src2[i_x]][2] *
p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_v[i_x/2] *
(MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
}
}
}
#undef MAX_TRANS
#undef TRANS_BITS
#undef p_trans
#undef p_pal
return;
}
/*****************************************************************************
* CloseFilter: clean up the filter
*****************************************************************************/
static void CloseFilter( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t*)p_this;
filter_sys_t *p_sys = p_filter->p_sys;
free( p_sys );
}
...@@ -51,6 +51,9 @@ static void vout_del_buffer( decoder_t *, picture_t * ); ...@@ -51,6 +51,9 @@ static void vout_del_buffer( decoder_t *, picture_t * );
static void vout_link_picture( decoder_t *, picture_t * ); static void vout_link_picture( decoder_t *, picture_t * );
static void vout_unlink_picture( decoder_t *, picture_t * ); static void vout_unlink_picture( decoder_t *, picture_t * );
static subpicture_t *spu_new_buffer( decoder_t * );
static void spu_del_buffer( decoder_t *, subpicture_t * );
static es_format_t null_es_format = {0}; static es_format_t null_es_format = {0};
struct decoder_owner_sys_t struct decoder_owner_sys_t
...@@ -64,6 +67,9 @@ struct decoder_owner_sys_t ...@@ -64,6 +67,9 @@ struct decoder_owner_sys_t
vout_thread_t *p_vout; vout_thread_t *p_vout;
vout_thread_t *p_spu_vout;
int i_spu_channel;
sout_instance_t *p_sout; sout_instance_t *p_sout;
sout_packetizer_input_t *p_sout_input; sout_packetizer_input_t *p_sout_input;
...@@ -387,17 +393,19 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, ...@@ -387,17 +393,19 @@ static decoder_t * CreateDecoder( input_thread_t *p_input,
p_dec->p_owner->p_aout = NULL; p_dec->p_owner->p_aout = NULL;
p_dec->p_owner->p_aout_input = NULL; p_dec->p_owner->p_aout_input = NULL;
p_dec->p_owner->p_vout = NULL; p_dec->p_owner->p_vout = NULL;
p_dec->p_owner->p_spu_vout = NULL;
p_dec->p_owner->i_spu_channel = 0;
p_dec->p_owner->p_sout = p_input->p_sout; p_dec->p_owner->p_sout = p_input->p_sout;
p_dec->p_owner->p_sout_input = NULL; p_dec->p_owner->p_sout_input = NULL;
p_dec->p_owner->p_packetizer = NULL; p_dec->p_owner->p_packetizer = NULL;
/* decoder fifo */ /* decoder fifo */
if( ( p_dec->p_owner->p_fifo = block_FifoNew( p_dec ) ) == NULL ) if( ( p_dec->p_owner->p_fifo = block_FifoNew( p_dec ) ) == NULL )
{ {
msg_Err( p_dec, "out of memory" ); msg_Err( p_dec, "out of memory" );
return NULL; return NULL;
} }
/* Set buffers allocation callbacks for the decoders */ /* Set buffers allocation callbacks for the decoders */
p_dec->pf_aout_buffer_new = aout_new_buffer; p_dec->pf_aout_buffer_new = aout_new_buffer;
p_dec->pf_aout_buffer_del = aout_del_buffer; p_dec->pf_aout_buffer_del = aout_del_buffer;
...@@ -405,6 +413,8 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, ...@@ -405,6 +413,8 @@ static decoder_t * CreateDecoder( input_thread_t *p_input,
p_dec->pf_vout_buffer_del = vout_del_buffer; p_dec->pf_vout_buffer_del = vout_del_buffer;
p_dec->pf_picture_link = vout_link_picture; p_dec->pf_picture_link = vout_link_picture;
p_dec->pf_picture_unlink = vout_unlink_picture; p_dec->pf_picture_unlink = vout_unlink_picture;
p_dec->pf_spu_buffer_new = spu_new_buffer;
p_dec->pf_spu_buffer_del = spu_del_buffer;
vlc_object_attach( p_dec, p_input ); vlc_object_attach( p_dec, p_input );
...@@ -618,7 +628,17 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) ...@@ -618,7 +628,17 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block )
} }
else if( p_dec->fmt_in.i_cat == SPU_ES ) else if( p_dec->fmt_in.i_cat == SPU_ES )
{ {
p_dec->pf_decode_sub( p_dec, &p_block ); vout_thread_t *p_vout;
subpicture_t *p_spu;
while( (p_spu = p_dec->pf_decode_sub( p_dec, &p_block ) ) )
{
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
vout_DisplaySubPicture( p_vout, p_spu );
vlc_object_release( p_vout );
}
}
} }
else else
{ {
...@@ -677,6 +697,18 @@ static void DeleteDecoder( decoder_t * p_dec ) ...@@ -677,6 +697,18 @@ static void DeleteDecoder( decoder_t * p_dec )
es_format_Clean( &p_dec->p_owner->sout ); es_format_Clean( &p_dec->p_owner->sout );
} }
if( p_dec->fmt_in.i_cat == SPU_ES )
{
vout_thread_t *p_vout;
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
vout_ClearOSDChannel( p_vout, p_dec->p_owner->i_spu_channel );
vlc_object_release( p_vout );
}
}
es_format_Clean( &p_dec->fmt_in ); es_format_Clean( &p_dec->fmt_in );
es_format_Clean( &p_dec->fmt_out ); es_format_Clean( &p_dec->fmt_out );
...@@ -840,3 +872,32 @@ static void vout_unlink_picture( decoder_t *p_dec, picture_t *p_pic ) ...@@ -840,3 +872,32 @@ static void vout_unlink_picture( decoder_t *p_dec, picture_t *p_pic )
{ {
vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic );
} }
static subpicture_t *spu_new_buffer( decoder_t *p_dec )
{
decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner;
vout_thread_t *p_vout;
subpicture_t *p_spu;
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( !p_vout ) return NULL;
if( p_sys->p_spu_vout != p_vout )
{
p_sys->i_spu_channel =
vout_RegisterOSDChannel( p_vout );
p_sys->p_spu_vout = p_vout;
}
p_spu = vout_CreateSubPicture( p_vout, p_sys->i_spu_channel,
MEMORY_SUBPICTURE );
vlc_object_release( p_vout );
return p_spu;
}
static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_spu )
{
vout_DestroySubPicture( p_dec->p_owner->p_vout, p_spu );
}
...@@ -1210,7 +1210,8 @@ char *stream_ReadLine( stream_t *s ) ...@@ -1210,7 +1210,8 @@ char *stream_ReadLine( stream_t *s )
} }
/* Remove trailing LF/CR */ /* Remove trailing LF/CR */
while( i_line > 0 && ( p_line[i_line-1] == '\r' || p_line[i_line-1] == '\n') ) i_line--; while( i_line > 0 && ( p_line[i_line-1] == '\r' ||
p_line[i_line-1] == '\n') ) i_line--;
if( i_read > 0 ) if( i_read > 0 )
{ {
...@@ -1218,7 +1219,7 @@ char *stream_ReadLine( stream_t *s ) ...@@ -1218,7 +1219,7 @@ char *stream_ReadLine( stream_t *s )
return p_line; return p_line;
} }
/* We failed to read any data, probably EOF*/ /* We failed to read any data, probably EOF */
if( p_line ) free( p_line ); if( p_line ) free( p_line );
return NULL; return NULL;
} }
...@@ -232,7 +232,7 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, ...@@ -232,7 +232,7 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent,
return NULL; return NULL;
} }
/* Initialize pictures and subpictures - translation tables and functions /* Initialize pictures - translation tables and functions
* will be initialized later in InitThread */ * will be initialized later in InitThread */
for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++) for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
{ {
...@@ -243,12 +243,6 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, ...@@ -243,12 +243,6 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent,
p_vout->p_picture[i_index].b_slow = 0; p_vout->p_picture[i_index].b_slow = 0;
} }
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
{
p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
p_vout->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
}
/* No images in the heap */ /* No images in the heap */
p_vout->i_heap_size = 0; p_vout->i_heap_size = 0;
...@@ -416,13 +410,8 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, ...@@ -416,13 +410,8 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent,
return NULL; return NULL;
} }
p_vout->p_text_renderer_module = /* Initialize subpicture unit */
module_Need( p_vout, "text renderer", NULL, 0 ); vout_InitSPU( p_vout );
if( p_vout->p_text_renderer_module == NULL )
{
msg_Warn( p_vout, "no suitable text renderer module" );
p_vout->pf_add_string = NULL;
}
/* Create a few object variables for interface interaction */ /* Create a few object variables for interface interaction */
var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
...@@ -489,10 +478,6 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, ...@@ -489,10 +478,6 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent,
/* Make sure the thread is destroyed */ /* Make sure the thread is destroyed */
p_vout->b_die = VLC_TRUE; p_vout->b_die = VLC_TRUE;
if( p_vout->p_text_renderer_module )
{
module_Unneed( p_vout, p_vout->p_text_renderer_module );
}
vlc_thread_join( p_vout ); vlc_thread_join( p_vout );
vlc_object_detach( p_vout ); vlc_object_detach( p_vout );
...@@ -1127,18 +1112,8 @@ static void EndThread( vout_thread_t *p_vout ) ...@@ -1127,18 +1112,8 @@ static void EndThread( vout_thread_t *p_vout )
} }
} }
/* Destroy all remaining subpictures */ /* Destroy subpicture unit */
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ ) vout_DestroySPU( p_vout );
{
if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
{
vout_DestroySubPicture( p_vout,
&p_vout->p_subpicture[i_index] );
}
}
if( p_vout->p_text_renderer_module )
module_Unneed( p_vout, p_vout->p_text_renderer_module );
/* Destroy translation tables */ /* Destroy translation tables */
p_vout->pf_end( p_vout ); p_vout->pf_end( p_vout );
......
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/ *****************************************************************************/
#include <vlc/vout.h> #include <vlc/vout.h>
#include <osd.h> #include "vlc_block.h"
#include "vlc_filter.h"
#include "osd.h"
/** /**
* \brief Show text on the video for some time * \brief Show text on the video for some time
...@@ -34,7 +36,7 @@ ...@@ -34,7 +36,7 @@
* \param i_vmargin vertical margin in pixels * \param i_vmargin vertical margin in pixels
* \param i_duration Amount of time the text is to be shown. * \param i_duration Amount of time the text is to be shown.
*/ */
subpicture_t *vout_ShowTextRelative( vout_thread_t *p_vout, int i_channel, int vout_ShowTextRelative( vout_thread_t *p_vout, int i_channel,
char *psz_string, text_style_t *p_style, char *psz_string, text_style_t *p_style,
int i_flags, int i_hmargin, int i_vmargin, int i_flags, int i_hmargin, int i_vmargin,
mtime_t i_duration ) mtime_t i_duration )
...@@ -42,17 +44,36 @@ subpicture_t *vout_ShowTextRelative( vout_thread_t *p_vout, int i_channel, ...@@ -42,17 +44,36 @@ subpicture_t *vout_ShowTextRelative( vout_thread_t *p_vout, int i_channel,
subpicture_t *p_subpic = NULL; subpicture_t *p_subpic = NULL;
mtime_t i_now = mdate(); mtime_t i_now = mdate();
if ( p_vout->pf_add_string ) if( p_vout->p_text && p_vout->p_text->p_module &&
p_vout->p_text->pf_render_string )
{ {
p_subpic = p_vout->pf_add_string( p_vout, i_channel, psz_string, block_t *p_block = block_New( p_vout, strlen(psz_string) + 1 );
p_style, i_flags, i_hmargin, i_vmargin, i_now, i_now + i_duration ); if( p_block )
{
memcpy( p_block->p_buffer, psz_string, p_block->i_buffer );
p_block->i_pts = p_block->i_dts = i_now;
p_block->i_length = i_duration;
p_subpic = p_vout->p_text->pf_render_string( p_vout->p_text,
p_block );
if( p_subpic )
{
p_subpic->i_x = i_hmargin;
p_subpic->i_y = i_vmargin;
p_subpic->i_flags = i_flags;
p_subpic->i_channel = i_channel;
vout_DisplaySubPicture( p_vout, p_subpic );
return VLC_SUCCESS;
}
}
return VLC_EGENERIC;
} }
else else
{ {
msg_Warn( p_vout, "No text renderer found" ); msg_Warn( p_vout, "No text renderer found" );
return VLC_EGENERIC;
} }
return p_subpic;
} }
/** /**
...@@ -74,12 +95,33 @@ int vout_ShowTextAbsolute( vout_thread_t *p_vout, int i_channel, ...@@ -74,12 +95,33 @@ int vout_ShowTextAbsolute( vout_thread_t *p_vout, int i_channel,
int i_flags, int i_hmargin, int i_vmargin, int i_flags, int i_hmargin, int i_vmargin,
mtime_t i_start, mtime_t i_stop ) mtime_t i_start, mtime_t i_stop )
{ {
if ( p_vout->pf_add_string ) subpicture_t *p_subpic = NULL;
if( p_vout->p_text && p_vout->p_text->p_module &&
p_vout->p_text->pf_render_string )
{
block_t *p_block = block_New( p_vout, strlen(psz_string) + 1 );
if( p_block )
{
memcpy( p_block->p_buffer, psz_string, p_block->i_buffer );
p_block->i_pts = p_block->i_dts = i_start;
p_block->i_length = i_stop - i_start;
p_subpic = p_vout->p_text->pf_render_string( p_vout->p_text,
p_block );
if( p_subpic )
{ {
p_vout->pf_add_string( p_vout, i_channel, psz_string, p_style, i_flags, p_subpic->i_x = i_hmargin;
i_hmargin, i_vmargin, i_start, i_stop ); p_subpic->i_y = i_vmargin;
p_subpic->i_flags = i_flags;
p_subpic->i_channel = i_channel;
vout_DisplaySubPicture( p_vout, p_subpic );
return VLC_SUCCESS; return VLC_SUCCESS;
} }
}
return VLC_EGENERIC;
}
else else
{ {
msg_Warn( p_vout, "No text renderer found" ); msg_Warn( p_vout, "No text renderer found" );
......
...@@ -586,6 +586,9 @@ void vout_InitFormat( video_frame_format_t *p_format, vlc_fourcc_t i_chroma, ...@@ -586,6 +586,9 @@ void vout_InitFormat( video_frame_format_t *p_format, vlc_fourcc_t i_chroma,
switch( i_chroma ) switch( i_chroma )
{ {
case FOURCC_YUVA:
p_format->i_bits_per_pixel = 32;
break;
case FOURCC_I444: case FOURCC_I444:
p_format->i_bits_per_pixel = 24; p_format->i_bits_per_pixel = 24;
break; break;
...@@ -608,6 +611,10 @@ void vout_InitFormat( video_frame_format_t *p_format, vlc_fourcc_t i_chroma, ...@@ -608,6 +611,10 @@ void vout_InitFormat( video_frame_format_t *p_format, vlc_fourcc_t i_chroma,
case FOURCC_Y211: case FOURCC_Y211:
p_format->i_bits_per_pixel = 8; p_format->i_bits_per_pixel = 8;
break; break;
case FOURCC_YUVP:
p_format->i_bits_per_pixel = 8;
break;
case FOURCC_RV32: case FOURCC_RV32:
p_format->i_bits_per_pixel = 32; p_format->i_bits_per_pixel = 32;
break; break;
...@@ -731,6 +738,30 @@ void vout_InitPicture( vlc_object_t *p_this, picture_t *p_pic, ...@@ -731,6 +738,30 @@ void vout_InitPicture( vlc_object_t *p_this, picture_t *p_pic,
p_pic->i_planes = 3; p_pic->i_planes = 3;
break; break;
case FOURCC_YUVA:
p_pic->p[ Y_PLANE ].i_lines = i_height;
p_pic->p[ Y_PLANE ].i_pitch = i_width;
p_pic->p[ Y_PLANE ].i_visible_pitch = p_pic->p[ Y_PLANE ].i_pitch;
p_pic->p[ U_PLANE ].i_lines = i_height;
p_pic->p[ U_PLANE ].i_pitch = i_width;
p_pic->p[ U_PLANE ].i_visible_pitch = p_pic->p[ U_PLANE ].i_pitch;
p_pic->p[ V_PLANE ].i_lines = i_height;
p_pic->p[ V_PLANE ].i_pitch = i_width;
p_pic->p[ V_PLANE ].i_visible_pitch = p_pic->p[ V_PLANE ].i_pitch;
p_pic->p[ A_PLANE ].i_lines = i_height;
p_pic->p[ A_PLANE ].i_pitch = i_width;
p_pic->p[ A_PLANE ].i_visible_pitch = p_pic->p[ A_PLANE ].i_pitch;
p_pic->i_planes = 4;
break;
case FOURCC_YUVP:
p_pic->p->i_lines = i_height;
p_pic->p->i_pitch = i_width;
p_pic->p->i_visible_pitch = p_pic->p->i_pitch;
p_pic->p->i_pixel_pitch = 8;
p_pic->i_planes = 1;
break;
case FOURCC_Y211: case FOURCC_Y211:
p_pic->p->i_lines = i_height; p_pic->p->i_lines = i_height;
p_pic->p->i_pitch = i_width; p_pic->p->i_pitch = i_width;
...@@ -811,20 +842,6 @@ void vout_InitPicture( vlc_object_t *p_this, picture_t *p_pic, ...@@ -811,20 +842,6 @@ void vout_InitPicture( vlc_object_t *p_this, picture_t *p_pic,
p_pic->p_heap->i_bmask = 0x0000ff; */ p_pic->p_heap->i_bmask = 0x0000ff; */
p_pic->i_planes = 1; p_pic->i_planes = 1;
break; break;
case FOURCC_YUVA:
p_pic->p[ Y_PLANE ].i_lines = i_height;
p_pic->p[ Y_PLANE ].i_pitch = i_width;
p_pic->p[ Y_PLANE ].i_visible_pitch = p_pic->p[ Y_PLANE ].i_pitch;
p_pic->p[ U_PLANE ].i_lines = i_height;
p_pic->p[ U_PLANE ].i_pitch = i_width;
p_pic->p[ U_PLANE ].i_visible_pitch = p_pic->p[ U_PLANE ].i_pitch;
p_pic->p[ V_PLANE ].i_lines = i_height;
p_pic->p[ V_PLANE ].i_pitch = i_width;
p_pic->p[ V_PLANE ].i_visible_pitch = p_pic->p[ V_PLANE ].i_pitch;
p_pic->p[ A_PLANE ].i_lines = i_height;
p_pic->p[ A_PLANE ].i_pitch = i_width;
p_pic->p[ A_PLANE ].i_visible_pitch = p_pic->p[ A_PLANE ].i_pitch;
p_pic->i_planes = 4;
default: default:
msg_Err( p_this, "unknown chroma type 0x%.8x (%4.4s)", msg_Err( p_this, "unknown chroma type 0x%.8x (%4.4s)",
......
...@@ -90,3 +90,6 @@ ...@@ -90,3 +90,6 @@
/* Planar 4:4:4:4 Y:U:V:A */ /* Planar 4:4:4:4 Y:U:V:A */
#define FOURCC_YUVA VLC_FOURCC('Y','U','V','A') #define FOURCC_YUVA VLC_FOURCC('Y','U','V','A')
/* Palettized YUV with palette element Y:U:V:A */
#define FOURCC_YUVP VLC_FOURCC('Y','U','V','P')
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* *
* Authors: Vincent Seguin <seguin@via.ecp.fr> * Authors: Vincent Seguin <seguin@via.ecp.fr>
* Samuel Hocevar <sam@zoy.org> * Samuel Hocevar <sam@zoy.org>
* Gildas Bazin <gbazin@videolan.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -33,6 +34,156 @@ ...@@ -33,6 +34,156 @@
#include "vlc_video.h" #include "vlc_video.h"
#include "video_output.h" #include "video_output.h"
#include "vlc_filter.h"
#include "osd.h"
static void UpdateSPU ( vout_thread_t *, vlc_object_t * );
static int CropCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static subpicture_t *spu_new_buffer( filter_t * );
static void spu_del_buffer( filter_t *, subpicture_t * );
/**
* Initialise the subpicture decoder unit
*
* \param p_vout the vout in which to create the subpicture unit
*/
void vout_InitSPU( vout_thread_t *p_vout )
{
vlc_object_t *p_input;
int i_index;
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
{
p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
p_vout->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
}
/* Load the text rendering module */
p_vout->p_text = vlc_object_create( p_vout, sizeof(filter_t) );
p_vout->p_text->pf_spu_buffer_new = spu_new_buffer;
p_vout->p_text->pf_spu_buffer_del = spu_del_buffer;
p_vout->p_text->p_owner = (filter_owner_sys_t *)p_vout;
vlc_object_attach( p_vout->p_text, p_vout );
p_vout->p_text->p_module =
module_Need( p_vout->p_text, "text renderer", 0, 0 );
if( p_vout->p_text->p_module == NULL )
{
msg_Warn( p_vout, "no suitable text renderer module" );
vlc_object_detach( p_vout->p_text );
vlc_object_destroy( p_vout->p_text );
p_vout->p_text = NULL;
}
p_vout->p_blend = NULL;
p_vout->i_crop_x = p_vout->i_crop_y =
p_vout->i_crop_width = p_vout->i_crop_height = 0;
p_vout->b_force_alpha = VLC_FALSE;
p_vout->b_force_crop = VLC_FALSE;
/* Create callback */
p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
if( p_input )
{
UpdateSPU( p_vout, VLC_OBJECT(p_input) );
var_AddCallback( p_input, "highlight", CropCallback, p_vout );
vlc_object_release( p_input );
}
}
/**
* Destroy the subpicture decoder unit
*
* \param p_vout the vout in which to destroy the subpicture unit
*/
void vout_DestroySPU( vout_thread_t *p_vout )
{
vlc_object_t *p_input;
int i_index;
/* Destroy all remaining subpictures */
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
{
if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
{
vout_DestroySubPicture( p_vout,
&p_vout->p_subpicture[i_index] );
}
}
if( p_vout->p_text )
{
if( p_vout->p_text->p_module )
module_Unneed( p_vout->p_text, p_vout->p_text->p_module );
vlc_object_detach( p_vout->p_text );
vlc_object_destroy( p_vout->p_text );
}
if( p_vout->p_blend )
{
if( p_vout->p_blend->p_module )
module_Unneed( p_vout->p_blend, p_vout->p_blend->p_module );
vlc_object_detach( p_vout->p_blend );
vlc_object_destroy( p_vout->p_blend );
}
/* Delete callback */
p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
if( p_input )
{
var_DelCallback( p_input, "highlight", CropCallback, p_vout );
vlc_object_release( p_input );
}
}
/**
* Create a subpicture region
*
* \param p_this vlc_object_t
* \param p_fmt the format that this subpicture region should have
*/
subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
video_format_t *p_fmt )
{
subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
memset( p_region, 0, sizeof(subpicture_region_t) );
p_region->p_next = 0;
p_region->fmt = *p_fmt;
vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
if( !p_region->picture.i_planes )
{
free( p_region );
return NULL;
}
if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
p_fmt->p_palette = p_region->fmt.p_palette =
malloc( sizeof(video_palette_t) );
else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
return p_region;
}
/**
* Destroy a subpicture region
*
* \param p_this vlc_object_t
* \param p_region the subpicture region to destroy
*/
void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
{
if( !p_region ) return;
if( p_region->picture.p_data_orig ) free( p_region->picture.p_data_orig );
if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
free( p_region );
}
/** /**
* Display a subpicture unit * Display a subpicture unit
...@@ -134,6 +285,11 @@ subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel, ...@@ -134,6 +285,11 @@ subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
p_subpic->i_y = 0; p_subpic->i_y = 0;
p_subpic->i_width = 0; p_subpic->i_width = 0;
p_subpic->i_height = 0; p_subpic->i_height = 0;
p_subpic->b_absolute= VLC_TRUE;
p_subpic->i_flags = 0;
p_subpic->pf_render = 0;
p_subpic->pf_destroy= 0;
p_subpic->p_sys = 0;
/* Remain last subpicture displayed in DEFAULT_CHAN */ /* Remain last subpicture displayed in DEFAULT_CHAN */
if( i_channel == DEFAULT_CHAN ) if( i_channel == DEFAULT_CHAN )
...@@ -143,6 +299,9 @@ subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel, ...@@ -143,6 +299,9 @@ subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
vlc_mutex_unlock( &p_vout->subpicture_lock ); vlc_mutex_unlock( &p_vout->subpicture_lock );
p_subpic->pf_create_region = __spu_CreateRegion;
p_subpic->pf_destroy_region = __spu_DestroyRegion;
return p_subpic; return p_subpic;
} }
...@@ -174,6 +333,13 @@ void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic ) ...@@ -174,6 +333,13 @@ void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
p_subpic, p_subpic->i_status ); p_subpic, p_subpic->i_status );
} }
while( p_subpic->p_region )
{
subpicture_region_t *p_region = p_subpic->p_region;
p_subpic->p_region = p_region->p_next;
spu_DestroyRegion( p_vout, p_region );
}
if( p_subpic->pf_destroy ) if( p_subpic->pf_destroy )
{ {
p_subpic->pf_destroy( p_subpic ); p_subpic->pf_destroy( p_subpic );
...@@ -192,13 +358,109 @@ void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic ) ...@@ -192,13 +358,109 @@ void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic, void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic,
subpicture_t *p_subpic ) subpicture_t *p_subpic )
{ {
/* Load the blending module */
if( !p_vout->p_blend && p_subpic && p_subpic->p_region )
{
p_vout->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
vlc_object_attach( p_vout->p_blend, p_vout );
p_vout->p_blend->fmt_out.video.i_width =
p_vout->p_blend->fmt_out.video.i_visible_width =
p_vout->render.i_width;
p_vout->p_blend->fmt_out.video.i_height =
p_vout->p_blend->fmt_out.video.i_visible_height =
p_vout->render.i_height;
p_vout->p_blend->fmt_out.video.i_x_offset =
p_vout->p_blend->fmt_out.video.i_y_offset = 0;
p_vout->p_blend->fmt_out.video.i_aspect =
p_vout->render.i_aspect;
p_vout->p_blend->fmt_out.video.i_chroma =
p_vout->output.i_chroma;
p_vout->p_blend->fmt_in.video = p_subpic->p_region->fmt;
p_vout->p_blend->p_module =
module_Need( p_vout->p_blend, "video blending", 0, 0 );
}
/* Get lock */ /* Get lock */
vlc_mutex_lock( &p_vout->subpicture_lock ); vlc_mutex_lock( &p_vout->subpicture_lock );
/* Check i_status again to make sure spudec hasn't destroyed the subpic */ /* Check i_status again to make sure spudec hasn't destroyed the subpic */
while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE ) while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
{
subpicture_region_t *p_region = p_subpic->p_region;
if( p_subpic->pf_render )
{ {
p_subpic->pf_render( p_vout, p_pic, p_subpic ); p_subpic->pf_render( p_vout, p_pic, p_subpic );
}
else while( p_region && p_vout->p_blend &&
p_vout->p_blend->pf_video_blend )
{
int i_x_offset = p_region->i_x + p_subpic->i_x;
int i_y_offset = p_region->i_y + p_subpic->i_y;
if( p_subpic->i_flags & OSD_ALIGN_BOTTOM )
{
i_y_offset += p_vout->output.i_height - p_region->fmt.i_height;
}
else if ( !(p_subpic->i_flags & OSD_ALIGN_TOP) )
{
i_y_offset += p_vout->output.i_height / 2 -
p_region->fmt.i_height / 2;
}
if( p_subpic->i_flags & OSD_ALIGN_RIGHT )
{
i_x_offset += p_vout->output.i_width - p_region->fmt.i_width;
}
else if ( !(p_subpic->i_flags & OSD_ALIGN_LEFT) )
{
i_x_offset += p_vout->output.i_width / 2 -
p_region->fmt.i_width / 2;
}
if( p_subpic->b_absolute )
{
i_x_offset = p_region->i_x + p_subpic->i_x;
i_y_offset = p_region->i_y + p_subpic->i_y;
}
p_vout->p_blend->fmt_in.video = p_region->fmt;
/* Force cropping if requested */
if( p_vout->b_force_crop )
{
p_vout->p_blend->fmt_in.video.i_x_offset = p_vout->i_crop_x;
p_vout->p_blend->fmt_in.video.i_y_offset = p_vout->i_crop_y;
p_vout->p_blend->fmt_in.video.i_visible_width =
p_vout->i_crop_width;
p_vout->p_blend->fmt_in.video.i_visible_height =
p_vout->i_crop_height;
i_x_offset += p_vout->i_crop_x;
i_y_offset += p_vout->i_crop_y;
}
/* Force palette if requested */
if( p_vout->b_force_alpha && VLC_FOURCC('Y','U','V','P') ==
p_vout->p_blend->fmt_in.video.i_chroma )
{
p_vout->p_blend->fmt_in.video.p_palette->palette[0][3] =
p_vout->pi_alpha[0];
p_vout->p_blend->fmt_in.video.p_palette->palette[1][3] =
p_vout->pi_alpha[1];
p_vout->p_blend->fmt_in.video.p_palette->palette[2][3] =
p_vout->pi_alpha[2];
p_vout->p_blend->fmt_in.video.p_palette->palette[3][3] =
p_vout->pi_alpha[3];
}
p_vout->p_blend->pf_video_blend( p_vout->p_blend,
p_pic, p_pic, &p_region->picture, i_x_offset, i_y_offset );
p_region = p_region->p_next;
}
p_subpic = p_subpic->p_next; p_subpic = p_subpic->p_next;
} }
...@@ -325,7 +587,8 @@ subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout, ...@@ -325,7 +587,8 @@ subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
*****************************************************************************/ *****************************************************************************/
int vout_RegisterOSDChannel( vout_thread_t *p_vout ) int vout_RegisterOSDChannel( vout_thread_t *p_vout )
{ {
msg_Dbg( p_vout, "Registering OSD channel, ID: %i", p_vout->i_channel_count + 1 ); msg_Dbg( p_vout, "Registering OSD channel, ID: %i",
p_vout->i_channel_count + 1 );
return ++p_vout->i_channel_count; return ++p_vout->i_channel_count;
} }
...@@ -361,8 +624,16 @@ void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel ) ...@@ -361,8 +624,16 @@ void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
{ {
continue; continue;
} }
if( p_subpic->i_channel == i_channel ) if( p_subpic->i_channel == i_channel )
{ {
while( p_subpic->p_region )
{
subpicture_region_t *p_region = p_subpic->p_region;
p_subpic->p_region = p_region->p_next;
spu_DestroyRegion( p_vout, p_region );
}
if( p_subpic->pf_destroy ) if( p_subpic->pf_destroy )
{ {
p_subpic->pf_destroy( p_subpic ); p_subpic->pf_destroy( p_subpic );
...@@ -373,3 +644,87 @@ void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel ) ...@@ -373,3 +644,87 @@ void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
vlc_mutex_unlock( &p_vout->subpicture_lock ); vlc_mutex_unlock( &p_vout->subpicture_lock );
} }
/*****************************************************************************
* UpdateSPU: update subpicture settings
*****************************************************************************
* This function is called from CropCallback and at initialization time, to
* retrieve crop information from the input.
*****************************************************************************/
static void UpdateSPU( vout_thread_t *p_vout, vlc_object_t *p_object )
{
vlc_value_t val;
p_vout->b_force_alpha = VLC_FALSE;
p_vout->b_force_crop = VLC_FALSE;
if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
p_vout->b_force_crop = VLC_TRUE;
var_Get( p_object, "x-start", &val );
p_vout->i_crop_x = val.i_int;
var_Get( p_object, "y-start", &val );
p_vout->i_crop_y = val.i_int;
var_Get( p_object, "x-end", &val );
p_vout->i_crop_width = val.i_int - p_vout->i_crop_x;
var_Get( p_object, "y-end", &val );
p_vout->i_crop_height = val.i_int - p_vout->i_crop_y;
#if 0
if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
{
int i;
for( i = 0; i < 4; i++ )
{
p_vout->pi_color[i] = ((uint8_t *)val.p_address)[i];
}
}
#endif
if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
{
int i;
for( i = 0; i < 4; i++ )
{
p_vout->pi_alpha[i] = ((uint8_t *)val.p_address)[i];
p_vout->pi_alpha[i] = p_vout->pi_alpha[i] == 0xf ?
0xff : p_vout->pi_alpha[i] << 4;
}
p_vout->b_force_alpha = VLC_TRUE;
}
msg_Dbg( p_vout, "crop: %i,%i,%i,%i, alpha: %i",
p_vout->i_crop_x, p_vout->i_crop_y,
p_vout->i_crop_width, p_vout->i_crop_height,
p_vout->b_force_alpha );
}
/*****************************************************************************
* CropCallback: called when the highlight properties are changed
*****************************************************************************
* This callback is called from the input thread when we need cropping
*****************************************************************************/
static int CropCallback( vlc_object_t *p_object, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
UpdateSPU( (vout_thread_t *)p_data, p_object );
return VLC_SUCCESS;
}
/*****************************************************************************
* Buffers allocation callbacks for the filters
*****************************************************************************/
static subpicture_t *spu_new_buffer( filter_t *p_filter )
{
vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_owner;
subpicture_t *p_spu;
p_spu = vout_CreateSubPicture( p_vout, !DEFAULT_CHAN, MEMORY_SUBPICTURE );
return p_spu;
}
static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_spu )
{
vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_owner;
vout_DestroySubPicture( p_vout, p_spu );
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment