[vlc-commits] [Git][videolan/vlc][master] 18 commits: sout: mosaic_bridge: remove decoder fmt out extra copy

Steve Lhomme (@robUx4) gitlab at videolan.org
Fri Dec 15 06:46:17 UTC 2023



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
0fcb1b0e by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: remove decoder fmt out extra copy

The output format will be erased by the update format callback of the
decoder anyway.

- - - - -
e701c20c by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: remove redundant initialization

Already set NULL in `decoder_Init`.

- - - - -
909f39ac by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: store updated decoder fmt

Accessing the decoder output format from the queue video callback is
racy in some decoder implementation. Let's create a copy and store it in
the sout context.

- - - - -
00ac367e by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: skip redundant update_format events

Some decoder still send update_format events when the format is
un-changed. Let's avoid recreating the filter chain and copying the
format in that case.
We still need a filter chain reset if the video context changes though,
so we need to hold a reference internally.

- - - - -
140c2c04 by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: rename filter chain variable

- - - - -
cd334864 by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: enhance debug output

- - - - -
994561dd by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: store the filter chain config

Instead of querying it every time.

- - - - -
37427dc2 by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: remove unnecessary checks

Send, Flush and Del cannot be called on an invalid id and we only allow
one ES added at at time.

- - - - -
c193f70d by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: return the decoder as the ID

Previous code was returning sout_sys as an ES ID. It is simpler to only
return the owned decoder, which is the actual object allocated by the
`Add` callback.

- - - - -
28cd5c0b by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: move decoder owned var to owner

Those variable have the same lifetime as the decoder owner or should
only be used when its created.
For var callbacks, It requires taking the global lock but, for most of
those variable callbacks where setting values read by other modules
without those being atomic so it is likely that taking the lock was
necessary to avoid potential race conditions anyway.

- - - - -
ac732fe4 by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: always create the filter chain

Always creating the filter chain lighten the nesting and avoids a lot of
NULL checking. Manipulating empty filter chains if the user does not
specify any filter is fine.

This is preparation work to actually use the filter chain to do the
chroma resampling and rescaling instead of doing it manually in this
module.

- - - - -
7c35391d by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: move rescaling to a separate function

No functional changes.

- - - - -
00495c82 by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: default chroma value at initialization

- - - - -
ced9721a by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: convert pictures with the filter chain

Instead of handling the scaling and chroma conversion separately via the
image API, prepend the already existing filter chain with a converter.
This gets closer to what we have in transcode and remove the needs for
the picture API here. It also avoids computing the scaling values at
every frame queued.

Variable callbacks needed to be adapted as the filter chain has to be
recreated at the decoder queue level for that use case. A local lock is
introduced to avoid taking advantage of the global lock just to set
variables that are local to the module but still need locking for the
owner access.

- - - - -
b7c09b72 by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: remove unnecessary variable

- - - - -
a6876a5e by Alaric Senat at 2023-12-15T06:28:21+00:00
mosaic: constify bridge ES ID

This string is owned by the mosaic bridge and should not be modified.

- - - - -
022a87d3 by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: move module descriptor to the bottom

- - - - -
9ca953e1 by Alaric Senat at 2023-12-15T06:28:21+00:00
sout: mosaic_bridge: switch to `vlc_frame_t`

- - - - -


2 changed files:

- modules/spu/mosaic.h
- modules/stream_out/mosaic_bridge.c


Changes:

=====================================
modules/spu/mosaic.h
=====================================
@@ -26,7 +26,7 @@ typedef struct bridged_es_t
     es_format_t fmt;
     vlc_picture_chain_t pictures;
     bool b_empty;
-    char *psz_id;
+    const char *psz_id;
 
     int i_alpha;
     int i_x;


=====================================
modules/stream_out/mosaic_bridge.c
=====================================
@@ -34,11 +34,9 @@
 #include <vlc_configuration.h>
 #include <vlc_plugin.h>
 #include <vlc_sout.h>
-#include <vlc_block.h>
 #include <vlc_codec.h>
 #include <vlc_meta.h>
 
-#include <vlc_image.h>
 #include <vlc_filter.h>
 #include <vlc_modules.h>
 
@@ -47,29 +45,37 @@
 /*****************************************************************************
  * Local structures
  *****************************************************************************/
-typedef struct
+
+struct decoder_owner
 {
+    decoder_t dec;
+    es_format_t fmt_in;
+    es_format_t fmt_out;
+    vlc_decoder_device *dec_dev;
+    vlc_video_context *vctx;
+    sout_stream_t *p_stream;
+
+    filter_chain_t *filters;
+    bool need_filter_reset;
+
     bridged_es_t *p_es;
+};
 
-    decoder_t       *p_decoder;
-    image_handler_t *p_image; /* filter for resizing */
+typedef struct
+{
+    struct decoder_owner *decoder_ref;
     int i_height, i_width;
     unsigned int i_sar_num, i_sar_den;
     char *psz_id;
-    bool b_inited;
 
     vlc_fourcc_t i_chroma; /* force image format chroma */
 
-    filter_chain_t *p_vf2;
+    char *filters_config;
+
+    vlc_mutex_t var_lock;
 } sout_stream_sys_t;
 
