/*****************************************************************************
* subpicture.c: Subpicture functions
*****************************************************************************
* Copyright (C) 2010 Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <assert.h>
#include <vlc_common.h>
#include <vlc_image.h>
#include <vlc_subpicture.h>
#include "subpicture.h"
#include <limits.h>
struct subpicture_private_t
{
video_format_t src;
video_format_t dst;
unsigned display_width;
unsigned display_height;
};
subpicture_t *subpicture_New( const subpicture_updater_t *p_upd )
{
subpicture_t *p_subpic = calloc( 1, sizeof(*p_subpic) );
if( !p_subpic )
return NULL;
p_subpic->i_order = 0;
p_subpic->b_fade = false;
p_subpic->b_subtitle = false;
p_subpic->i_alpha = 0xFF;
vlc_spu_regions_init(&p_subpic->regions);
if( p_upd )
{
subpicture_private_t *p_private = malloc( sizeof(*p_private) );
if( !p_private )
{
free( p_subpic );
return NULL;
}
video_format_Init( &p_private->src, 0 );
video_format_Init( &p_private->dst, 0 );
p_private->display_width = 0;
p_private->display_height = 0;
p_subpic->updater = *p_upd;
p_subpic->p_private = p_private;
}
else
{
p_subpic->p_private = NULL;
p_subpic->updater.ops = NULL;
p_subpic->updater.sys = NULL;
}
return p_subpic;
}
void subpicture_Delete( subpicture_t *p_subpic )
{
vlc_spu_regions_Clear( &p_subpic->regions );
if (p_subpic->updater.ops != NULL &&
p_subpic->updater.ops->destroy != NULL)
{
p_subpic->updater.ops->destroy(p_subpic);
}
if( p_subpic->p_private )
{
video_format_Clean( &p_subpic->p_private->src );
video_format_Clean( &p_subpic->p_private->dst );
}
free( p_subpic->p_private );
free( p_subpic );
}
vlc_render_subpicture *vlc_render_subpicture_New( void )
{
vlc_render_subpicture *p_subpic = malloc( sizeof(*p_subpic) );
if( unlikely(p_subpic == NULL ) )
return NULL;
vlc_vector_init(&p_subpic->regions);
return p_subpic;
}
void vlc_render_subpicture_Delete( vlc_render_subpicture *p_subpic )
{
struct subpicture_region_rendered *p_region;
vlc_vector_foreach(p_region, &p_subpic->regions)
{
picture_Release( p_region->p_picture );
free( p_region );
}
vlc_vector_clear( &p_subpic->regions );
free( p_subpic );
}
subpicture_t *subpicture_NewFromPicture( vlc_object_t *p_obj,
picture_t *p_picture, vlc_fourcc_t i_chroma )
{
/* */
video_format_t fmt_in = p_picture->format;
/* */
video_format_t fmt_out;
fmt_out = fmt_in;
fmt_out.i_chroma = i_chroma;
/* */
image_handler_t *p_image = image_HandlerCreate( p_obj );
if( !p_image )
return NULL;
picture_t *p_pip = image_Convert( p_image, p_picture, &fmt_in, &fmt_out );
image_HandlerDelete( p_image );
if( !p_pip )
return NULL;
subpicture_t *p_subpic = subpicture_New( NULL );
if( unlikely(!p_subpic) )
{
picture_Release( p_pip );
return NULL;
}
p_subpic->i_original_picture_width = fmt_out.i_visible_width;
p_subpic->i_original_picture_height = fmt_out.i_visible_height;
subpicture_region_t *p_region = subpicture_region_ForPicture( p_pip );
picture_Release( p_pip );
if (likely(p_region == NULL))
{
subpicture_Delete(p_subpic);
return NULL;
}
p_region->b_absolute = true; p_region->b_in_window = false;
p_region->fmt.i_sar_num =
p_region->fmt.i_sar_den = 0;
// margin to the center or to the top/left if the subpicture is absolute
p_region->i_x = 0;
p_region->i_y = 0;
vlc_spu_regions_push( &p_subpic->regions, p_region );
return p_subpic;
}
void subpicture_Update( subpicture_t *p_subpicture,
const video_format_t *p_fmt_src,
const video_format_t *p_fmt_dst,
unsigned display_width, unsigned display_height,
vlc_tick_t i_ts )
{
subpicture_updater_t *p_upd = &p_subpicture->updater;
subpicture_private_t *p_private = p_subpicture->p_private;
if (p_upd->ops == NULL)
return;
struct vlc_spu_updater_configuration cfg = {
.current = {
.display_width = display_width,
.display_height = display_height,
.video_src = p_fmt_src,
.video_dst = p_fmt_dst,
},
.previous = {
.display_width = p_private->display_width,
.display_height = p_private->display_height,
.video_src = &p_private->src,
.video_dst = &p_private->dst,
},
.pts = i_ts,
};
p_upd->ops->update( p_subpicture, &cfg );
video_format_Clean( &p_private->src );
video_format_Clean( &p_private->dst );
video_format_Copy( &p_private->src, p_fmt_src );
video_format_Copy( &p_private->dst, p_fmt_dst );
p_private->display_width = display_width;
p_private->display_height = display_height;
}
typedef struct subpicture_region_private_t
{
subpicture_region_t region;
video_format_t fmt;
picture_t *p_picture;
} subpicture_region_private_t;
const video_format_t * subpicture_region_cache_GetFormat( const subpicture_region_t *p_region )
{
subpicture_region_private_t *p_priv = container_of(p_region, subpicture_region_private_t, region);
return &p_priv->fmt;
}
picture_t * subpicture_region_cache_GetPicture( subpicture_region_t *p_region )
{
subpicture_region_private_t *p_priv = container_of(p_region, subpicture_region_private_t, region);
return p_priv->p_picture;
}
bool subpicture_region_cache_IsValid(const subpicture_region_t *p_region)
{
subpicture_region_private_t *p_priv = container_of(p_region, subpicture_region_private_t, region);
return p_priv->fmt.i_chroma;
}
void subpicture_region_cache_Invalidate( subpicture_region_t *p_region )
{
subpicture_region_private_t *p_priv = container_of(p_region, subpicture_region_private_t, region);
if( p_priv->p_picture )
{
picture_Release( p_priv->p_picture );
p_priv->p_picture = NULL;
}
video_format_Clean( &p_priv->fmt );
video_format_Init( &p_priv->fmt, 0 );
}
int subpicture_region_cache_Assign( subpicture_region_t *p_region, picture_t *p_picture )
{
subpicture_region_private_t *p_priv = container_of(p_region, subpicture_region_private_t, region);
if ( video_format_Copy( &p_priv->fmt, &p_picture->format ) != VLC_SUCCESS )
return VLC_EGENERIC;
p_priv->p_picture = p_picture;
return VLC_SUCCESS;
}
static subpicture_region_t * subpicture_region_NewInternal( void )
{
subpicture_region_private_t *p_region = calloc( 1, sizeof(subpicture_region_private_t) );
if( unlikely(p_region == NULL) )
return NULL;
p_region->region.i_alpha = 0xff;
p_region->region.i_x = INT_MAX;
p_region->region.i_y = INT_MAX;
p_region->region.b_absolute = false; p_region->region.b_in_window = false;
return &p_region->region;
}
subpicture_region_t *subpicture_region_New( const video_format_t *p_fmt )
{
assert(p_fmt->i_chroma != VLC_CODEC_TEXT);
subpicture_region_t *p_region = subpicture_region_NewInternal( );
if( !p_region )
return NULL;
video_format_Copy( &p_region->fmt, p_fmt );
if (p_region->fmt.transfer == TRANSFER_FUNC_UNDEF)
p_region->fmt.transfer = TRANSFER_FUNC_SRGB;
if (p_region->fmt.primaries == COLOR_PRIMARIES_UNDEF)
p_region->fmt.primaries = COLOR_PRIMARIES_SRGB;
if (p_region->fmt.space == COLOR_SPACE_UNDEF)
p_region->fmt.space = COLOR_SPACE_SRGB;
if (p_region->fmt.color_range == COLOR_RANGE_UNDEF)
p_region->fmt.color_range = COLOR_RANGE_FULL;
p_region->p_picture = picture_NewFromFormat( &p_region->fmt );
if( !p_region->p_picture )
{
video_format_Clean( &p_region->fmt );
free( p_region );
return NULL;
}
if ( p_fmt->i_chroma == VLC_CODEC_YUVP || p_fmt->i_chroma == VLC_CODEC_RGBP )
{
/* YUVP/RGBP should have a palette */
if( p_region->p_picture->format.p_palette == NULL )
{
p_region->p_picture->format.p_palette = calloc( 1, sizeof(*p_region->p_picture->format.p_palette) );
if( p_region->p_picture->format.p_palette == NULL )
{
video_format_Clean( &p_region->fmt );
picture_Release( p_region->p_picture );
free( p_region );
return NULL;
}
}
}
else
{
assert(p_fmt->p_palette == NULL);
}
return p_region;
}
subpicture_region_t *subpicture_region_NewText( void )
{
subpicture_region_t *p_region = subpicture_region_NewInternal( );
if( !p_region )
return NULL;
p_region->text_flags |= VLC_SUBPIC_TEXT_FLAG_IS_TEXT;
video_format_Init( &p_region->fmt, 0 );
p_region->fmt.transfer = TRANSFER_FUNC_SRGB;
p_region->fmt.primaries = COLOR_PRIMARIES_SRGB;
p_region->fmt.space = COLOR_SPACE_SRGB;
p_region->fmt.color_range = COLOR_RANGE_FULL;
return p_region;
}
subpicture_region_t *subpicture_region_ForPicture( picture_t *pic )
{
subpicture_region_t *p_region = subpicture_region_NewInternal( );
if( !p_region )
return NULL;
video_format_Copy( &p_region->fmt, &pic->format );
if ( pic->format.i_chroma == VLC_CODEC_YUVP || pic->format.i_chroma == VLC_CODEC_RGBP )
{
/* YUVP/RGBP should have a palette */
if( pic->format.p_palette == NULL )
{
pic->format.p_palette = calloc( 1, sizeof(*pic->format.p_palette) );
if( pic->format.p_palette == NULL )
{
video_format_Clean( &pic->format );
free( p_region );
return NULL;
}
}
}
else
{
assert(pic->format.p_palette == NULL);
}
p_region->p_picture = picture_Hold(pic);
return p_region;
}
void subpicture_region_Delete( subpicture_region_t *p_region )
{
if( !p_region )
return;
subpicture_region_private_t *p_priv = container_of(p_region, subpicture_region_private_t, region);
subpicture_region_cache_Invalidate( p_region );
if( p_region->p_picture )
picture_Release( p_region->p_picture );
text_segment_ChainDelete( p_region->p_text );
video_format_Clean( &p_region->fmt );
free( p_priv );
}
void vlc_spu_regions_Clear( vlc_spu_regions *regions )
{
subpicture_region_t *p_head;
vlc_spu_regions_foreach(p_head, regions)
{
vlc_spu_regions_remove( regions, p_head );
subpicture_region_Delete( p_head );
}
}
#include <vlc_filter.h>
unsigned picture_BlendSubpicture(picture_t *dst,
vlc_blender_t *blend, vlc_render_subpicture *src)
{
unsigned done = 0;
assert(src);
struct subpicture_region_rendered *r;
vlc_vector_foreach(r, &src->regions) {
assert(r->p_picture);
if (filter_ConfigureBlend(blend, dst->format.i_width,
dst->format.i_height, &r->p_picture->format)
|| filter_Blend(blend, dst, r->place.x, r->place.y, r->p_picture, r->i_alpha))
msg_Err(blend, "blending %4.4s to %4.4s failed",
(char *)&blend->fmt_in.video.i_chroma,
(char *)&blend->fmt_out.video.i_chroma );
else
done++;
}
return done;
}
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland
|
|