Commit d6e6460d authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

filter_chain: introduce dedicated filter_chain_NewVideo() for video filters

Also remove the filter chain buffer functions update hack, keep constant
callbacks for video filters in the filter chain, remove now useless
parameters from filter_chain_New(), and inline
filter_chain_AppendFilterInternal().
parent 6650b321
......@@ -297,13 +297,25 @@ typedef struct filter_chain_t filter_chain_t;
* \param p_object pointer to a vlc object
* \param psz_capability vlc capability of filters in filter chain
* \param b_allow_format_fmt_change allow changing of fmt
* \param pf_buffer_allocation_init callback function to initialize buffer allocations
* \param pf_buffer_allocation_clear callback function to clear buffer allocation initialization
* \param p_buffer_allocation_data pointer to private allocation data
* \return pointer to a filter chain
*/
VLC_API filter_chain_t * filter_chain_New( vlc_object_t *, const char *, bool, int (*)( filter_t *, void * ), void (*)( filter_t * ), void * ) VLC_USED;
#define filter_chain_New( a, b, c, d, e, f ) filter_chain_New( VLC_OBJECT( a ), b, c, d, e, f )
VLC_API filter_chain_t * filter_chain_New( vlc_object_t *, const char *, bool )
VLC_USED;
#define filter_chain_New( a, b, c ) filter_chain_New( VLC_OBJECT( a ), b, c )
/**
* Creates a new video filter chain.
*
* \param obj pointer to parent VLC object
* \param change whether to allow changing the output format
* \param owner owner video buffer callbacks
* \return new filter chain, or NULL on error
*/
VLC_API filter_chain_t * filter_chain_NewVideo( vlc_object_t *obj, bool change,
const filter_owner_t *owner )
VLC_USED;
#define filter_chain_NewVideo( a, b, c ) \
filter_chain_NewVideo( VLC_OBJECT( a ), b, c )
/**
* Delete filter chain will delete all filters in the chain and free all
......
......@@ -274,14 +274,6 @@ static void Close( vlc_object_t * p_this )
free( p_sys );
}
static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
{
p_filter->owner.sys = p_data;
p_filter->owner.video.buffer_new = video_new_buffer_filter;
p_filter->owner.video.buffer_del = video_del_buffer_filter;
return VLC_SUCCESS;
}
static sout_stream_id_sys_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
{
sout_stream_sys_t *p_sys = p_stream->p_sys;
......@@ -400,9 +392,15 @@ static sout_stream_id_sys_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
msg_Dbg( p_stream, "psz_chain: %s", psz_chain );
if( psz_chain )
{
p_sys->p_vf2 = filter_chain_New( p_stream, "video filter2", false,
video_filter_buffer_allocation_init,
NULL, p_sys->p_decoder->p_owner );
filter_owner_t owner = {
.sys = p_sys->p_decoder->p_owner,
.video = {
.buffer_new = video_new_buffer_filter,
.buffer_del = video_del_buffer_filter,
},
};
p_sys->p_vf2 = filter_chain_NewVideo( p_stream, false, &owner );
es_format_t fmt;
es_format_Copy( &fmt, &p_sys->p_decoder->fmt_out );
if( p_sys->i_chroma )
......
......@@ -77,21 +77,13 @@ static picture_t *transcode_video_filter_buffer_new( filter_t *p_filter )
p_filter->fmt_out.video.i_chroma = p_filter->fmt_out.i_codec;
return picture_NewFromFormat( &p_filter->fmt_out.video );
}
static void transcode_video_filter_buffer_del( filter_t *p_filter, picture_t *p_pic )
{
VLC_UNUSED(p_filter);
picture_Release( p_pic );
}
static int transcode_video_filter_allocation_init( filter_t *p_filter,
void *p_data )
{
VLC_UNUSED(p_data);
p_filter->owner.video.buffer_new = transcode_video_filter_buffer_new;
p_filter->owner.video.buffer_del = transcode_video_filter_buffer_del;
return VLC_SUCCESS;
}
static void* EncoderThread( void *obj )
{
sout_stream_sys_t *p_sys = (sout_stream_sys_t*)obj;
......@@ -293,13 +285,17 @@ int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_sys_t *id )
static void transcode_video_filter_init( sout_stream_t *p_stream,
sout_stream_id_sys_t *id )
{
filter_owner_t owner = {
.sys = p_stream->p_sys,
.video = {
.buffer_new = transcode_video_filter_buffer_new,
.buffer_del = transcode_video_filter_buffer_del,
},
};
es_format_t *p_fmt_out = &id->p_decoder->fmt_out;
id->p_encoder->fmt_in.video.i_chroma = id->p_encoder->fmt_in.i_codec;
id->p_f_chain = filter_chain_New( p_stream, "video filter2",
false,
transcode_video_filter_allocation_init,
NULL, p_stream->p_sys );
id->p_encoder->fmt_in.video.i_chroma = id->p_encoder->fmt_in.i_codec;
id->p_f_chain = filter_chain_New( p_stream, false, &owner );
filter_chain_Reset( id->p_f_chain, p_fmt_out, p_fmt_out );
/* Deinterlace */
......@@ -322,10 +318,7 @@ static void transcode_video_filter_init( sout_stream_t *p_stream,
if( p_stream->p_sys->psz_vf2 )
{
id->p_uf_chain = filter_chain_New( p_stream, "video filter2",
true,
transcode_video_filter_allocation_init,
NULL, p_stream->p_sys );
id->p_uf_chain = filter_chain_New( p_stream, true, &owner );
filter_chain_Reset( id->p_uf_chain, p_fmt_out,
&id->p_encoder->fmt_in );
if( p_fmt_out->video.i_chroma != id->p_encoder->fmt_in.video.i_chroma )
......
......@@ -49,7 +49,6 @@ vlc_module_end ()
* Local prototypes.
*****************************************************************************/
static picture_t *Chain ( filter_t *, picture_t * );
static int BufferAllocationInit ( filter_t *, void * );
static int BuildTransformChain( filter_t *p_filter );
static int BuildChromaResize( filter_t * );
......@@ -74,6 +73,23 @@ struct filter_sys_t
filter_chain_t *p_chain;
};
/*****************************************************************************
* Buffer management
*****************************************************************************/
static picture_t *BufferNew( filter_t *p_filter )
{
filter_t *p_parent = p_filter->owner.sys;
return filter_NewPicture( p_parent );
}
static void BufferDel( filter_t *p_filter, picture_t *p_pic )
{
filter_t *p_parent = p_filter->owner.sys;
filter_DeletePicture( p_parent, p_pic );
}
#define CHAIN_LEVEL_MAX 1
/*****************************************************************************
......@@ -99,7 +115,15 @@ static int Activate( vlc_object_t *p_this )
if( !p_sys )
return VLC_ENOMEM;
p_sys->p_chain = filter_chain_New( p_filter, "video filter2", false, BufferAllocationInit, NULL, p_filter );
filter_owner_t owner = {
.sys = p_filter,
.video = {
.buffer_new = BufferNew,
.buffer_del = BufferDel,
},
};
p_sys->p_chain = filter_chain_NewVideo( p_filter, false, &owner );
if( !p_sys->p_chain )
{
free( p_sys );
......@@ -261,31 +285,6 @@ exit:
return i_ret;
}
/*****************************************************************************
* Buffer management
*****************************************************************************/
static picture_t *BufferNew( filter_t *p_filter )
{
filter_t *p_parent = p_filter->owner.sys;
return filter_NewPicture( p_parent );
}
static void BufferDel( filter_t *p_filter, picture_t *p_pic )
{
filter_t *p_parent = p_filter->owner.sys;
filter_DeletePicture( p_parent, p_pic );
}
static int BufferAllocationInit ( filter_t *p_filter, void *p_data )
{
p_filter->owner.sys = p_data;
p_filter->owner.video.buffer_new = BufferNew;
p_filter->owner.video.buffer_del = BufferDel;
return VLC_SUCCESS;
}
/*****************************************************************************
*
*****************************************************************************/
......
......@@ -41,7 +41,6 @@
static int Activate( vlc_object_t * );
static void Destroy( vlc_object_t * );
static picture_t *Filter( filter_t *, picture_t * );
static int alloc_init( filter_t *, void * );
/* This module effectively implements a form of picture-in-picture.
* - The outer picture is called the canvas.
......@@ -132,6 +131,16 @@ struct filter_sys_t
filter_chain_t *p_chain;
};
static picture_t *video_new( filter_t *p_filter )
{
return filter_NewPicture( p_filter->owner.sys );
}
static void video_del( filter_t *p_filter, picture_t *p_pic )
{
return filter_DeletePicture( p_filter->owner.sys, p_pic );
}
/*****************************************************************************
*
*****************************************************************************/
......@@ -226,8 +235,15 @@ static int Activate( vlc_object_t *p_this )
return VLC_ENOMEM;
p_filter->p_sys = p_sys;
p_sys->p_chain = filter_chain_New( p_filter, "video filter2", true,
alloc_init, NULL, p_filter );
filter_owner_t owner = {
.sys = p_filter,
.video = {
.buffer_new = video_new,
.buffer_del = video_del,
},
};
p_sys->p_chain = filter_chain_NewVideo( p_filter, true, &owner );
if( !p_sys->p_chain )
{
msg_Err( p_filter, "Could not allocate filter chain" );
......@@ -364,24 +380,3 @@ static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
{
return filter_chain_VideoFilter( p_filter->p_sys->p_chain, p_pic );
}
/*****************************************************************************
*
*****************************************************************************/
static picture_t *video_new( filter_t *p_filter )
{
return filter_NewPicture( p_filter->owner.sys );
}
static void video_del( filter_t *p_filter, picture_t *p_pic )
{
return filter_DeletePicture( p_filter->owner.sys, p_pic );
}
static int alloc_init( filter_t *p_filter, void *p_data )
{
p_filter->owner.sys = p_data;
p_filter->owner.video.buffer_new = video_new;
p_filter->owner.video.buffer_del = video_del;
return VLC_SUCCESS;
}
......@@ -127,6 +127,7 @@ filter_chain_GetLength
filter_chain_MouseFilter
filter_chain_MouseEvent
filter_chain_New
filter_chain_NewVideo
filter_chain_Reset
filter_chain_SubFilter
filter_chain_VideoFilter
......
......@@ -31,14 +31,6 @@
#include <libvlc.h>
#include <assert.h>
typedef struct
{
int (*pf_init)( filter_t *, void *p_data ); /* Callback called once filter allocation has succeeded to initialize the filter's buffer allocation callbacks. This function is responsible for setting p_owner if needed. */
void (* pf_clean)( filter_t * ); /* Callback called on filter removal from chain to clean up buffer allocation callbacks data (ie p_owner) */
void *p_data; /* Data for pf_buffer_allocation_init */
} filter_chain_allocator_t;
typedef struct chained_filter_t
{
/* Public part of the filter structure */
......@@ -55,26 +47,11 @@ static inline chained_filter_t *chained (filter_t *filter)
return (chained_filter_t *)filter;
}
static int AllocatorInit( const filter_chain_allocator_t *,
chained_filter_t * );
static void AllocatorClean( const filter_chain_allocator_t *,
chained_filter_t * );
static bool IsInternalVideoAllocator( chained_filter_t * );
static int InternalVideoInit( filter_t *, void * );
static const filter_chain_allocator_t internal_video_allocator = {
.pf_init = InternalVideoInit,
.pf_clean = NULL,
.p_data = NULL,
};
/* */
struct filter_chain_t
{
vlc_object_t *p_this; /**< Owner object */
filter_chain_allocator_t allocator; /**< Owner allocation callbacks */
filter_owner_t callbacks; /**< Inner callbacks */
filter_owner_t owner; /**< Owner (downstream) callbacks */
chained_filter_t *first, *last; /**< List of filters */
......@@ -88,49 +65,96 @@ struct filter_chain_t
/**
* Local prototypes
*/
static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *,
const char *, config_chain_t *,
const es_format_t *, const es_format_t * );
static int filter_chain_DeleteFilterInternal( filter_chain_t *, filter_t * );
static int UpdateBufferFunctions( filter_chain_t * );
static void FilterDeletePictures( filter_t *, picture_t * );
static filter_chain_t *filter_chain_NewInner( const filter_owner_t *callbacks,
const char *cap, bool fmt_out_change, const filter_owner_t *owner )
{
assert( callbacks != NULL && callbacks->sys != NULL );
assert( cap != NULL );
filter_chain_t *chain = malloc( sizeof(*chain) + strlen( cap ) );
if( unlikely(chain == NULL) )
return NULL;
chain->callbacks = *callbacks;
if( owner != NULL )
chain->owner = *owner;
chain->first = NULL;
chain->last = NULL;
es_format_Init( &chain->fmt_in, UNKNOWN_ES, 0 );
es_format_Init( &chain->fmt_out, UNKNOWN_ES, 0 );
chain->length = 0;
chain->b_allow_fmt_out_change = fmt_out_change;
strcpy( chain->psz_capability, cap );
return chain;
}
#undef filter_chain_New
/**
* Filter chain initialisation
*/
filter_chain_t *filter_chain_New( vlc_object_t *p_this,
const char *psz_capability,
bool b_allow_fmt_out_change,
int (*pf_buffer_allocation_init)( filter_t *, void * ),
void (*pf_buffer_allocation_clean)( filter_t * ),
void *p_buffer_allocation_data )
filter_chain_t *filter_chain_New( vlc_object_t *obj, const char *cap,
bool fmt_out_change )
{
assert( p_this );
assert( psz_capability );
filter_owner_t callbacks = {
.sys = obj,
};
size_t size = sizeof(filter_chain_t) + strlen(psz_capability);
filter_chain_t *p_chain = malloc( size );
if( !p_chain )
return NULL;
return filter_chain_NewInner( &callbacks, cap, fmt_out_change, NULL );
}
p_chain->p_this = p_this;
p_chain->last = p_chain->first = NULL;
p_chain->length = 0;
strcpy( p_chain->psz_capability, psz_capability );
/** Chained filter picture allocator function */
static picture_t *filter_chain_VideoBufferNew( filter_t *filter )
{
if( chained(filter)->next != NULL )
{
picture_t *pic = picture_NewFromFormat( &filter->fmt_out.video );
if( pic == NULL )
msg_Err( filter, "Failed to allocate picture" );
return pic;
}
else
{
filter_chain_t *chain = filter->owner.sys;
/* XXX ugly */
filter->owner.sys = chain->owner.sys;
picture_t *pic = chain->owner.video.buffer_new( filter );
filter->owner.sys = chain;
return pic;
}
}
static void filter_chain_VideoBufferDelete( filter_t *filter, picture_t *pic )
{
if( chained(filter)->next != NULL )
picture_Release( pic );
else
{
filter_chain_t *chain = filter->owner.sys;
es_format_Init( &p_chain->fmt_in, UNKNOWN_ES, 0 );
es_format_Init( &p_chain->fmt_out, UNKNOWN_ES, 0 );
p_chain->b_allow_fmt_out_change = b_allow_fmt_out_change;
chain->owner.video.buffer_del( filter, pic );
}
}
p_chain->allocator.pf_init = pf_buffer_allocation_init;
p_chain->allocator.pf_clean = pf_buffer_allocation_clean;
p_chain->allocator.p_data = p_buffer_allocation_data;
#undef filter_chain_NewVideo
filter_chain_t *filter_chain_NewVideo( vlc_object_t *obj, bool allow_change,
const filter_owner_t *restrict owner )
{
filter_owner_t callbacks = {
.sys = obj,
.video = {
.buffer_new = filter_chain_VideoBufferNew,
.buffer_del = filter_chain_VideoBufferDelete,
},
};
return p_chain;
return filter_chain_NewInner( &callbacks, "video filter2", allow_change,
owner );
}
/**
......@@ -138,7 +162,8 @@ filter_chain_t *filter_chain_New( vlc_object_t *p_this,
*/
void filter_chain_Delete( filter_chain_t *p_chain )
{
filter_chain_Reset( p_chain, NULL, NULL );
while( p_chain->first != NULL )
filter_chain_DeleteFilterInternal( p_chain, &p_chain->first->filter );
es_format_Clean( &p_chain->fmt_in );
es_format_Clean( &p_chain->fmt_out );
......@@ -166,22 +191,87 @@ void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in,
}
}
filter_t *filter_chain_AppendFilter( filter_chain_t *p_chain,
const char *psz_name,
config_chain_t *p_cfg,
const es_format_t *p_fmt_in,
const es_format_t *p_fmt_out )
filter_t *filter_chain_AppendFilter( filter_chain_t *chain, const char *name,
config_chain_t *cfg,
const es_format_t *fmt_in,
const es_format_t *fmt_out )
{
filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name,
p_cfg, p_fmt_in,
p_fmt_out );
if( UpdateBufferFunctions( p_chain ) < 0 )
msg_Err( p_filter, "Woah! This doesn't look good." );
return p_filter;
vlc_object_t *parent = chain->callbacks.sys;
chained_filter_t *chained =
vlc_custom_create( parent, sizeof(*chained), "filter" );
if( unlikely(chained == NULL) )
return NULL;
filter_t *filter = &chained->filter;
if( fmt_in == NULL )
{
if( chain->last != NULL )
fmt_in = &chain->last->filter.fmt_out;
else
fmt_in = &chain->fmt_in;
}
if( fmt_out == NULL )
fmt_out = &chain->fmt_out;
es_format_Copy( &filter->fmt_in, fmt_in );
es_format_Copy( &filter->fmt_out, fmt_out );
filter->b_allow_fmt_out_change = chain->b_allow_fmt_out_change;
filter->p_cfg = cfg;
filter->owner = chain->callbacks;
filter->owner.sys = chain;
filter->p_module = module_need( filter, chain->psz_capability, name,
name != NULL );
if( filter->p_module == NULL )
goto error;
if( filter->b_allow_fmt_out_change )
{
es_format_Clean( &chain->fmt_out );
es_format_Copy( &chain->fmt_out, &filter->fmt_out );
}
if( chain->last == NULL )
{
assert( chain->first == NULL );
chain->first = chained;
}
else
chain->last->next = chained;
chained->prev = chain->last;
chain->last = chained;
chained->next = NULL;
chain->length++;
vlc_mouse_t *mouse = malloc( sizeof(*mouse) );
if( likely(mouse != NULL) )
vlc_mouse_Init( mouse );
chained->mouse = mouse;
chained->pending = NULL;
msg_Dbg( parent, "Filter '%s' (%p) appended to chain",
(name != NULL) ? name : module_get_name(filter->p_module, false),
filter );
return filter;
error:
if( name != NULL )
msg_Err( parent, "Failed to create %s '%s'", chain->psz_capability,
name );
else
msg_Err( parent, "Failed to create %s", chain->psz_capability );
es_format_Clean( &filter->fmt_out );
es_format_Clean( &filter->fmt_in );
vlc_object_release( filter );
return NULL;
}
int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
{
vlc_object_t *obj = chain->callbacks.sys;
char *buf = NULL;
int ret = 0;
......@@ -196,12 +286,11 @@ int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
free( buf );
buf = next;
filter_t *filter = filter_chain_AppendFilterInternal( chain, name, cfg,
NULL, NULL );
filter_t *filter = filter_chain_AppendFilter( chain, name, cfg,
NULL, NULL );
if( filter == NULL )
{
msg_Err( chain->p_this, "Failed while trying to append '%s' "
"to filter chain", name );
msg_Err( obj, "Failed to append '%s' to chain", name );
free( name );
free( cfg );
goto error;
......@@ -211,8 +300,6 @@ int filter_chain_AppendFromString( filter_chain_t *chain, const char *str )
ret++;
}
if( UpdateBufferFunctions( chain ) )
assert( 0 ); /* should never happen, vf2 alloc cannot fail */
free( buf );
return ret;
......@@ -231,9 +318,7 @@ int filter_chain_DeleteFilter( filter_chain_t *p_chain, filter_t *p_filter )
const int i_ret = filter_chain_DeleteFilterInternal( p_chain, p_filter );
if( i_ret < 0 )
return i_ret;
/* FIXME That one seems bad if a error is returned */
return UpdateBufferFunctions( p_chain );
return VLC_SUCCESS;
}
int filter_chain_ForEach( filter_chain_t *chain,
......@@ -407,93 +492,10 @@ int filter_chain_MouseEvent( filter_chain_t *p_chain,
}
/* Helpers */
static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *p_chain,
const char *psz_name,
config_chain_t *p_cfg,
const es_format_t *p_fmt_in,
const es_format_t *p_fmt_out )
{
chained_filter_t *p_chained =
vlc_custom_create( p_chain->p_this, sizeof(*p_chained), "filter" );
filter_t *p_filter = &p_chained->filter;
if( !p_filter )
return NULL;
if( !p_fmt_in )
{
if( p_chain->last != NULL )
p_fmt_in = &p_chain->last->filter.fmt_out;
else
p_fmt_in = &p_chain->fmt_in;
}
if( !p_fmt_out )
{
p_fmt_out = &p_chain->fmt_out;
}
es_format_Copy( &p_filter->fmt_in, p_fmt_in );
es_format_Copy( &p_filter->fmt_out, p_fmt_out );
p_filter->p_cfg = p_cfg;
p_filter->b_allow_fmt_out_change = p_chain->b_allow_fmt_out_change;
p_filter->p_module = module_need( p_filter, p_chain->psz_capability,
psz_name, psz_name != NULL );
if( !p_filter->p_module )
goto error;
if( p_filter->b_allow_fmt_out_change )
{
es_format_Clean( &p_chain->fmt_out );
es_format_Copy( &p_chain->fmt_out, &p_filter->fmt_out );
}
if( AllocatorInit( &p_chain->allocator, p_chained ) )
goto error;
if( p_chain->last == NULL )