-struct decoder_owner
-{
-    decoder_t dec;
-    es_format_t fmt_in;
-    vlc_decoder_device *dec_dev;
-    sout_stream_t *p_stream;
-};
+#define CFG_PREFIX "sout-mosaic-bridge-"
 
 static inline struct decoder_owner *dec_get_owner( decoder_t *p_dec )
 {
@@ -79,94 +85,11 @@ static inline struct decoder_owner *dec_get_owner( decoder_t *p_dec )
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static int  Open    ( vlc_object_t * );
-static void *Add( sout_stream_t *, const es_format_t *, const char * );
-static void  Del( sout_stream_t *, void * );
-static int   Send( sout_stream_t *, void *, block_t * );
 
 static void decoder_queue_video( decoder_t *p_dec, picture_t *p_pic );
 static int video_update_format_decoder( decoder_t *p_dec, vlc_video_context * );
 static picture_t *video_new_buffer_filter( filter_t * );
 
-static int HeightCallback( vlc_object_t *, char const *,
-                           vlc_value_t, vlc_value_t, void * );
-static int WidthCallback( vlc_object_t *, char const *,
-                          vlc_value_t, vlc_value_t, void * );
-static int alphaCallback( vlc_object_t *, char const *,
-                          vlc_value_t, vlc_value_t, void * );
-static int xCallback( vlc_object_t *, char const *,
-                      vlc_value_t, vlc_value_t, void * );
-static int yCallback( vlc_object_t *, char const *,
-                      vlc_value_t, vlc_value_t, void * );
-
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-#define ID_TEXT N_("ID")
-#define ID_LONGTEXT N_( \
-    "Specify an identifier string for this subpicture" )
-
-#define WIDTH_TEXT N_("Video width")
-#define WIDTH_LONGTEXT N_( \
-    "Output video width." )
-#define HEIGHT_TEXT N_("Video height")
-#define HEIGHT_LONGTEXT N_( \
-    "Output video height." )
-#define RATIO_TEXT N_("Sample aspect ratio")
-#define RATIO_LONGTEXT N_( \
-    "Sample aspect ratio of the destination (1:1, 3:4, 2:3)." )
-
-#define VFILTER_TEXT N_("Video filter")
-#define VFILTER_LONGTEXT N_( \
-    "Video filters will be applied to the video stream." )
-
-#define CHROMA_TEXT N_("Image chroma")
-#define CHROMA_LONGTEXT N_( \
-    "Force the use of a specific chroma. Use YUVA if you're planning " \
-    "to use the Alphamask or Bluescreen video filter." )
-
-#define ALPHA_TEXT N_("Transparency")
-#define ALPHA_LONGTEXT N_( \
-    "Transparency of the mosaic picture." )
-
-#define X_TEXT N_("X offset")
-#define X_LONGTEXT N_( \
-    "X coordinate of the upper left corner in the mosaic if non negative." )
-
-#define Y_TEXT N_("Y offset")
-#define Y_LONGTEXT N_( \
-    "Y coordinate of the upper left corner in the mosaic if non negative." )
-
-#define CFG_PREFIX "sout-mosaic-bridge-"
-
-vlc_module_begin ()
-    set_shortname( N_( "Mosaic bridge" ) )
-    set_description(N_("Mosaic bridge stream output") )
-    set_capability( "sout output", 0 )
-    add_shortcut( "mosaic-bridge" )
-
-    set_subcategory( SUBCAT_SOUT_STREAM )
-
-    add_string( CFG_PREFIX "id", "Id", ID_TEXT, ID_LONGTEXT )
-    add_integer( CFG_PREFIX "width", 0, WIDTH_TEXT,
-                 WIDTH_LONGTEXT )
-    add_integer( CFG_PREFIX "height", 0, HEIGHT_TEXT,
-                 HEIGHT_LONGTEXT )
-    add_string( CFG_PREFIX "sar", "1:1", RATIO_TEXT,
-                RATIO_LONGTEXT )
-    add_string( CFG_PREFIX "chroma", NULL, CHROMA_TEXT, CHROMA_LONGTEXT )
-
-    add_module_list(CFG_PREFIX "vfilter", "video filter", NULL,
-                    VFILTER_TEXT, VFILTER_LONGTEXT)
-
-    add_integer_with_range( CFG_PREFIX "alpha", 255, 0, 255,
-                            ALPHA_TEXT, ALPHA_LONGTEXT )
-    add_integer( CFG_PREFIX "x", -1, X_TEXT, X_LONGTEXT )
-    add_integer( CFG_PREFIX "y", -1, Y_TEXT, Y_LONGTEXT )
-
-    set_callback( Open )
-vlc_module_end ()
-
 static int Control(sout_stream_t *stream, int query, va_list args)
 {
     (void) stream;
@@ -186,130 +109,11 @@ static int Control(sout_stream_t *stream, int query, va_list args)
 
 static void Flush( sout_stream_t *stream, void *id)
 {
-    sout_stream_sys_t *p_sys = stream->p_sys;
+    struct decoder_owner *owner = id;
 
-    if ( p_sys == (sout_stream_sys_t *)id )
-    {
-        if (p_sys->p_vf2 != NULL)
-            filter_chain_VideoFlush(p_sys->p_vf2);
-    }
-}
+    filter_chain_VideoFlush( owner->filters );
 
-/*****************************************************************************
- * Close
- *****************************************************************************/
-static void Close( sout_stream_t *p_stream )
-{
-    sout_stream_sys_t *p_sys = p_stream->p_sys;
-
-    /* Delete the callbacks */
-    var_DelCallback( p_stream, CFG_PREFIX "height", HeightCallback, p_stream );
-    var_DelCallback( p_stream, CFG_PREFIX "width", WidthCallback, p_stream );
-    var_DelCallback( p_stream, CFG_PREFIX "alpha", alphaCallback, p_stream );
-    var_DelCallback( p_stream, CFG_PREFIX "x", xCallback, p_stream );
-    var_DelCallback( p_stream, CFG_PREFIX "y", yCallback, p_stream );
-
-    free( p_sys->psz_id );
-
-    free( p_sys );
-}
-
-static const struct sout_stream_operations ops = {
-    .add = Add,
-    .del = Del,
-    .send = Send,
-    .control = Control,
-    .flush = Flush,
-    .close = Close,
-};
-
-static const char *const ppsz_sout_options[] = {
-    "id", "width", "height", "sar", "vfilter", "chroma", "alpha", "x", "y", NULL
-};
-
-/*****************************************************************************
- * Open
- *****************************************************************************/
-static int Open( vlc_object_t *p_this )
-{
-    sout_stream_t        *p_stream = (sout_stream_t *)p_this;
-    sout_stream_sys_t    *p_sys;
-    vlc_value_t           val;
-
-    config_ChainParse( p_stream, CFG_PREFIX, ppsz_sout_options,
-                       p_stream->p_cfg );
-
-    p_sys = malloc( sizeof( sout_stream_sys_t ) );
-    if( !p_sys )
-        return VLC_ENOMEM;
-
-    p_stream->p_sys = p_sys;
-    p_sys->b_inited = false;
-
-    p_sys->psz_id = var_CreateGetString( p_stream, CFG_PREFIX "id" );
-
-    p_sys->i_height =
-        var_CreateGetIntegerCommand( p_stream, CFG_PREFIX "height" );
-    var_AddCallback( p_stream, CFG_PREFIX "height", HeightCallback, p_stream );
-
-    p_sys->i_width =
-        var_CreateGetIntegerCommand( p_stream, CFG_PREFIX "width" );
-    var_AddCallback( p_stream, CFG_PREFIX "width", WidthCallback, p_stream );
-
-    var_Get( p_stream, CFG_PREFIX "sar", &val );
-    if( val.psz_string )
-    {
-        char *psz_parser = strchr( val.psz_string, ':' );
-
-        if( psz_parser )
-        {
-            *psz_parser++ = '\0';
-            p_sys->i_sar_num = atoi( val.psz_string );
-            p_sys->i_sar_den = atoi( psz_parser );
-            vlc_ureduce( &p_sys->i_sar_num, &p_sys->i_sar_den,
-                         p_sys->i_sar_num, p_sys->i_sar_den, 0 );
-        }
-        else
-        {
-            msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string );
-            p_sys->i_sar_num = p_sys->i_sar_den = 1;
-        }
-
-        free( val.psz_string );
-    }
-    else
-    {
-        p_sys->i_sar_num = p_sys->i_sar_den = 1;
-    }
-
-    p_sys->i_chroma = 0;
-    val.psz_string = var_GetNonEmptyString( p_stream, CFG_PREFIX "chroma" );
-    if( val.psz_string && strlen( val.psz_string ) >= 4 )
-    {
-        memcpy( &p_sys->i_chroma, val.psz_string, 4 );
-        msg_Dbg( p_stream, "Forcing image chroma to 0x%.8x (%4.4s)", p_sys->i_chroma, (char*)&p_sys->i_chroma );
-        if ( vlc_fourcc_GetChromaDescription( p_sys->i_chroma ) == NULL )
-        {
-            msg_Err( p_stream, "Unknown chroma" );
-            free( p_sys );
-            return VLC_EINVAL;
-        }
-    }
-    free( val.psz_string );
-
-#define INT_COMMAND( a ) do { \
-    var_Create( p_stream, CFG_PREFIX #a, \
-                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND ); \
-    var_AddCallback( p_stream, CFG_PREFIX #a, a ## Callback, \
-                     p_stream ); } while(0)
-    INT_COMMAND( alpha );
-    INT_COMMAND( x );
-    INT_COMMAND( y );
-
-#undef INT_COMMAND
-
-    p_stream->ops = &ops;
-    return VLC_SUCCESS;
+    (void)stream;
 }
 
 static vlc_decoder_device * MosaicHoldDecoderDevice( struct decoder_owner *p_owner )
