2019-11-04 14:40:04 +00:00
/* switchbin
* Copyright ( C ) 2016 Carlos Rafael Giani
*
* gstswitchbin . c : Element for switching between paths based on input caps
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library 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
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the
* Free Software Foundation , Inc . , 51 Franklin St , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
2019-11-04 14:40:10 +00:00
/**
* SECTION : element - switchbin
* @ title : switchbin
*
2023-05-09 13:44:02 +00:00
* switchbin is a helper element that chooses between a set of processing
* chains ( called " paths " ) based on incoming caps , the caps of the paths ,
* and the result of caps queries issued to the elements within the paths .
* It switches between these paths based on thes caps . Paths are child objects ,
* which are accessed by the # GstChildProxy interface .
*
* The intent is to allow for easy construction of dynamic pipelines that
* automatically switches between paths based on the caps , which is useful
* for cases when certain elements are only to be used for certain types
* of dataflow . One common example is a switchbin that inserts postprocessing
* elements only if the incoming caps are of a type that allows for such
* postprocessing , like when a video dataflow could be raw frames in some
* cases and encoded MPEG video in others - postprocessing plugins for
* color space conversion , scaling and such then should only be inserted
* if the data consists of raw frames , while encoded video is passed
* through unchanged .
*
* Each path has an " element " property . If a # GstElement is passed to this ,
* switchbin takes ownership over that element . ( Any previously set element
* is removed and unref ' d before the new one is set . ) The element property
* can also be NULL for a special passthrough mode ( see below ) . In addition ,
* each path has a " caps " property , which is used for finding matching
* paths . These caps are referred to as the " path caps " .
*
* NOTE : Currently , switchbin has a limitation that path elements must
* have exactly one " sink " and one " src " pad , both of which need to be
* always available , so no request and no sometimes pads .
2019-11-04 14:40:10 +00:00
*
* Whenever new input caps are encountered at the switchbin ' s sinkpad ,
2023-05-09 13:44:02 +00:00
* the first path with matching caps is picked . A " match " means that the
* result of gst_caps_can_intersect ( ) is TRUE . The paths are looked at
2019-11-04 14:40:10 +00:00
* in order : path \ # 0 ' s caps are looked at first , checked against the new
2023-05-09 13:44:02 +00:00
* input caps with gst_caps_can_intersect ( ) , and if the return value
2019-11-04 14:40:10 +00:00
* is TRUE , path \ # 0 is picked . Otherwise , path \ # 1 ' s caps are looked at etc .
2023-05-09 13:44:02 +00:00
* If no path matches , a GST_STREAM_ERROR_WRONG_TYPE error is reported .
*
* For queries , the concept of " allowed caps " is important . These are the
* caps that are possible to use with this switchbin . They are computed
* differently for sink - and for srcpads .
*
* Allowed sinkpad caps are computed by visiting each path , issuing an
* internal caps query to the path element ' s sink pad , intersecting the
* result from that query with the path caps , and appending that intersection
* to the overall allowed sinkpad caps . Allowed srcpad caps are similar ,
* except that the result of the internal query is directly attached to the
* overall allowed srcpad caps ( no intersection with path caps takes place ) :
* The intuition behind this is that in sinkpad direction , only caps that
* are compatible with both the path caps and whatever the internal element
* can handle are really usable - other caps will be rejected . In srcpad
* direction , path caps do not exert an influence .
*
* The switchbin responds to caps and accept - caps queries in a particular
* way . They involve the aforementioned " allowed caps " .
*
* Caps queries are responded to by first checking if there are any paths .
* If num - paths is 0 , the query always fails . If there is no current path
* selected , or if the path has no element , the allowed sink / srcpad caps
* ( depending on whether the query comes from the sink - or srcpad direction )
* is directly used as the response . If a current path is selected , and it
* has an element , the query is forwarded to that element instead .
*
* Accept - caps queries are handled by checking if the switchbin can find
* a path whose caps match the caps from that query . If there is one , the
* response to that query is TRUE , otherwise FALSE .
*
* As mentioned before , path caps can in theory be any kind of caps . However ,
* they always only affect the input side ( = the sink pad side of the switchbin ) .
* Path elements can produce output of any type , so their srcpad caps can be
* anything , even caps that are entirely different . For example , it is perfectly
* valid if the path caps are " video/x-raw " , the path element sink pad template
* caps also are " video/x-raw " , and the src pad caps of the elements are
* " application/x-rtp " .
*
* Path elements can be set to NULL . Such paths perform dataflow passthrough .
* The path then just forwards data . This includes caps and accept - caps queries .
* Since there is no element , the internal caps queries go to the switchbin
* peers instead ( to the upstream peer when the query is at the switchbin ' s
* srcpad , and to the downstream peer if the query is at the sinkpad ) .
2019-11-04 14:40:10 +00:00
*
* < refsect2 >
* < title > Example launch line < / title >
*
* In this example , if the data is raw PCM audio with 44.1 kHz , a volume
* element is used for reducing the audio volume to 10 % . Otherwise , it is
2023-05-09 13:44:02 +00:00
* just passed through . So , 44.1 kHz PCM audio will sound quiet , while
* 48 kHz PCM and any non - PCM data will be passed through unmodified .
2019-11-04 14:40:10 +00:00
*
* | [
* gst - launch - 1.0 uridecodebin uri = < URI > ! switchbin num - paths = 2 \
* path0 : : element = " audioconvert ! volume volume=0.1 " path0 : : caps = " audio/x-raw, rate=44100 " \
2023-05-09 13:44:02 +00:00
* path1 : : caps = " ANY " ! \
2019-11-04 14:40:10 +00:00
* autoaudiosink
* ] |
*
2023-05-09 13:44:02 +00:00
* This example ' s path \ # 1 is a passthrough path . Its caps are " ANY " caps ,
* and its element is NULL ( the default value ) . Dataflow is passed through ,
* and caps and accept - caps queries are forwarded to the switchbin peers .
*
* NOTE : Setting the caps to NULL instead of ANY would have accomplíshed
* the same in this example , since NULL path caps are internally
* interpreted as ANY caps .
*
2019-11-04 14:40:10 +00:00
* < / refsect2 >
*
*/
2019-11-04 14:40:04 +00:00
# include <string.h>
# include <stdio.h>
# include "gstswitchbin.h"
GST_DEBUG_CATEGORY ( switch_bin_debug ) ;
# define GST_CAT_DEFAULT switch_bin_debug
enum
{
PROP_0 ,
2018-07-02 14:25:51 +00:00
PROP_NUM_PATHS ,
PROP_CURRENT_PATH ,
PROP_LAST
2019-11-04 14:40:04 +00:00
} ;
# define DEFAULT_NUM_PATHS 0
2018-07-02 14:25:51 +00:00
GParamSpec * switchbin_props [ PROP_LAST ] ;
# define PATH_LOCK(obj) g_mutex_lock(&(GST_SWITCH_BIN_CAST (obj)->path_mutex))
# define PATH_UNLOCK(obj) g_mutex_unlock(&(GST_SWITCH_BIN_CAST (obj)->path_mutex))
# define PATH_UNLOCK_AND_CHECK(obj) gst_switch_bin_unlock_paths_and_notify(GST_SWITCH_BIN_CAST (obj));
2019-11-04 14:40:04 +00:00
static GstStaticPadTemplate static_sink_template =
GST_STATIC_PAD_TEMPLATE ( " sink " ,
GST_PAD_SINK ,
GST_PAD_ALWAYS ,
GST_STATIC_CAPS_ANY ) ;
static GstStaticPadTemplate static_src_template =
GST_STATIC_PAD_TEMPLATE ( " src " ,
GST_PAD_SRC ,
GST_PAD_ALWAYS ,
GST_STATIC_CAPS_ANY ) ;
static void gst_switch_bin_child_proxy_iface_init ( gpointer iface ,
gpointer iface_data ) ;
static GObject * gst_switch_bin_child_proxy_get_child_by_index ( GstChildProxy *
child_proxy , guint index ) ;
static guint gst_switch_bin_child_proxy_get_children_count ( GstChildProxy *
child_proxy ) ;
G_DEFINE_TYPE_WITH_CODE ( GstSwitchBin ,
gst_switch_bin ,
GST_TYPE_BIN ,
G_IMPLEMENT_INTERFACE ( GST_TYPE_CHILD_PROXY ,
gst_switch_bin_child_proxy_iface_init )
) ;
2021-02-25 14:22:15 +00:00
GST_ELEMENT_REGISTER_DEFINE ( switchbin , " switchbin " , GST_RANK_NONE ,
gst_switch_bin_get_type ( ) ) ;
2019-11-04 14:40:04 +00:00
2018-07-02 14:25:51 +00:00
static void gst_switch_bin_unlock_paths_and_notify ( GstSwitchBin * switchbin ) ;
2019-11-04 14:40:04 +00:00
2019-11-12 11:06:45 +00:00
static void gst_switch_bin_dispose ( GObject * object ) ;
2019-11-04 14:40:04 +00:00
static void gst_switch_bin_finalize ( GObject * object ) ;
static void gst_switch_bin_set_property ( GObject * object , guint prop_id ,
GValue const * value , GParamSpec * pspec ) ;
static void gst_switch_bin_get_property ( GObject * object , guint prop_id ,
GValue * value , GParamSpec * pspec ) ;
static gboolean gst_switch_bin_sink_event ( GstPad * pad ,
GstObject * parent , GstEvent * event ) ;
static gboolean gst_switch_bin_sink_query ( GstPad * pad ,
GstObject * parent , GstQuery * query ) ;
static gboolean gst_switch_bin_src_query ( GstPad * pad , GstObject * parent ,
GstQuery * query ) ;
static gboolean gst_switch_bin_set_num_paths ( GstSwitchBin * switch_bin ,
guint new_num_paths ) ;
static gboolean gst_switch_bin_select_path_for_caps ( GstSwitchBin *
switch_bin , GstCaps * caps ) ;
static gboolean gst_switch_bin_switch_to_path ( GstSwitchBin * switch_bin ,
GstSwitchBinPath * switch_bin_path ) ;
static GstSwitchBinPath * gst_switch_bin_find_matching_path ( GstSwitchBin *
switch_bin , GstCaps const * caps ) ;
static void gst_switch_bin_set_sinkpad_block ( GstSwitchBin * switch_bin ,
gboolean do_block ) ;
static GstPadProbeReturn gst_switch_bin_blocking_pad_probe ( GstPad * pad ,
GstPadProbeInfo * info , gpointer user_data ) ;
static GstCaps * gst_switch_bin_get_allowed_caps ( GstSwitchBin * switch_bin ,
2021-02-16 04:00:07 +00:00
GstPad * switch_bin_pad , gchar const * pad_name , GstCaps * filter ) ;
2019-11-04 14:40:04 +00:00
static gboolean gst_switch_bin_are_caps_acceptable ( GstSwitchBin *
switch_bin , GstCaps const * caps ) ;
2018-07-02 14:25:51 +00:00
static void
gst_switch_bin_unlock_paths_and_notify ( GstSwitchBin * switchbin )
{
gboolean do_notify = switchbin - > path_changed ;
switchbin - > path_changed = FALSE ;
PATH_UNLOCK ( switchbin ) ;
2019-11-04 14:40:04 +00:00
2018-07-02 14:25:51 +00:00
if ( do_notify )
g_object_notify_by_pspec ( G_OBJECT ( switchbin ) ,
switchbin_props [ PROP_CURRENT_PATH ] ) ;
}
2019-11-04 14:40:04 +00:00
static void
gst_switch_bin_child_proxy_iface_init ( gpointer iface ,
G_GNUC_UNUSED gpointer iface_data )
{
GstChildProxyInterface * child_proxy_iface = iface ;
child_proxy_iface - > get_child_by_index =
GST_DEBUG_FUNCPTR ( gst_switch_bin_child_proxy_get_child_by_index ) ;
child_proxy_iface - > get_children_count =
GST_DEBUG_FUNCPTR ( gst_switch_bin_child_proxy_get_children_count ) ;
}
static GObject *
gst_switch_bin_child_proxy_get_child_by_index ( GstChildProxy * child_proxy ,
guint index )
{
GObject * result ;
GstSwitchBin * switch_bin = GST_SWITCH_BIN ( child_proxy ) ;
PATH_LOCK ( switch_bin ) ;
if ( G_UNLIKELY ( index > = switch_bin - > num_paths ) )
result = NULL ;
else
result = g_object_ref ( G_OBJECT ( switch_bin - > paths [ index ] ) ) ;
PATH_UNLOCK ( switch_bin ) ;
return result ;
}
static guint
gst_switch_bin_child_proxy_get_children_count ( GstChildProxy * child_proxy )
{
guint result ;
GstSwitchBin * switch_bin = GST_SWITCH_BIN ( child_proxy ) ;
PATH_LOCK ( switch_bin ) ;
result = switch_bin - > num_paths ;
PATH_UNLOCK ( switch_bin ) ;
return result ;
}
static void
gst_switch_bin_class_init ( GstSwitchBinClass * klass )
{
GObjectClass * object_class ;
GstElementClass * element_class ;
GST_DEBUG_CATEGORY_INIT ( switch_bin_debug , " switchbin " , 0 , " switch bin " ) ;
object_class = G_OBJECT_CLASS ( klass ) ;
element_class = GST_ELEMENT_CLASS ( klass ) ;
gst_element_class_add_pad_template ( element_class ,
gst_static_pad_template_get ( & static_sink_template ) ) ;
gst_element_class_add_pad_template ( element_class ,
gst_static_pad_template_get ( & static_src_template ) ) ;
2019-11-12 11:06:45 +00:00
object_class - > dispose = GST_DEBUG_FUNCPTR ( gst_switch_bin_dispose ) ;
2019-11-04 14:40:04 +00:00
object_class - > finalize = GST_DEBUG_FUNCPTR ( gst_switch_bin_finalize ) ;
object_class - > set_property = GST_DEBUG_FUNCPTR ( gst_switch_bin_set_property ) ;
object_class - > get_property = GST_DEBUG_FUNCPTR ( gst_switch_bin_get_property ) ;
2019-11-04 14:40:10 +00:00
/**
* GstSwitchBin : num - paths
*
* Configure how many paths the switchbin will be choosing between . Attempting
* to configure a path outside the range 0. . ( n - 1 ) will fail . Reducing the
* number of paths will release any paths outside the new range , which might
* trigger activation of a new path by re - assessing the current caps .
*/
2018-07-02 14:25:51 +00:00
switchbin_props [ PROP_NUM_PATHS ] = g_param_spec_uint ( " num-paths " ,
" Number of paths " , " Number of paths " , 0 , G_MAXUINT - 1 ,
DEFAULT_NUM_PATHS , G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ) ;
2019-11-04 14:40:04 +00:00
g_object_class_install_property ( object_class ,
2018-07-02 14:25:51 +00:00
PROP_NUM_PATHS , switchbin_props [ PROP_NUM_PATHS ] ) ;
/**
* GstSwitchBin : current - path
*
* Returns the currently selected path number . If there is no current
* path ( due to no caps , or unsupported caps ) , the value is # G_MAXUINT . Read - only .
*/
switchbin_props [ PROP_CURRENT_PATH ] =
g_param_spec_uint ( " current-path " , " Current Path " ,
" Currently selected path " , 0 , G_MAXUINT ,
0 , G_PARAM_READABLE | G_PARAM_STATIC_STRINGS ) ;
g_object_class_install_property ( object_class ,
PROP_CURRENT_PATH , switchbin_props [ PROP_CURRENT_PATH ] ) ;
2019-11-04 14:40:04 +00:00
gst_element_class_set_static_metadata ( element_class ,
" switchbin " ,
" Generic/Bin " ,
" Switch between sub-pipelines (paths) based on input caps " ,
" Carlos Rafael Giani <dv@pseudoterminal.org> " ) ;
}
static void
gst_switch_bin_init ( GstSwitchBin * switch_bin )
{
GstPad * pad ;
switch_bin - > num_paths = DEFAULT_NUM_PATHS ;
switch_bin - > paths = NULL ;
switch_bin - > current_path = NULL ;
switch_bin - > blocking_probe_id = 0 ;
switch_bin - > last_caps = NULL ;
switch_bin - > sinkpad = gst_ghost_pad_new_no_target_from_template ( " sink " ,
gst_element_class_get_pad_template ( GST_ELEMENT_GET_CLASS ( switch_bin ) ,
" sink " )
) ;
gst_element_add_pad ( GST_ELEMENT ( switch_bin ) , switch_bin - > sinkpad ) ;
switch_bin - > srcpad = gst_ghost_pad_new_no_target_from_template ( " src " ,
gst_element_class_get_pad_template ( GST_ELEMENT_GET_CLASS ( switch_bin ) ,
" src " )
) ;
gst_element_add_pad ( GST_ELEMENT ( switch_bin ) , switch_bin - > srcpad ) ;
gst_pad_set_event_function ( switch_bin - > sinkpad , gst_switch_bin_sink_event ) ;
gst_pad_set_query_function ( switch_bin - > sinkpad , gst_switch_bin_sink_query ) ;
gst_pad_set_query_function ( switch_bin - > srcpad , gst_switch_bin_src_query ) ;
switch_bin - > input_identity =
gst_element_factory_make ( " identity " , " input-identity " ) ;
gst_bin_add ( GST_BIN ( switch_bin ) , switch_bin - > input_identity ) ;
pad = gst_element_get_static_pad ( switch_bin - > input_identity , " sink " ) ;
gst_ghost_pad_set_target ( GST_GHOST_PAD ( switch_bin - > sinkpad ) , pad ) ;
gst_object_unref ( GST_OBJECT ( pad ) ) ;
}
2019-11-12 11:06:45 +00:00
static void
gst_switch_bin_dispose ( GObject * object )
{
GstSwitchBin * switch_bin = GST_SWITCH_BIN ( object ) ;
guint i ;
/* Chaining up will release all children of the bin,
* invalidating any pointer to elements in the paths , so make sure
* and clear those first */
PATH_LOCK ( switch_bin ) ;
for ( i = 0 ; i < switch_bin - > num_paths ; + + i ) {
if ( switch_bin - > paths [ i ] )
switch_bin - > paths [ i ] - > element = NULL ;
}
PATH_UNLOCK ( switch_bin ) ;
G_OBJECT_CLASS ( gst_switch_bin_parent_class ) - > dispose ( object ) ;
}
2019-11-04 14:40:04 +00:00
static void
gst_switch_bin_finalize ( GObject * object )
{
GstSwitchBin * switch_bin = GST_SWITCH_BIN ( object ) ;
2019-11-12 11:06:45 +00:00
guint i ;
2019-11-04 14:40:04 +00:00
if ( switch_bin - > last_caps ! = NULL )
gst_caps_unref ( switch_bin - > last_caps ) ;
2019-11-12 11:06:45 +00:00
for ( i = 0 ; i < switch_bin - > num_paths ; + + i ) {
gst_object_unparent ( GST_OBJECT ( switch_bin - > paths [ i ] ) ) ;
}
2019-11-04 14:40:04 +00:00
g_free ( switch_bin - > paths ) ;
G_OBJECT_CLASS ( gst_switch_bin_parent_class ) - > finalize ( object ) ;
}
static void
gst_switch_bin_set_property ( GObject * object , guint prop_id ,
GValue const * value , GParamSpec * pspec )
{
GstSwitchBin * switch_bin = GST_SWITCH_BIN ( object ) ;
switch ( prop_id ) {
case PROP_NUM_PATHS :
PATH_LOCK ( switch_bin ) ;
gst_switch_bin_set_num_paths ( switch_bin , g_value_get_uint ( value ) ) ;
2018-07-02 14:25:51 +00:00
PATH_UNLOCK_AND_CHECK ( switch_bin ) ;
2019-11-04 14:40:04 +00:00
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
break ;
}
}
static void
gst_switch_bin_get_property ( GObject * object , guint prop_id , GValue * value ,
GParamSpec * pspec )
{
GstSwitchBin * switch_bin = GST_SWITCH_BIN ( object ) ;
switch ( prop_id ) {
case PROP_NUM_PATHS :
PATH_LOCK ( switch_bin ) ;
g_value_set_uint ( value , switch_bin - > num_paths ) ;
2018-07-02 14:25:51 +00:00
PATH_UNLOCK_AND_CHECK ( switch_bin ) ;
break ;
case PROP_CURRENT_PATH :
PATH_LOCK ( switch_bin ) ;
if ( switch_bin - > current_path ) {
guint i ;
for ( i = 0 ; i < switch_bin - > num_paths ; + + i ) {
if ( switch_bin - > paths [ i ] = = switch_bin - > current_path ) {
g_value_set_uint ( value , i ) ;
break ;
}
}
} else {
/* No valid path, return MAXUINT */
g_value_set_uint ( value , G_MAXUINT ) ;
}
2019-11-04 14:40:04 +00:00
PATH_UNLOCK ( switch_bin ) ;
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
break ;
}
}
static gboolean
gst_switch_bin_sink_event ( GstPad * pad , GstObject * parent , GstEvent * event )
{
GstSwitchBin * switch_bin = GST_SWITCH_BIN ( parent ) ;
switch ( GST_EVENT_TYPE ( event ) ) {
case GST_EVENT_CAPS :
{
/* Intercept the caps event to switch to an appropriate path, then
* resume default caps event processing */
GstCaps * caps ;
gboolean ret ;
gst_event_parse_caps ( event , & caps ) ;
GST_DEBUG_OBJECT ( switch_bin ,
" sink pad got caps event with caps % " GST_PTR_FORMAT
" ; looking for matching path " , ( gpointer ) caps ) ;
PATH_LOCK ( switch_bin ) ;
ret = gst_switch_bin_select_path_for_caps ( switch_bin , caps ) ;
2018-07-02 14:25:51 +00:00
PATH_UNLOCK_AND_CHECK ( switch_bin ) ;
2019-11-04 14:40:04 +00:00
if ( ! ret ) {
gst_event_unref ( event ) ;
return FALSE ;
} else
return gst_pad_event_default ( pad , parent , event ) ;
}
default :
GST_DEBUG_OBJECT ( switch_bin , " sink event: %s " ,
gst_event_type_get_name ( GST_EVENT_TYPE ( event ) ) ) ;
return gst_pad_event_default ( pad , parent , event ) ;
}
}
static gboolean
gst_switch_bin_handle_query ( GstPad * pad , GstObject * parent , GstQuery * query ,
char const * pad_name )
{
GstSwitchBin * switch_bin = GST_SWITCH_BIN ( parent ) ;
switch ( GST_QUERY_TYPE ( query ) ) {
case GST_QUERY_CAPS :
{
GstCaps * filter , * caps ;
gst_query_parse_caps ( query , & filter ) ;
2023-05-10 10:38:11 +00:00
GST_DEBUG_OBJECT ( switch_bin , " new caps query; filter: % " GST_PTR_FORMAT ,
filter ) ;
2023-05-14 14:36:13 +00:00
caps =
gst_switch_bin_get_allowed_caps ( switch_bin , pad , pad_name , filter ) ;
2019-11-04 14:40:04 +00:00
if ( caps ! = NULL ) {
GST_DEBUG_OBJECT ( switch_bin , " %s caps query: caps: % " GST_PTR_FORMAT ,
pad_name , ( gpointer ) caps ) ;
gst_query_set_caps_result ( query , caps ) ;
gst_caps_unref ( caps ) ;
return TRUE ;
} else
return FALSE ;
}
case GST_QUERY_ACCEPT_CAPS :
{
GstCaps * caps ;
gboolean acceptable ;
gst_query_parse_accept_caps ( query , & caps ) ;
PATH_LOCK ( switch_bin ) ;
acceptable = gst_switch_bin_are_caps_acceptable ( switch_bin , caps ) ;
PATH_UNLOCK ( switch_bin ) ;
GST_DEBUG_OBJECT ( switch_bin ,
" %s accept_caps query: acceptable: %d caps: % " GST_PTR_FORMAT ,
pad_name , ( gint ) acceptable , ( gpointer ) caps ) ;
gst_query_set_accept_caps_result ( query , acceptable ) ;
return TRUE ;
}
default :
return gst_pad_query_default ( pad , parent , query ) ;
}
}
static gboolean
gst_switch_bin_sink_query ( GstPad * pad , GstObject * parent , GstQuery * query )
{
return gst_switch_bin_handle_query ( pad , parent , query , " sink " ) ;
}
static gboolean
gst_switch_bin_src_query ( GstPad * pad , GstObject * parent , GstQuery * query )
{
return gst_switch_bin_handle_query ( pad , parent , query , " src " ) ;
}
static gboolean
gst_switch_bin_set_num_paths ( GstSwitchBin * switch_bin , guint new_num_paths )
{
guint i ;
gboolean cur_path_removed = FALSE ;
/* must be called with path lock held */
if ( switch_bin - > num_paths = = new_num_paths ) {
GST_DEBUG_OBJECT ( switch_bin ,
" no change in number of paths - ignoring call " ) ;
return TRUE ;
} else if ( switch_bin - > num_paths < new_num_paths ) {
/* New number of paths is larger -> N new paths need to be created & added,
* where N = new_num_paths - switch_bin - > num_paths . */
GST_DEBUG_OBJECT ( switch_bin , " adding %u new paths " ,
new_num_paths - switch_bin - > num_paths ) ;
switch_bin - > paths =
g_realloc ( switch_bin - > paths , sizeof ( GstObject * ) * new_num_paths ) ;
for ( i = switch_bin - > num_paths ; i < new_num_paths ; + + i ) {
GstSwitchBinPath * path ;
gchar * path_name ;
/* default names would be something like "switchbinpath0" */
path_name = g_strdup_printf ( " path%u " , i ) ;
switch_bin - > paths [ i ] = path =
g_object_new ( gst_switch_bin_path_get_type ( ) , " name " , path_name ,
NULL ) ;
path - > bin = switch_bin ;
gst_object_set_parent ( GST_OBJECT ( path ) , GST_OBJECT ( switch_bin ) ) ;
gst_child_proxy_child_added ( GST_CHILD_PROXY ( switch_bin ) ,
G_OBJECT ( path ) , path_name ) ;
GST_DEBUG_OBJECT ( switch_bin , " added path #%u \" %s \" (%p) " , i , path_name ,
( gpointer ) path ) ;
g_free ( path_name ) ;
}
} else {
/* New number of paths is smaller -> the last N paths need to be removed,
* where N = switch_bin - > num_paths - new_num_paths . If one of the paths
* that are being removed is the current path , then a new current path
* is selected . */
gchar * path_name ;
GST_DEBUG_OBJECT ( switch_bin , " removing the last %u paths " ,
switch_bin - > num_paths - new_num_paths ) ;
for ( i = new_num_paths ; i < switch_bin - > num_paths ; + + i ) {
GstSwitchBinPath * path = switch_bin - > paths [ i ] ;
path_name = g_strdup ( GST_OBJECT_NAME ( path ) ) ;
if ( path = = switch_bin - > current_path ) {
cur_path_removed = TRUE ;
gst_switch_bin_switch_to_path ( switch_bin , NULL ) ;
GST_DEBUG_OBJECT ( switch_bin ,
" path #%u \" %s \" (%p) is the current path - selecting a new current path will be necessary " ,
i , path_name , ( gpointer ) ( switch_bin - > paths [ i ] ) ) ;
}
gst_child_proxy_child_removed ( GST_CHILD_PROXY ( switch_bin ) ,
G_OBJECT ( path ) , path_name ) ;
gst_object_unparent ( GST_OBJECT ( switch_bin - > paths [ i ] ) ) ;
GST_DEBUG_OBJECT ( switch_bin , " removed path #%u \" %s \" (%p) " , i ,
path_name , ( gpointer ) ( switch_bin - > paths [ i ] ) ) ;
g_free ( path_name ) ;
}
2019-11-12 11:06:45 +00:00
switch_bin - > paths =
g_realloc ( switch_bin - > paths , sizeof ( GstObject * ) * new_num_paths ) ;
2019-11-04 14:40:04 +00:00
}
switch_bin - > num_paths = new_num_paths ;
if ( new_num_paths > 0 ) {
if ( cur_path_removed ) {
/* Select a new current path if the previous one was removed above */
if ( switch_bin - > last_caps ! = NULL ) {
GST_DEBUG_OBJECT ( switch_bin ,
" current path was removed earlier - need to select a new one based on the last caps % "
GST_PTR_FORMAT , ( gpointer ) ( switch_bin - > last_caps ) ) ;
return gst_switch_bin_select_path_for_caps ( switch_bin ,
switch_bin - > last_caps ) ;
} else {
/* This should not happen. Every time a current path is selected, the
* caps that were used for the selection are copied as the last_caps .
* So , if a current path exists , but last_caps is NULL , it indicates
* a bug . For example , if the current path was selected without calling
* gst_switch_bin_select_path_for_caps ( ) . */
g_assert_not_reached ( ) ;
return FALSE ; /* shuts up compiler warning */
}
} else
return TRUE ;
} else
return gst_switch_bin_switch_to_path ( switch_bin , NULL ) ;
}
static gboolean
gst_switch_bin_select_path_for_caps ( GstSwitchBin * switch_bin , GstCaps * caps )
{
/* must be called with path lock held */
gboolean ret ;
GstSwitchBinPath * path ;
path = gst_switch_bin_find_matching_path ( switch_bin , caps ) ;
if ( path = = NULL ) {
/* No matching path found, the caps are incompatible. Report this and exit. */
GST_ELEMENT_ERROR ( switch_bin , STREAM , WRONG_TYPE ,
( " could not find compatible path " ) , ( " sink caps: % " GST_PTR_FORMAT ,
( gpointer ) caps ) ) ;
ret = FALSE ;
} else {
/* Matching path found. Try to switch to it. */
GST_DEBUG_OBJECT ( switch_bin , " found matching path \" %s \" (%p) - switching " ,
GST_OBJECT_NAME ( path ) , ( gpointer ) path ) ;
ret = gst_switch_bin_switch_to_path ( switch_bin , path ) ;
}
if ( ret & & ( caps ! = switch_bin - > last_caps ) )
gst_caps_replace ( & ( switch_bin - > last_caps ) , caps ) ;
return ret ;
}
static gboolean
gst_switch_bin_switch_to_path ( GstSwitchBin * switch_bin ,
GstSwitchBinPath * switch_bin_path )
{
/* must be called with path lock held */
gboolean ret = TRUE ;
if ( switch_bin_path ! = NULL )
GST_DEBUG_OBJECT ( switch_bin , " switching to path \" %s \" (%p) " ,
GST_OBJECT_NAME ( switch_bin_path ) , ( gpointer ) switch_bin_path ) ;
else
GST_DEBUG_OBJECT ( switch_bin ,
" switching to NULL path (= disabling current path) " ) ;
/* No current path set and no path is to be set -> nothing to do */
if ( ( switch_bin_path = = NULL ) & & ( switch_bin - > current_path = = NULL ) )
return TRUE ;
/* If this path is already the current one, do nothing */
if ( switch_bin - > current_path = = switch_bin_path )
return TRUE ;
/* Block incoming data to be able to safely switch */
gst_switch_bin_set_sinkpad_block ( switch_bin , TRUE ) ;
/* Unlink the current path's element (if there is a current path) */
if ( switch_bin - > current_path ! = NULL ) {
GstSwitchBinPath * cur_path = switch_bin - > current_path ;
if ( cur_path - > element ! = NULL ) {
gst_element_set_state ( cur_path - > element , GST_STATE_NULL ) ;
gst_element_unlink ( switch_bin - > input_identity , cur_path - > element ) ;
}
gst_ghost_pad_set_target ( GST_GHOST_PAD ( switch_bin - > srcpad ) , NULL ) ;
switch_bin - > current_path = NULL ;
2018-07-02 14:25:51 +00:00
switch_bin - > path_changed = TRUE ;
2019-11-04 14:40:04 +00:00
}
/* Link the new path's element (if a new path is specified) */
if ( switch_bin_path ! = NULL ) {
if ( switch_bin_path - > element ! = NULL ) {
GstPad * pad ;
/* There is a path element. Link it into the pipeline. Data passes through
* it now , since its associated path just became the current one . */
/* TODO: currently, only elements with one "src" "sink" always-pad are supported;
* add support for request and sometimes pads */
pad = gst_element_get_static_pad ( switch_bin_path - > element , " src " ) ;
if ( pad = = NULL ) {
GST_ERROR_OBJECT ( switch_bin ,
" path element has no static srcpad - cannot link " ) ;
ret = FALSE ;
goto finish ;
}
if ( ! gst_ghost_pad_set_target ( GST_GHOST_PAD ( switch_bin - > srcpad ) , pad ) ) {
GST_ERROR_OBJECT ( switch_bin ,
" could not set the path element's srcpad as the ghost srcpad's target " ) ;
ret = FALSE ;
}
gst_object_unref ( GST_OBJECT ( pad ) ) ;
if ( ! ret )
goto finish ;
if ( ! gst_element_link ( switch_bin - > input_identity ,
switch_bin_path - > element ) ) {
GST_ERROR_OBJECT ( switch_bin ,
" linking the path element's sinkpad failed ; check if the path element's sink caps and the upstream elements connected to the switchbin's sinkpad match " ) ;
ret = FALSE ;
goto finish ;
}
/* Unlock the element's state in case it was locked earlier
* so its state can be synced to the switchbin ' s */
gst_element_set_locked_state ( switch_bin_path - > element , FALSE ) ;
if ( ! gst_element_sync_state_with_parent ( switch_bin_path - > element ) ) {
GST_ERROR_OBJECT ( switch_bin ,
" could not sync the path element's state with that of the switchbin " ) ;
ret = FALSE ;
goto finish ;
}
} else {
GstPad * srcpad ;
2023-05-03 07:55:27 +00:00
/* There is no path element. Just forward data. */
GST_DEBUG_OBJECT ( switch_bin , " path has no element ; will forward data " ) ;
2019-11-04 14:40:04 +00:00
srcpad = gst_element_get_static_pad ( switch_bin - > input_identity , " src " ) ;
g_assert ( srcpad ! = NULL ) ;
if ( ! gst_ghost_pad_set_target ( GST_GHOST_PAD ( switch_bin - > srcpad ) ,
srcpad ) ) {
GST_ERROR_OBJECT ( switch_bin ,
" could not set the path element's srcpad as the ghost srcpad's target " ) ;
ret = FALSE ;
}
gst_object_unref ( GST_OBJECT ( srcpad ) ) ;
}
}
switch_bin - > current_path = switch_bin_path ;
2018-07-02 14:25:51 +00:00
switch_bin - > path_changed = TRUE ;
2019-11-04 14:40:04 +00:00
/* If there is a new path to use, unblock the input */
if ( switch_bin_path ! = NULL )
gst_switch_bin_set_sinkpad_block ( switch_bin , FALSE ) ;
finish :
return ret ;
}
static GstSwitchBinPath *
gst_switch_bin_find_matching_path ( GstSwitchBin * switch_bin ,
GstCaps const * caps )
{
/* must be called with path lock held */
guint i ;
for ( i = 0 ; i < switch_bin - > num_paths ; + + i ) {
GstSwitchBinPath * path = switch_bin - > paths [ i ] ;
2023-05-03 07:55:27 +00:00
/* Path caps are never supposed to be NULL. Even if the user
* specifies NULL as caps in the path properties , the code in
* gst_switch_bin_path_set_property ( ) turns them into ANY caps . */
g_assert ( path - > caps ! = NULL ) ;
2019-11-04 14:40:04 +00:00
if ( gst_caps_can_intersect ( caps , path - > caps ) )
return path ;
}
return NULL ;
}
static GstCaps *
gst_switch_bin_get_allowed_caps ( GstSwitchBin * switch_bin ,
2021-02-16 04:00:07 +00:00
GstPad * switch_bin_pad , gchar const * pad_name , GstCaps * filter )
2019-11-04 14:40:04 +00:00
{
guint i ;
2023-05-14 14:36:13 +00:00
guint num_paths ;
GstSwitchBinPath * * paths ;
2019-11-04 14:40:04 +00:00
GstCaps * total_path_caps ;
2023-05-04 19:20:18 +00:00
gboolean peer_caps_queried = FALSE ;
gboolean peer_caps_query_successful ;
GstCaps * peer_caps = NULL ;
2021-02-24 18:04:00 +00:00
gboolean is_sink_pad =
( gst_pad_get_direction ( switch_bin_pad ) = = GST_PAD_SINK ) ;
2019-11-04 14:40:04 +00:00
2023-05-14 14:36:13 +00:00
/* Acquire references to the paths, path elements, and path caps, then
* operate on those references instead of on the actual paths . That way ,
* we do not need to keep the path lock acquired for the entirety of the
* function , which is important , since we need to issue caps queries to
* other elements here . Doing that while the path lock is acquired can
* cause deadlocks . And since we operate on references here , concurrent
* changes to the paths won ' t cause race conditions . */
PATH_LOCK ( switch_bin ) ;
2019-11-04 14:40:04 +00:00
if ( switch_bin - > num_paths = = 0 ) {
2023-05-14 14:36:13 +00:00
PATH_UNLOCK ( switch_bin ) ;
/* No paths exist, so nothing can be returned. This is not
* necessarily an error - it can happen that caps queries take
* place before the caller had a chance to set up paths for example . */
GST_DEBUG_OBJECT ( switch_bin , " no paths exist; "
" cannot return any allowed caps " ) ;
2019-11-04 14:40:04 +00:00
return NULL ;
}
2023-05-14 14:36:13 +00:00
num_paths = switch_bin - > num_paths ;
paths = g_malloc0_n ( num_paths , sizeof ( GstSwitchBinPath * ) ) ;
for ( i = 0 ; i < num_paths ; + + i )
paths [ i ] = gst_object_ref ( switch_bin - > paths [ i ] ) ;
PATH_UNLOCK ( switch_bin ) ;
/* From this moment on, the original paths are no longer accessed,
* so we can release the path lock . */
2023-05-04 19:20:18 +00:00
/* The allowed caps are a combination of the caps of all paths, the filter
* caps , and the result of issuing caps queries to the path elements
* ( or to the switchbin sink / srcpads when paths have no elements ) . */
2019-11-04 14:40:04 +00:00
total_path_caps = gst_caps_new_empty ( ) ;
2023-05-14 14:36:13 +00:00
for ( i = 0 ; i < num_paths ; + + i ) {
GstSwitchBinPath * path = paths [ i ] ;
2023-05-04 19:20:18 +00:00
GstCaps * queried_caps = NULL ;
GstCaps * intersected_caps ;
2023-05-14 14:36:13 +00:00
GstCaps * path_caps ;
GstElement * path_element ;
2023-05-04 19:20:18 +00:00
gboolean query_successful ;
GstPad * pad ;
GstQuery * caps_query ;
2023-05-14 14:36:13 +00:00
GST_OBJECT_LOCK ( path ) ;
2023-05-09 14:31:44 +00:00
/* Path caps are never supposed to be NULL. Even if the user
* specifies NULL as caps in the path properties , the code in
* gst_switch_bin_path_set_property ( ) turns them into ANY caps . */
g_assert ( path - > caps ! = NULL ) ;
2023-05-14 14:36:13 +00:00
path_caps = gst_caps_ref ( path - > caps ) ;
path_element =
( path - > element ! = NULL ) ? gst_object_ref ( path - > element ) : NULL ;
GST_OBJECT_UNLOCK ( path ) ;
2023-05-09 14:31:44 +00:00
2023-05-04 19:20:18 +00:00
/* We need to check what caps are handled by up/downstream, relative
* to the switchbin src / sinkcaps . If there is an element , issue a
* caps query to it to get that information . If there is no element ,
* then this path is a passthrough path , so the logical step is to
* query peers instead . */
2019-11-04 14:40:04 +00:00
2023-05-14 14:36:13 +00:00
if ( path_element ! = NULL ) {
pad = gst_element_get_static_pad ( path_element , pad_name ) ;
2023-05-09 14:31:44 +00:00
caps_query = gst_query_new_caps ( filter ) ;
2019-11-04 14:40:04 +00:00
2023-05-04 19:20:18 +00:00
query_successful = gst_pad_query ( pad , caps_query ) ;
if ( query_successful ) {
gst_query_parse_caps_result ( caps_query , & queried_caps ) ;
/* Ref the caps, otherwise they will be gone when the query is unref'd. */
gst_caps_ref ( queried_caps ) ;
2023-05-14 14:36:13 +00:00
GST_DEBUG_OBJECT ( switch_bin , " queried element of path #%u "
" (with filter applied if one is present), and query succeeded; "
" result: % " GST_PTR_FORMAT , i , ( gpointer ) queried_caps ) ;
} else {
GST_DEBUG_OBJECT ( switch_bin , " queried element of path #%u "
" (with filter applied if one is present), but query failed " , i ) ;
2021-02-24 18:04:00 +00:00
}
2019-11-04 14:40:04 +00:00
gst_query_unref ( caps_query ) ;
2023-05-04 19:20:18 +00:00
gst_object_unref ( GST_OBJECT ( pad ) ) ;
2023-05-14 14:36:13 +00:00
gst_object_unref ( GST_OBJECT ( path_element ) ) ;
2019-11-04 14:40:04 +00:00
} else {
2023-05-04 19:20:18 +00:00
/* Unlike in the non-NULL element case above, we issue a query
* only once . We need to query the peer , and that peer does not
* differ between paths , so querying more than once is redundant . */
if ( ! peer_caps_queried ) {
pad = is_sink_pad ? switch_bin - > srcpad : switch_bin - > sinkpad ;
2023-05-09 14:31:44 +00:00
caps_query = gst_query_new_caps ( filter ) ;
2023-05-04 19:20:18 +00:00
peer_caps_query_successful = gst_pad_peer_query ( pad , caps_query ) ;
if ( peer_caps_query_successful ) {
gst_query_parse_caps_result ( caps_query , & peer_caps ) ;
/* Ref the caps, otherwise they will be gone when the query is unref'd. */
gst_caps_ref ( peer_caps ) ;
2023-05-14 14:36:13 +00:00
GST_DEBUG_OBJECT ( switch_bin , " queried peer of %s pad (with filter "
" applied if one is present), and query succeeded; result: % "
GST_PTR_FORMAT , is_sink_pad ? " sink " : " src " , ( gpointer )
peer_caps ) ;
} else {
GST_DEBUG_OBJECT ( switch_bin , " queried peer of %s pad "
" (with filter applied if one is present), but query failed " ,
is_sink_pad ? " sink " : " src " ) ;
2023-05-04 19:20:18 +00:00
}
gst_query_unref ( caps_query ) ;
peer_caps_queried = TRUE ;
}
/* Ref the caps here again because they are unref'd further below and
* we want to keep the peer_caps around until all paths are handled . */
if ( peer_caps ! = NULL )
queried_caps = gst_caps_ref ( peer_caps ) ;
query_successful = peer_caps_query_successful ;
}
if ( query_successful ) {
/* If the caps query above succeeded, we know what up/downstream can
* handle . In the sinkpad direction , the path caps further restrict
* what caps can be used in this path , so intersect them with the
* queried caps . In the srcpad direction , no such restriction exists . */
if ( is_sink_pad )
2023-05-14 14:36:13 +00:00
intersected_caps = gst_caps_intersect ( queried_caps , path_caps ) ;
2023-05-04 19:20:18 +00:00
else
intersected_caps = gst_caps_copy ( queried_caps ) ;
gst_caps_append ( total_path_caps , intersected_caps ) ;
2023-05-14 14:36:13 +00:00
gst_caps_unref ( path_caps ) ;
2023-05-04 19:20:18 +00:00
} else {
/* If the query failed (for example, because the pad is not yet linked),
* we have to make assumptions . In the sinkpad direction , the safest
* bet is to use the path caps , since no matter what , only caps that
* are a match with them can pass through this path . In the srcpad
* direction , there are no restriction , so use ANY caps . */
2021-02-24 18:04:00 +00:00
if ( is_sink_pad )
2023-05-14 14:36:13 +00:00
gst_caps_append ( total_path_caps , path_caps ) ;
2021-02-16 04:00:07 +00:00
else
gst_caps_append ( total_path_caps , gst_caps_new_any ( ) ) ;
2019-11-04 14:40:04 +00:00
}
2023-05-04 19:20:18 +00:00
gst_caps_replace ( & queried_caps , NULL ) ;
2019-11-04 14:40:04 +00:00
}
/* Apply filter caps if present */
if ( filter ! = NULL ) {
GstCaps * tmp_caps = total_path_caps ;
2023-05-04 19:18:28 +00:00
/* Use filter caps as first caps in intersection along with the
* GST_CAPS_INTERSECT_FIRST mode . This makes it possible to
* define the order of the resulting caps by making it follow
* the order of the filter caps . */
total_path_caps = gst_caps_intersect_full ( filter , tmp_caps ,
GST_CAPS_INTERSECT_FIRST ) ;
2019-11-04 14:40:04 +00:00
gst_caps_unref ( tmp_caps ) ;
}
2023-05-04 19:20:18 +00:00
gst_caps_replace ( & peer_caps , NULL ) ;
2023-05-14 14:36:13 +00:00
/* Clear up the path references we acquired while holding
* the path lock earlier . */
for ( i = 0 ; i < num_paths ; + + i )
gst_object_unref ( paths [ i ] ) ;
g_free ( paths ) ;
2019-11-04 14:40:04 +00:00
return total_path_caps ;
}
static gboolean
gst_switch_bin_are_caps_acceptable ( GstSwitchBin * switch_bin ,
GstCaps const * caps )
{
/* must be called with path lock held */
return ( gst_switch_bin_find_matching_path ( switch_bin , caps ) ! = NULL ) ;
}
static void
gst_switch_bin_set_sinkpad_block ( GstSwitchBin * switch_bin , gboolean do_block )
{
GstPad * pad ;
if ( ( do_block & & ( switch_bin - > blocking_probe_id ! = 0 ) ) | | ( ! do_block
& & ( switch_bin - > blocking_probe_id = = 0 ) ) )
return ;
pad = gst_element_get_static_pad ( switch_bin - > input_identity , " sink " ) ;
if ( do_block ) {
switch_bin - > blocking_probe_id =
gst_pad_add_probe ( pad , GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM ,
gst_switch_bin_blocking_pad_probe , NULL , NULL ) ;
} else {
gst_pad_remove_probe ( pad , switch_bin - > blocking_probe_id ) ;
switch_bin - > blocking_probe_id = 0 ;
}
GST_DEBUG_OBJECT ( switch_bin , " sinkpad block enabled: %d " , do_block ) ;
gst_object_unref ( GST_OBJECT ( pad ) ) ;
}
static GstPadProbeReturn
gst_switch_bin_blocking_pad_probe ( G_GNUC_UNUSED GstPad * pad ,
GstPadProbeInfo * info , G_GNUC_UNUSED gpointer user_data )
{
if ( GST_PAD_PROBE_INFO_TYPE ( info ) & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM ) {
GstEvent * event = GST_PAD_PROBE_INFO_EVENT ( info ) ;
switch ( GST_EVENT_TYPE ( event ) ) {
case GST_EVENT_CAPS :
case GST_EVENT_STREAM_START :
return GST_PAD_PROBE_PASS ;
default :
break ;
}
}
return GST_PAD_PROBE_OK ;
}
/************ GstSwitchBinPath ************/
typedef struct _GstSwitchBinPathClass GstSwitchBinPathClass ;
# define GST_TYPE_SWITCH_BIN_PATH (gst_switch_bin_path_get_type())
# define GST_SWITCH_BIN_PATH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_SWITCH_BIN_PATH, GstSwitchBinPath))
# define GST_SWITCH_BIN_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_SWITCH_BIN_PATH, GstSwitchBinPathClass))
# define GST_SWITCH_BIN_PATH_CAST(obj) ((GstSwitchBinPath *)(obj))
# define GST_IS_SWITCH_BIN_PATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_SWITCH_BIN_PATH))
# define GST_IS_SWITCH_BIN_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_SWITCH_BIN_PATH))
struct _GstSwitchBinPathClass
{
GstObjectClass parent_class ;
} ;
enum
{
PROP_PATH_0 ,
PROP_ELEMENT ,
PROP_PATH_CAPS
} ;
G_DEFINE_TYPE ( GstSwitchBinPath , gst_switch_bin_path , GST_TYPE_OBJECT ) ;
static void gst_switch_bin_path_dispose ( GObject * object ) ;
static void gst_switch_bin_path_set_property ( GObject * object ,
guint prop_id , GValue const * value , GParamSpec * pspec ) ;
static void gst_switch_bin_path_get_property ( GObject * object ,
guint prop_id , GValue * value , GParamSpec * pspec ) ;
static gboolean gst_switch_bin_path_use_new_element ( GstSwitchBinPath *
switch_bin_path , GstElement * new_element ) ;
static void
gst_switch_bin_path_class_init ( GstSwitchBinPathClass * klass )
{
GObjectClass * object_class = G_OBJECT_CLASS ( klass ) ;
object_class - > dispose = GST_DEBUG_FUNCPTR ( gst_switch_bin_path_dispose ) ;
object_class - > set_property =
GST_DEBUG_FUNCPTR ( gst_switch_bin_path_set_property ) ;
object_class - > get_property =
GST_DEBUG_FUNCPTR ( gst_switch_bin_path_get_property ) ;
g_object_class_install_property ( object_class ,
PROP_ELEMENT ,
g_param_spec_object ( " element " ,
" Element " ,
2023-05-04 19:20:18 +00:00
" The path's element (if set to NULL, this path passes through dataflow) " ,
2019-11-04 14:40:04 +00:00
GST_TYPE_ELEMENT , G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS )
) ;
g_object_class_install_property ( object_class ,
PROP_PATH_CAPS ,
g_param_spec_boxed ( " caps " ,
" Caps " ,
" Caps which, if they are a subset of the input caps, select this path as the active one " ,
GST_TYPE_CAPS , G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS )
) ;
}
static void
gst_switch_bin_path_init ( GstSwitchBinPath * switch_bin_path )
{
switch_bin_path - > caps = gst_caps_new_any ( ) ;
switch_bin_path - > element = NULL ;
switch_bin_path - > bin = NULL ;
}
static void
gst_switch_bin_path_dispose ( GObject * object )
{
GstSwitchBinPath * switch_bin_path = GST_SWITCH_BIN_PATH ( object ) ;
if ( switch_bin_path - > caps ! = NULL ) {
gst_caps_unref ( switch_bin_path - > caps ) ;
switch_bin_path - > caps = NULL ;
}
if ( switch_bin_path - > element ! = NULL ) {
gst_switch_bin_path_use_new_element ( switch_bin_path , NULL ) ;
}
/* element is managed by the bin itself */
G_OBJECT_CLASS ( gst_switch_bin_path_parent_class ) - > dispose ( object ) ;
}
static void
gst_switch_bin_path_set_property ( GObject * object , guint prop_id ,
GValue const * value , GParamSpec * pspec )
{
GstSwitchBinPath * switch_bin_path = GST_SWITCH_BIN_PATH ( object ) ;
switch ( prop_id ) {
case PROP_ELEMENT :
{
/* Get the object without modifying the refcount */
GstElement * new_element = GST_ELEMENT ( g_value_get_object ( value ) ) ;
GST_OBJECT_LOCK ( switch_bin_path ) ;
PATH_LOCK ( switch_bin_path - > bin ) ;
gst_switch_bin_path_use_new_element ( switch_bin_path , new_element ) ;
2018-07-02 14:25:51 +00:00
PATH_UNLOCK_AND_CHECK ( switch_bin_path - > bin ) ;
2019-11-04 14:40:04 +00:00
GST_OBJECT_UNLOCK ( switch_bin_path ) ;
break ;
}
case PROP_PATH_CAPS :
{
GstCaps * old_caps ;
GstCaps const * new_caps = gst_value_get_caps ( value ) ;
GST_OBJECT_LOCK ( switch_bin_path ) ;
old_caps = switch_bin_path - > caps ;
if ( new_caps = = NULL ) {
/* NULL caps are interpreted as ANY */
switch_bin_path - > caps = gst_caps_new_any ( ) ;
} else
switch_bin_path - > caps = gst_caps_copy ( new_caps ) ;
GST_OBJECT_UNLOCK ( switch_bin_path ) ;
if ( old_caps ! = NULL )
gst_caps_unref ( old_caps ) ;
/* the new caps do not get applied right away
* they only start to be used with the next stream */
break ;
}
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
break ;
}
}
static void
gst_switch_bin_path_get_property ( GObject * object , guint prop_id ,
GValue * value , GParamSpec * pspec )
{
GstSwitchBinPath * switch_bin_path = GST_SWITCH_BIN_PATH ( object ) ;
switch ( prop_id ) {
case PROP_ELEMENT :
/* If a path element exists, increase its refcount first. This is
* necessary because the code that called g_object_get ( ) to fetch this
* element will also unref it when it is finished with it . */
if ( switch_bin_path - > element ! = NULL )
gst_object_ref ( GST_OBJECT ( switch_bin_path - > element ) ) ;
/* Use g_value_take_object() instead of g_value_set_object() as the latter
* increases the element ' s refcount for the duration of the GValue ' s lifetime */
g_value_take_object ( value , switch_bin_path - > element ) ;
break ;
case PROP_PATH_CAPS :
GST_OBJECT_LOCK ( switch_bin_path ) ;
gst_value_set_caps ( value , switch_bin_path - > caps ) ;
GST_OBJECT_UNLOCK ( switch_bin_path ) ;
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
break ;
}
}
static gboolean
gst_switch_bin_path_use_new_element ( GstSwitchBinPath * switch_bin_path ,
GstElement * new_element )
{
/* Must be called with lock */
GstSwitchBinPath * current_path = switch_bin_path - > bin - > current_path ;
gboolean is_current_path = ( current_path = = switch_bin_path ) ;
/* Before switching the element, make sure it is not linked,
* which is the case if this is the current path . */
if ( is_current_path )
gst_switch_bin_switch_to_path ( switch_bin_path - > bin , NULL ) ;
/* Remove any present path element prior to using the new one */
if ( switch_bin_path - > element ! = NULL ) {
gst_element_set_state ( switch_bin_path - > element , GST_STATE_NULL ) ;
/* gst_bin_remove automatically unrefs the path element */
gst_bin_remove ( GST_BIN ( switch_bin_path - > bin ) , switch_bin_path - > element ) ;
switch_bin_path - > element = NULL ;
}
2023-05-03 07:55:27 +00:00
/* If new_element is non-NULL, use it as the path's new element.
* If it is NULL , store that NULL pointer . Setting the path element
* to NULL is useful if the caller wants to manually remove the
* element from the path . ( Setting it to NULL unparents & unrefs
* the path element . ) It is also useful if the caller just wants
* to forward data unaltered in that path ( switchbin ' s input_identity
* element will then have its srcpad be directly exposed as a ghost
* pad on the bin ) . */
2019-11-04 14:40:04 +00:00
if ( new_element ! = NULL ) {
gst_bin_add ( GST_BIN ( switch_bin_path - > bin ) , new_element ) ;
switch_bin_path - > element = new_element ;
/* Lock the element's state in case. This prevents freezes, which can happen
* when an element from a not - current path tries to follow a state change ,
* but is unable to do so as long as it isn ' t linked . By locking the state ,
* it won ' t follow state changes , so the freeze does not happen . */
gst_element_set_locked_state ( new_element , TRUE ) ;
}
/* We are done. Switch back to the path if it is the current one,
* since we switched away from it earlier . */
if ( is_current_path )
return gst_switch_bin_switch_to_path ( switch_bin_path - > bin , current_path ) ;
else
return TRUE ;
}