@@ -339,9 +143,16 @@ static void ReleaseDecoder( decoder_t *p_dec )
         p_owner->dec_dev = NULL;
     }
     es_format_Clean( &p_owner->fmt_in );
-    decoder_Destroy( p_dec );
+    es_format_Clean( &p_owner->fmt_out );
+    decoder_Clean( p_dec );
+    if ( p_owner->filters != NULL )
+        filter_chain_Delete( p_owner->filters );
+    if ( p_owner->vctx != NULL )
+        vlc_video_context_Release( p_owner->vctx );
+    vlc_object_delete( p_dec );
 }
 
+
 static vlc_decoder_device * video_filter_hold_device(vlc_object_t *o, void *sys)
 {
     VLC_UNUSED(o);
@@ -355,24 +166,17 @@ Add( sout_stream_t *p_stream, const es_format_t *p_fmt, const char *es_id )
     sout_stream_sys_t *p_sys = p_stream->p_sys;
     bridge_t *p_bridge;
     bridged_es_t *p_es;
-    char *psz_chain;
     int i;
 
-    if( p_sys->b_inited || p_fmt->i_cat != VIDEO_ES )
+    if( p_sys->decoder_ref != NULL || p_fmt->i_cat != VIDEO_ES )
         return NULL;
 
     /* Create decoder object */
     struct decoder_owner *p_owner = vlc_object_create( p_stream, sizeof( *p_owner ) );
     if( !p_owner )
         return NULL;
-    p_sys->p_decoder = &p_owner->dec;
-    decoder_Init( p_sys->p_decoder, &p_owner->fmt_in, p_fmt );
-
-    p_sys->p_decoder->b_frame_drop_allowed = true;
-    p_sys->p_decoder->fmt_out = *p_sys->p_decoder->fmt_in;
-    p_sys->p_decoder->fmt_out.i_extra = 0;
-    p_sys->p_decoder->fmt_out.p_extra = 0;
-    p_sys->p_decoder->pf_decode = NULL;
+    decoder_Init( &p_owner->dec, &p_owner->fmt_in, p_fmt );
+    p_owner->dec.b_frame_drop_allowed = true;
 
     /* Create user specified video filters */
     static const struct filter_video_callbacks cbs =
@@ -380,22 +184,21 @@ Add( sout_stream_t *p_stream, const es_format_t *p_fmt, const char *es_id )
         video_new_buffer_filter, video_filter_hold_device,
     };
 
-    psz_chain = var_GetNonEmptyString( p_stream, CFG_PREFIX "vfilter" );
-    msg_Dbg( p_stream, "psz_chain: '%s'", psz_chain ? psz_chain : "");
-    if( psz_chain )
-    {
-        filter_owner_t owner = {
-            .video = &cbs,
-            .sys = p_owner,
-        };
+    msg_Dbg( p_stream,
+             "User filter config chain: '%s'",
+             (p_sys->filters_config != NULL) ? p_sys->filters_config : "" );
+    filter_owner_t owner = {
+        .video = &cbs,
+        .sys = p_owner,
+    };
 
-        p_sys->p_vf2 = filter_chain_NewVideo( p_stream, false, &owner );
-        free( psz_chain );
-    }
-    else
+    p_owner->filters = filter_chain_NewVideo( p_stream, false, &owner );
+    if( unlikely(p_owner->filters == NULL) )
     {
-        p_sys->p_vf2 = NULL;
+        ReleaseDecoder(&p_owner->dec);
+        return NULL;
     }
+    p_owner->need_filter_reset = true;
 
     static const struct decoder_owner_callbacks dec_cbs =
     {
@@ -405,22 +208,22 @@ Add( sout_stream_t *p_stream, const es_format_t *p_fmt, const char *es_id )
             .queue = decoder_queue_video,
         },
     };
-    p_sys->p_decoder->cbs = &dec_cbs;
+    p_owner->dec.cbs = &dec_cbs;
 
+    es_format_Init( &p_owner->fmt_out, VIDEO_ES, 0 );
     p_owner->p_stream = p_stream;
-    //p_sys->p_decoder->p_cfg = p_sys->p_video_cfg;
+    p_owner->vctx = NULL;
 
-    p_sys->p_decoder->p_module =
-        module_need_var( p_sys->p_decoder, "video decoder", "codec" );
+    p_owner->dec.p_module =
+        module_need_var( &p_owner->dec, "video decoder", "codec" );
 
-    if( !p_sys->p_decoder->p_module )
+    if( p_owner->dec.p_module == NULL )
     {
         msg_Err( p_stream, "cannot find decoder" );
-        ReleaseDecoder( p_sys->p_decoder );
+        ReleaseDecoder( &p_owner->dec );
         return NULL;
     }
 
-    p_sys->b_inited = true;
     vlc_global_lock( VLC_MOSAIC_MUTEX );
 
     p_bridge = GetBridge( p_stream );
@@ -453,7 +256,10 @@ Add( sout_stream_t *p_stream, const es_format_t *p_fmt, const char *es_id )
         p_bridge->pp_es[i] = xmalloc( sizeof(bridged_es_t) );
     }
 
-    p_sys->p_es = p_es = p_bridge->pp_es[i];
+    p_owner->p_es = p_es = p_bridge->pp_es[i];
+    vlc_mutex_lock( &p_sys->var_lock );
+    p_sys->decoder_ref = p_owner;
+    vlc_mutex_unlock( &p_sys->var_lock );
 
     p_es->i_alpha = var_GetInteger( p_stream, CFG_PREFIX "alpha" );
     p_es->i_x = var_GetInteger( p_stream, CFG_PREFIX "x" );
@@ -466,43 +272,25 @@ Add( sout_stream_t *p_stream, const es_format_t *p_fmt, const char *es_id )
 
     vlc_global_unlock( VLC_MOSAIC_MUTEX );
 
-    if ( p_sys->i_height || p_sys->i_width )
-    {
-        p_sys->p_image = image_HandlerCreate( p_stream );
-    }
-    else
-    {
-        p_sys->p_image = NULL;
-    }
-
     msg_Dbg( p_stream, "mosaic bridge id=%s pos=%d", p_es->psz_id, i );
 
-    return p_sys;
+    return p_owner;
     (void)es_id;
 }
 
 static void Del( sout_stream_t *p_stream, void *id )
 {
-    VLC_UNUSED(id);
     sout_stream_sys_t *p_sys = p_stream->p_sys;
+    struct decoder_owner *owner = id;
     bridge_t *p_bridge;
     bridged_es_t *p_es;
     bool b_last_es = true;
     int i;
 
-    if( !p_sys->b_inited )
-        return;
-
-    ReleaseDecoder( p_sys->p_decoder );
-
-    /* Destroy user specified video filters */
-    if( p_sys->p_vf2 )
-        filter_chain_Delete( p_sys->p_vf2 );
-
     vlc_global_lock( VLC_MOSAIC_MUTEX );
 
     p_bridge = GetBridge( p_stream );
-    p_es = p_sys->p_es;
+    p_es = owner->p_es;
 
     p_es->b_empty = true;
     while ( !vlc_picture_chain_IsEmpty( &p_es->pictures ) )
@@ -530,135 +318,138 @@ static void Del( sout_stream_t *p_stream, void *id )
         var_Destroy( p_libvlc, "mosaic-struct" );
     }
 
+    vlc_mutex_lock( &p_sys->var_lock );
+    p_sys->decoder_ref = NULL;
+    vlc_mutex_unlock( &p_sys->var_lock );
+    ReleaseDecoder( &owner->dec );
+
     vlc_global_unlock( VLC_MOSAIC_MUTEX );
+}
 
-    if ( p_sys->p_image )
+static void ApplyRescale( video_format_t *dst,
+                          const video_format_t *src,
+                          const sout_stream_sys_t *sys )
+{
+    const unsigned dec_out_aspect = (int64_t)VOUT_ASPECT_FACTOR *
+                                    src->i_sar_num * src->i_width /
+                                    (src->i_sar_den * src->i_height);
+    if ( sys->i_height == 0 )
     {
-        image_HandlerDelete( p_sys->p_image );
+        dst->i_width = sys->i_width;
+        dst->i_height = (sys->i_width * VOUT_ASPECT_FACTOR * sys->i_sar_num /
+                         sys->i_sar_den / dec_out_aspect) &
+                        ~0x1;
     }
-
-    p_sys->b_inited = false;
+    else if ( sys->i_width == 0 )
+    {
+        dst->i_height = sys->i_height;
+        dst->i_width = (sys->i_height * dec_out_aspect * sys->i_sar_den /
+                        sys->i_sar_num / VOUT_ASPECT_FACTOR) &
+                       ~0x1;
+    }
+    else
+    {
+        dst->i_width = sys->i_width;
+        dst->i_height = sys->i_height;
+        dst->i_sar_num = sys->i_sar_num;
+        dst->i_sar_den = sys->i_sar_den;
+    }
+    dst->i_visible_width = dst->i_width;
+    dst->i_visible_height = dst->i_height;
 }
 
-static void decoder_queue_video( decoder_t *p_dec, picture_t *p_pic )
+static int ResetFilterChain(struct decoder_owner *owner)
 {
-    struct decoder_owner *p_owner = dec_get_owner( p_dec );
-    sout_stream_t *p_stream = p_owner->p_stream;
-    sout_stream_sys_t *p_sys = p_stream->p_sys;
-    picture_t *p_new_pic;
-    const video_format_t *p_fmt_in = &p_sys->p_decoder->fmt_out.video;
+    sout_stream_sys_t *sys = owner->p_stream->p_sys;
+    es_format_t rescaled;
+    es_format_InitFromVideo( &rescaled, &owner->fmt_out.video );
 
-    if( p_sys->i_height || p_sys->i_width )
-    {
-        video_format_t fmt_out;
+    rescaled.video.i_chroma = sys->i_chroma;
+    if ( sys->i_width != 0 || sys->i_height != 0 )
+        ApplyRescale( &rescaled.video, &owner->fmt_out.video, sys );
 
-        video_format_Init( &fmt_out, p_sys->i_chroma ? p_sys->i_chroma : VLC_CODEC_I420 );
+    filter_chain_Reset(
+        owner->filters, &owner->fmt_out, owner->vctx, &rescaled );
 
-        const unsigned i_fmt_in_aspect =
-            (int64_t)VOUT_ASPECT_FACTOR *
-            p_fmt_in->i_sar_num * p_fmt_in->i_width /
-            (p_fmt_in->i_sar_den * p_fmt_in->i_height);
-        if ( !p_sys->i_height )
-        {
-            fmt_out.i_width = p_sys->i_width;
-            fmt_out.i_height = (p_sys->i_width * VOUT_ASPECT_FACTOR
-                * p_sys->i_sar_num / p_sys->i_sar_den / i_fmt_in_aspect)
-                  & ~0x1;
-        }
-        else if ( !p_sys->i_width )
-        {
-            fmt_out.i_height = p_sys->i_height;
-            fmt_out.i_width = (p_sys->i_height * i_fmt_in_aspect
-                * p_sys->i_sar_den / p_sys->i_sar_num / VOUT_ASPECT_FACTOR)
-                  & ~0x1;
-        }
-        else
-        {
-            fmt_out.i_width = p_sys->i_width;
-            fmt_out.i_height = p_sys->i_height;
-        }
-        fmt_out.i_visible_width = fmt_out.i_width;
-        fmt_out.i_visible_height = fmt_out.i_height;
+    int status = filter_chain_AppendConverter( owner->filters, &rescaled );
+    if ( status != VLC_SUCCESS )
+        goto end;
 
-        p_new_pic = image_Convert( p_sys->p_image,
-                                   p_pic, p_fmt_in, &fmt_out );
-        video_format_Clean( &fmt_out );
-        if( p_new_pic == NULL )
-        {
-            msg_Err( p_stream, "image conversion failed" );
-            picture_Release( p_pic );
-            return;
-        }
-    }
-    else
+    if ( sys->filters_config != NULL )
+        status = filter_chain_AppendFromString( owner->filters, sys->filters_config );
+
+    if ( status >= 0 )
     {
-        /* TODO: chroma conversion if needed */
-        video_format_t pic_fmt = p_pic->format;
-        pic_fmt.i_sar_num = p_fmt_in->i_sar_num;
-        pic_fmt.i_sar_den = p_fmt_in->i_sar_den;
+        owner->need_filter_reset = false;
+        status = VLC_SUCCESS;
+    }
 
-        p_new_pic = picture_NewFromFormat( &pic_fmt );
-        if( !p_new_pic )
-        {
-            picture_Release( p_pic );
-            msg_Err( p_stream, "image allocation failed" );
-            return;
-        }
+end:
+    es_format_Clean( &rescaled );
+    return status;
+}
+
+static void decoder_queue_video( decoder_t *p_dec, picture_t *p_pic )
+{
+    struct decoder_owner *p_owner = dec_get_owner( p_dec );
+    sout_stream_sys_t *sys = p_owner->p_stream->p_sys;
 
-        picture_Copy( p_new_pic, p_pic );
+    int status = VLC_SUCCESS;
+    vlc_mutex_lock( &sys->var_lock );
+    if ( p_owner->need_filter_reset )
+    {
+        status = ResetFilterChain( p_owner );
+    }
+    vlc_mutex_unlock( &sys->var_lock );
+
+    if ( status != VLC_SUCCESS )
+    {
+        picture_Release( p_pic );
+        return;
     }
-    picture_Release( p_pic );
 
-    if( p_sys->p_vf2 )
-        p_new_pic = filter_chain_VideoFilter( p_sys->p_vf2, p_new_pic );
+    p_pic = filter_chain_VideoFilter( p_owner->filters, p_pic );
 
     /* push the picture in the mosaic-struct structure */
-    bridged_es_t *p_es = p_sys->p_es;
+    bridged_es_t *p_es = p_owner->p_es;
     vlc_global_lock( VLC_MOSAIC_MUTEX );
-    vlc_picture_chain_Append( &p_es->pictures, p_new_pic );
+    vlc_picture_chain_Append( &p_es->pictures, p_pic );
     vlc_global_unlock( VLC_MOSAIC_MUTEX );
 }
 
-static int Send( sout_stream_t *p_stream, void *id, block_t *p_buffer )
+static int Send( sout_stream_t *p_stream, void *id, vlc_frame_t *frame )
 {
-    sout_stream_sys_t *p_sys = p_stream->p_sys;
-
-    if ( (sout_stream_sys_t *)id != p_sys )
-    {
-        block_ChainRelease( p_buffer );
-        return VLC_SUCCESS;
-    }
+    struct decoder_owner *owner = id;
 
-    int ret = p_sys->p_decoder->pf_decode( p_sys->p_decoder, p_buffer );
+    int ret = owner->dec.pf_decode( &owner->dec, frame );
     return ret == VLCDEC_SUCCESS ? VLC_SUCCESS : VLC_EGENERIC;
+    (void)p_stream;
 }
 
 static int video_update_format_decoder( decoder_t *p_dec, vlc_video_context *vctx )
 {
     struct decoder_owner *p_owner = dec_get_owner( p_dec );
-    sout_stream_sys_t *p_sys = p_owner->p_stream->p_sys;
-    if ( p_sys->p_vf2 )
+
+    if ( video_format_IsSimilar(&p_dec->fmt_out.video,
+                                &p_owner->fmt_out.video) &&
+         vctx == p_owner->vctx )
+        return VLC_SUCCESS;
+
+    es_format_Clean( &p_owner->fmt_out );
+    es_format_Copy( &p_owner->fmt_out, &p_dec->fmt_out );
+
+    sout_stream_sys_t *sys = p_owner->p_stream->p_sys;
+    vlc_mutex_lock( &sys->var_lock );
+    if ( p_owner->vctx != NULL )
     {
-        // update the filter after the format changed/is known
-        char *psz_chain = var_GetNonEmptyString( p_owner->p_stream, CFG_PREFIX "vfilter" );
-        msg_Dbg( p_owner->p_stream, "update filter: '%s'",
-                 psz_chain ?  psz_chain : "" );
-        if( psz_chain )
-        {
-            es_format_t fmt;
-            es_format_InitFromVideo( &fmt, &p_dec->fmt_out.video );
-            if( p_sys->i_chroma )
-            {
-                fmt.video.i_chroma = p_sys->i_chroma;
-                vctx = NULL; // CPU chroma, no video context
-            }
-            filter_chain_Reset( p_sys->p_vf2, &fmt, vctx, &fmt );
-            es_format_Clean( &fmt );
-            filter_chain_AppendFromString( p_sys->p_vf2, psz_chain );
-            free( psz_chain );
-        }
+        vlc_video_context_Release( p_owner->vctx );
+        p_owner->vctx = NULL;
     }
-    return 0;
+    if ( vctx != NULL )
+        p_owner->vctx = vlc_video_context_Hold( vctx );
+    const int status = ResetFilterChain( p_owner );
+    vlc_mutex_unlock( &sys->var_lock );
+    return status;
 }
 
 static picture_t *video_new_buffer_filter( filter_t *p_filter )
@@ -677,11 +468,12 @@ static int HeightCallback( vlc_object_t *p_this, char const *psz_var,
     sout_stream_t *p_stream = (sout_stream_t *)p_data;
     sout_stream_sys_t *p_sys = p_stream->p_sys;
 
-    /* We create the handler before updating the value in p_sys
-     * so we don't have to worry about locking */
-    if( !p_sys->p_image && newval.i_int )
-        p_sys->p_image = image_HandlerCreate( p_stream );
+    vlc_mutex_lock( &p_sys->var_lock );
+    struct decoder_owner *owner = p_sys->decoder_ref;
+    if ( p_sys->i_height != newval.i_int && owner != NULL )
+        owner->need_filter_reset = true;
     p_sys->i_height = newval.i_int;
+    vlc_mutex_unlock( &p_sys->var_lock );
 
     return VLC_SUCCESS;
 }
@@ -694,11 +486,12 @@ static int WidthCallback( vlc_object_t *p_this, char const *psz_var,
     sout_stream_t *p_stream = (sout_stream_t *)p_data;
     sout_stream_sys_t *p_sys = p_stream->p_sys;
 
-    /* We create the handler before updating the value in p_sys
-     * so we don't have to worry about locking */
-    if( !p_sys->p_image && newval.i_int )
-        p_sys->p_image = image_HandlerCreate( p_stream );
+    vlc_mutex_lock( &p_sys->var_lock );
+    struct decoder_owner *owner = p_sys->decoder_ref;
+    if ( p_sys->i_width != newval.i_int && owner != NULL )
+        owner->need_filter_reset = true;
     p_sys->i_width = newval.i_int;
+    vlc_mutex_unlock( &p_sys->var_lock );
 
     return VLC_SUCCESS;
 }
@@ -711,8 +504,10 @@ static int alphaCallback( vlc_object_t *p_this, char const *psz_var,
     sout_stream_t *p_stream = (sout_stream_t *)p_data;
     sout_stream_sys_t *p_sys = p_stream->p_sys;
 
-    if( p_sys->p_es )
-        p_sys->p_es->i_alpha = newval.i_int;
+    vlc_global_lock( VLC_MOSAIC_MUTEX );
+    if( p_sys->decoder_ref && p_sys->decoder_ref->p_es )
+        p_sys->decoder_ref->p_es->i_alpha = newval.i_int;
+    vlc_global_unlock( VLC_MOSAIC_MUTEX );
 
     return VLC_SUCCESS;
 }
@@ -725,8 +520,10 @@ static int xCallback( vlc_object_t *p_this, char const *psz_var,
     sout_stream_t *p_stream = (sout_stream_t *)p_data;
     sout_stream_sys_t *p_sys = p_stream->p_sys;
 
-    if( p_sys->p_es )
-        p_sys->p_es->i_x = newval.i_int;
+    vlc_global_lock( VLC_MOSAIC_MUTEX );
+    if( p_sys->decoder_ref && p_sys->decoder_ref->p_es )
+        p_sys->decoder_ref->p_es->i_x = newval.i_int;
+    vlc_global_unlock( VLC_MOSAIC_MUTEX );
 
     return VLC_SUCCESS;
 }
@@ -739,8 +536,199 @@ static int yCallback( vlc_object_t *p_this, char const *psz_var,
     sout_stream_t *p_stream = (sout_stream_t *)p_data;
     sout_stream_sys_t *p_sys = p_stream->p_sys;
 
-    if( p_sys->p_es )
-        p_sys->p_es->i_y = newval.i_int;
+    vlc_global_lock( VLC_MOSAIC_MUTEX );
+    if( p_sys->decoder_ref && p_sys->decoder_ref->p_es )
+        p_sys->decoder_ref->p_es->i_y = newval.i_int;
+    vlc_global_unlock( VLC_MOSAIC_MUTEX );
 
     return VLC_SUCCESS;
 }
+
+/*****************************************************************************
+ * Close
+ *****************************************************************************/
+static void Close( sout_stream_t *p_stream )
+{
+    sout_stream_sys_t *p_sys = p_stream->p_sys;
+
+    /* Delete the callbacks */
+    var_DelCallback( p_stream, CFG_PREFIX "height", HeightCallback, p_stream );
+    var_DelCallback( p_stream, CFG_PREFIX "width", WidthCallback, p_stream );
+    var_DelCallback( p_stream, CFG_PREFIX "alpha", alphaCallback, p_stream );
+    var_DelCallback( p_stream, CFG_PREFIX "x", xCallback, p_stream );
+    var_DelCallback( p_stream, CFG_PREFIX "y", yCallback, p_stream );
+
+    free( p_sys->psz_id );
+    free( p_sys->filters_config );
+
+    free( p_sys );
+}
+
+static const struct sout_stream_operations ops = {
+    .add = Add,
+    .del = Del,
+    .send = Send,
+    .control = Control,
+    .flush = Flush,
+    .close = Close,
+};
+
+static const char *const ppsz_sout_options[] = {
+    "id", "width", "height", "sar", "vfilter", "chroma", "alpha", "x", "y", NULL
+};
+
+/*****************************************************************************
+ * Open
+ *****************************************************************************/
+static int Open( vlc_object_t *p_this )
+{
+    sout_stream_t        *p_stream = (sout_stream_t *)p_this;
+    sout_stream_sys_t    *p_sys;
+    vlc_value_t           val;
+
+    config_ChainParse( p_stream, CFG_PREFIX, ppsz_sout_options,
+                       p_stream->p_cfg );
+
+    p_sys = malloc( sizeof( sout_stream_sys_t ) );
+    if( !p_sys )
+        return VLC_ENOMEM;
+
+    p_stream->p_sys = p_sys;
+
+    p_sys->psz_id = var_CreateGetString( p_stream, CFG_PREFIX "id" );
+
+    p_sys->decoder_ref = NULL;
+
+    p_sys->i_height =
+        var_CreateGetIntegerCommand( p_stream, CFG_PREFIX "height" );
+    var_AddCallback( p_stream, CFG_PREFIX "height", HeightCallback, p_stream );
+
+    p_sys->i_width =
+        var_CreateGetIntegerCommand( p_stream, CFG_PREFIX "width" );
+    var_AddCallback( p_stream, CFG_PREFIX "width", WidthCallback, p_stream );
+
+    var_Get( p_stream, CFG_PREFIX "sar", &val );
+    if( val.psz_string )
+    {
+        char *psz_parser = strchr( val.psz_string, ':' );
+
+        if( psz_parser )
+        {
+            *psz_parser++ = '\0';
+            p_sys->i_sar_num = atoi( val.psz_string );
+            p_sys->i_sar_den = atoi( psz_parser );
+            vlc_ureduce( &p_sys->i_sar_num, &p_sys->i_sar_den,
+                         p_sys->i_sar_num, p_sys->i_sar_den, 0 );
+        }
+        else
+        {
+            msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string );
+            p_sys->i_sar_num = p_sys->i_sar_den = 1;
+        }
+
+        free( val.psz_string );
+    }
+    else
+    {
+        p_sys->i_sar_num = p_sys->i_sar_den = 1;
+    }
+
+    p_sys->i_chroma = VLC_CODEC_I420;
+    val.psz_string = var_GetNonEmptyString( p_stream, CFG_PREFIX "chroma" );
+    if( val.psz_string && strlen( val.psz_string ) >= 4 )
+    {
+        memcpy( &p_sys->i_chroma, val.psz_string, 4 );
+        msg_Dbg( p_stream, "Forcing image chroma to 0x%.8x (%4.4s)", p_sys->i_chroma, (char*)&p_sys->i_chroma );
+        if ( vlc_fourcc_GetChromaDescription( p_sys->i_chroma ) == NULL )
+        {
+            msg_Err( p_stream, "Unknown chroma" );
+            free( p_sys );
+            return VLC_EINVAL;
+        }
+    }
+    free( val.psz_string );
+
+    p_sys->filters_config = var_GetNonEmptyString( p_stream, CFG_PREFIX "vfilter" );
+
+    vlc_mutex_init( &p_sys->var_lock );
+
+#define INT_COMMAND( a ) do { \
+    var_Create( p_stream, CFG_PREFIX #a, \
+                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND ); \
+    var_AddCallback( p_stream, CFG_PREFIX #a, a ## Callback, \
+                     p_stream ); } while(0)
+    INT_COMMAND( alpha );
+    INT_COMMAND( x );
+    INT_COMMAND( y );
+
+#undef INT_COMMAND
+
+    p_stream->ops = &ops;
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+#define ID_TEXT N_("ID")
+#define ID_LONGTEXT N_( \
+    "Specify an identifier string for this subpicture" )
+
+#define WIDTH_TEXT N_("Video width")
+#define WIDTH_LONGTEXT N_( \
+    "Output video width." )
+#define HEIGHT_TEXT N_("Video height")
+#define HEIGHT_LONGTEXT N_( \
+    "Output video height." )
+#define RATIO_TEXT N_("Sample aspect ratio")
+#define RATIO_LONGTEXT N_( \
+    "Sample aspect ratio of the destination (1:1, 3:4, 2:3)." )
+
+#define VFILTER_TEXT N_("Video filter")
+#define VFILTER_LONGTEXT N_( \
+    "Video filters will be applied to the video stream." )
+
+#define CHROMA_TEXT N_("Image chroma")
+#define CHROMA_LONGTEXT N_( \
+    "Force the use of a specific chroma. Use YUVA if you're planning " \
+    "to use the Alphamask or Bluescreen video filter." )
+
+#define ALPHA_TEXT N_("Transparency")
+#define ALPHA_LONGTEXT N_( \
+    "Transparency of the mosaic picture." )
+
+#define X_TEXT N_("X offset")
+#define X_LONGTEXT N_( \
+    "X coordinate of the upper left corner in the mosaic if non negative." )
+
+#define Y_TEXT N_("Y offset")
+#define Y_LONGTEXT N_( \
+    "Y coordinate of the upper left corner in the mosaic if non negative." )
+
+vlc_module_begin ()
+    set_shortname( N_( "Mosaic bridge" ) )
+    set_description(N_("Mosaic bridge stream output") )
+    set_capability( "sout output", 0 )
+    add_shortcut( "mosaic-bridge" )
+
+    set_subcategory( SUBCAT_SOUT_STREAM )
+
+    add_string( CFG_PREFIX "id", "Id", ID_TEXT, ID_LONGTEXT )
+    add_integer( CFG_PREFIX "width", 0, WIDTH_TEXT,
+                 WIDTH_LONGTEXT )
+    add_integer( CFG_PREFIX "height", 0, HEIGHT_TEXT,
+                 HEIGHT_LONGTEXT )
+    add_string( CFG_PREFIX "sar", "1:1", RATIO_TEXT,
+                RATIO_LONGTEXT )
+    add_string( CFG_PREFIX "chroma", NULL, CHROMA_TEXT, CHROMA_LONGTEXT )
+
+    add_module_list(CFG_PREFIX "vfilter", "video filter", NULL,
+                    VFILTER_TEXT, VFILTER_LONGTEXT)
+
+    add_integer_with_range( CFG_PREFIX "alpha", 255, 0, 255,
+                            ALPHA_TEXT, ALPHA_LONGTEXT )
+    add_integer( CFG_PREFIX "x", -1, X_TEXT, X_LONGTEXT )
+    add_integer( CFG_PREFIX "y", -1, Y_TEXT, Y_LONGTEXT )
+
+    set_callback( Open )
+vlc_module_end ()



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/502576657253b9901bd78ea06c886346cae56dbb...9ca953e1d4dc05113457845315fe02a90fdcc007

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/502576657253b9901bd78ea06c886346cae56dbb...9ca953e1d4dc05113457845315fe02a90fdcc007
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list