Initialization process of gstlibav

File directory structure of GST libav

The core code of gstlibav is in the GST libav / ext / libav directory. The c files corresponding to the plugin are listed as follows:

gst-libav/ext/libav
├── gstavauddec.c
├── gstavaudenc.c
├── gstav.c
├── gstavdemux.c
├── gstavmux.c
├── gstavprotocol.c
├── gstavutils.c
├── gstavviddec.c
├── gstavvidenc.c

gstav. Plugin is defined in C_ Init function, plugin_init is the entry function of libav, which is in the plugin_init will call the register function corresponding to decoder, demux and MUX

  plugin_init
  - gst_ffmpeg_log_callback
  - gst_ffmpeg_init_pix_fmt_info
  - gst_ffmpeg_cfg_init
  - gst_ffmpegaudenc_register
  - gst_ffmpegvidenc_register
  - gst_ffmpegauddec_register
  - gst_ffmpegviddec_register
  - gst_ffmpegdemux_register
  - gst_ffmpegmux_register
  - gst_ffmpegdeinterlace_register

GST_ PLUGIN_ Expansion of define

gst/gstplugin.h

gstav. GST of C_ PLUGIN_ Define initializes the basic information of the plugin_init is passed to GST as a parameter init_ plugin_ register_ static,gst_plugin_register_static can be seen in the following decomposition analysis.

#define GST_VERSION_MAJOR (1)
#define GST_VERSION_MINOR (14)
#define GST_VERSION_MICRO (5) 

GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    libav,
    "All libav codecs and formats (" LIBAV_SOURCE ")",
    plugin_init, PACKAGE_VERSION, LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

GST_PLUGIN_DEFINE macro definition

#define GST_PLUGIN_DEFINE(major,minor,name,description,init,version,license,package,origin) \
G_BEGIN_DECLS \
GST_PLUGIN_EXPORT const GstPluginDesc * G_PASTE(gst_plugin_, G_PASTE(name, _get_desc)) (void); \
GST_PLUGIN_EXPORT void G_PASTE(gst_plugin_, G_PASTE(name, _register)) (void); \
\
static const GstPluginDesc gst_plugin_desc = { \
  major, \
  minor, \
  G_STRINGIFY(name), \
  (gchar *) description, \
  init, \
  version, \
  license, \
  PACKAGE, \
  package, \
  origin, \
  __GST_PACKAGE_RELEASE_DATETIME, \
  GST_PADDING_INIT \
};                                       \
\
const GstPluginDesc * \c++
G_PASTE(gst_plugin_, G_PASTE(name, _get_desc)) (void) \
{ \
    return &gst_plugin_desc; \
} \
\
void \
G_PASTE(gst_plugin_, G_PASTE(name, _register)) (void) \
{ \
  gst_plugin_register_static (major, minor, G_STRINGIFY(name), \
      description, init, version, license, \
      PACKAGE, package, origin); \
} \
G_END_DECLS

gst_plugin_register_static: gstreamer/gst/gstplugin.c

gst_plugin_register_func: gstreamer/gst/gstplugin.c

G_PASTE & G_PASTE_ARGS

#define G_PASTE_ARGS(identifier1,identifier2) identifier1 ## identifier2
#define G_PASTE(identifier1,identifier2)      G_PASTE_ARGS (identifier1, identifier2)

Follow GST below_ PLUGIN_ Definition of define, before GST_ PLUGIN_ libav defined by define can be expanded in turn.

The first two function declarations expand:

const GstPluginDesc * G_PASTE(gst_plugin_, G_PASTE(name, _get_desc)) (void);
\
const GstPluginDesc *G_PASTE(gst_plugin_, libav_get_desc)) (void);
\
const GstPluginDesc gst_plugin_libav_get_desc (void);
void G_PASTE(gst_plugin_, G_PASTE(name, _register)) (void);
\
void G_PASTE(gst_plugin_, libav_register) (void);

void gst_plugin_libav_register(void);

After expansion, the first two lines declare two functions gst_plugin_libav_get_desc and gst_plugin_libav_register.

gst_plugin_desc

The middle part is about GST_ plugin_ Initialization of desc structure:

static const GstPluginDesc gst_plugin_desc = { \
  major, \
  minor, \
  G_STRINGIFY(name), \
  (gchar *) description, \
  init, \
  version, \
  license, \
  PACKAGE, \
  package, \
  origin, \
  __GST_PACKAGE_RELEASE_DATETIME, \
  GST_PADDING_INIT \
};    

_ GstPluginDesc prototype

struct _GstPluginDesc {
  gint major_version;
  gint minor_version;
  const gchar *name;
  const gchar *description;
  GstPluginInitFunc plugin_init;
  const gchar *version;
  const gchar *license;
  const gchar *source;
  const gchar *package;
  const gchar *origin;
  const gchar *release_datetime;
  /*< private >*/
  gpointer _gst_reserved[GST_PADDING];
};

gst_plugin_libav_register & gst_plugin_libav_get_desc

The macro is expanded as follows:

const GstPluginDesc gst_plugin_libav_get_desc (void) {
	return &gst_plugin_desc;
}

void gst_plugin_libav_register(void) {
  	gst_plugin_register_static (major, minor, G_STRINGIFY(name), 
      description, init, version, license, PACKAGE, package, origin);
}

gst_plugin_register_static

gst_ plugin_ register_ Create a GObject type plugin in func through the previous gst_plugin_desc initializes and then calls plugin_init function

gboolean
gst_plugin_register_static (gint major_version, gint minor_version,
    const gchar * name, const gchar * description, GstPluginInitFunc init_func,
    const gchar * version, const gchar * license, const gchar * source,
    const gchar * package, const gchar * origin)
{
  GstPluginDesc desc = { major_version, minor_version, name, description,
    init_func, version, license, source, package, origin, NULL,
  };
  GstPlugin *plugin;
  gboolean res = FALSE;

  g_return_val_if_fail (name != NULL, FALSE);
  g_return_val_if_fail (description != NULL, FALSE);
  g_return_val_if_fail (init_func != NULL, FALSE);
  g_return_val_if_fail (version != NULL, FALSE);
  g_return_val_if_fail (license != NULL, FALSE);
  g_return_val_if_fail (source != NULL, FALSE);
  g_return_val_if_fail (package != NULL, FALSE);
  g_return_val_if_fail (origin != NULL, FALSE);

  /* make sure gst_init() has been called */
  g_return_val_if_fail (_gst_plugin_inited != FALSE, FALSE);

  GST_LOG ("attempting to load static plugin \"%s\" now...", name);
  // Create plugin
  plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
    
  // Call gst_plugin_register_func
  if (gst_plugin_register_func (plugin, &desc, NULL) != NULL) {
    GST_INFO ("registered static plugin \"%s\"", name);

    // gst_registry_get gets registry, which is actually a static global variable of type GstRegistry
    // gst_registry_add_plugin add this plugin to the list of registry
    res = gst_registry_add_plugin (gst_registry_get (), plugin);
    GST_INFO ("added static plugin \"%s\", result: %d", name, res);
  }
  return res;
}

gst_plugin_register_func

gst_ plugin_ register_ The func() function registers the plugin, checks the version and other information, and then copies the description information about the plugin through the function pointer desc - > plugin_ Init really completes the registration of the plugin.

static GstPlugin *
gst_plugin_register_func (GstPlugin * plugin, const GstPluginDesc * desc,
    gpointer user_data)
{
  if (!gst_plugin_check_version (desc->major_version, desc->minor_version)) {
    if (GST_CAT_DEFAULT)
      GST_WARNING ("plugin \"%s\" has incompatible version "
          "(plugin: %d.%d, gst: %d,%d), not loading",
          GST_STR_NULL (plugin->filename), desc->major_version,
          desc->minor_version, GST_VERSION_MAJOR, GST_VERSION_MINOR);
    return NULL;
  }

  if (!desc->license || !desc->description || !desc->source ||
      !desc->package || !desc->origin) {
    if (GST_CAT_DEFAULT)
      GST_WARNING ("plugin \"%s\" has missing detail in GstPluginDesc, not "
          "loading", GST_STR_NULL (plugin->filename));
    return NULL;
  }

  if (!gst_plugin_check_license (desc->license)) {
    if (GST_CAT_DEFAULT)
      GST_WARNING ("plugin \"%s\" has invalid license \"%s\", not loading",
          GST_STR_NULL (plugin->filename), desc->license);
    return NULL;
  }

  if (GST_CAT_DEFAULT)
    GST_LOG ("plugin \"%s\" looks good", GST_STR_NULL (plugin->filename));

  gst_plugin_desc_copy (&plugin->desc, desc);

  /* make resident so we're really sure it never gets unloaded again.
   * Theoretically this is not needed, but practically it doesn't hurt.
   * And we're rather safe than sorry. */
  if (plugin->module)
    g_module_make_resident (plugin->module);

  if (user_data) {
    // plugin_init call
    if (!(((GstPluginInitFullFunc) (desc->plugin_init)) (plugin, user_data))) {
      if (GST_CAT_DEFAULT)
        GST_WARNING ("plugin \"%s\" failed to initialise",
            GST_STR_NULL (plugin->filename));
      return NULL;
    }
  } else {
    // plugin_init call
    if (!((desc->plugin_init) (plugin))) {
      if (GST_CAT_DEFAULT)
        GST_WARNING ("plugin \"%s\" failed to initialise",
            GST_STR_NULL (plugin->filename));
      return NULL;
    }
  }

  if (GST_CAT_DEFAULT)
    GST_LOG ("plugin \"%s\" initialised", GST_STR_NULL (plugin->filename));

  return plugin;
}

gst_registry_add_plugin

gst_registry_add_plugin() function and GST_ registry_ add_ The feature () function is similar to gst_registry_add_feature() is a method of saving a feature to a global variable_ gst_ registry_ The ` ` priv - > features member of default, while gst_registry_add_plugin will add the created plugin to and save it to_ gst_ registry_ Priv - > plugins ` member of default.

gboolean
gst_registry_add_plugin (GstRegistry * registry, GstPlugin * plugin)
{
  GstPlugin *existing_plugin;

  g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
  g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);

  GST_OBJECT_LOCK (registry);
  if (G_LIKELY (plugin->basename)) {
    /* we have a basename, see if we find the plugin */
    existing_plugin =
        gst_registry_lookup_bn_locked (registry, plugin->basename);
    if (existing_plugin) {
       // ... ...
    }
  }

  // When pipeline is running, grep 'adding plugin' can see the log registered by the plugin
  GST_DEBUG_OBJECT (registry, "adding plugin %p for filename \"%s\"",
      plugin, GST_STR_NULL (plugin->filename));

  // Add plugin to the list of registry
  registry->priv->plugins = g_list_prepend (registry->priv->plugins, plugin);
  ++registry->priv->n_plugins;

  if (G_LIKELY (plugin->basename))
    g_hash_table_replace (registry->priv->basename_hash, plugin->basename,
        plugin);

  gst_object_ref_sink (plugin);
  GST_OBJECT_UNLOCK (registry);

  GST_LOG_OBJECT (registry, "emitting plugin-added for filename \"%s\"",
      GST_STR_NULL (plugin->filename));
  g_signal_emit (registry, gst_registry_signals[PLUGIN_ADDED], 0, plugin);

  return TRUE;
}

log output of libav plugin:

grep 'adding plugin' pipeline.log

adding plugin 0x55fdcb68d190 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstx265.so"
adding plugin 0x55fdcb68d2c0 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgsttimecode.so"
adding plugin 0x55fdcb68d3f0 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstinterlace.so"
adding plugin 0x55fdcb68d520 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstdc1394.so"
adding plugin 0x55fdcb68d650 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstde265.so"
adding plugin 0x55fdcb68d780 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstadpcmdec.so"
adding plugin 0x55fdcb68d8b0 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstfaad.so"
adding plugin 0x55fdcb68d9e0 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstvcdsrc.so"
adding plugin 0x55fdcb68db10 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstfrei0r.so"
adding plugin 0x55fdcb68dc40 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstfieldanalysis.so"
adding plugin 0x55fdcb68dd70 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstasf.so"
adding plugin 0x55fdcb68dea0 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstopenglmixers.so"
adding plugin 0x55fdcb697060 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstwaylandsink.so"
adding plugin 0x55fdcb697190 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstfbdevsink.so"
adding plugin 0x55fdcb6972c0 for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstuvch264.so"


grep 'plugin-added' pipeline.log
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstx265.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgsttimecode.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstinterlace.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstdc1394.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstde265.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstadpcmdec.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstfaad.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstvcdsrc.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstfrei0r.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstfieldanalysis.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstasf.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstopenglmixers.so"
emitting plugin-added for filename "/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstwaylandsink.so"

plugin_init

The plugin parameter is in GST_ plugin_ register_ Calling plugin_ in static When using the init function,

static gboolean
plugin_init (GstPlugin * plugin)
{
  // Initialize debug parameters
  GST_DEBUG_CATEGORY_INIT (ffmpeg_debug, "libav", 0, "libav elements");

  /* Bail if not FFmpeg. We can no longer ensure operation with Libav */
  if (!gst_ffmpeg_avcodec_is_ffmpeg ()) {
    GST_ERROR_OBJECT (plugin,
        "Incompatible, non-FFmpeg libavcodec/format found");
    return FALSE;
  }
    
  // Set callback
#ifndef GST_DISABLE_GST_DEBUG
  av_log_set_callback (gst_ffmpeg_log_callback);
#endif

  // Initialize pix_fmt
  gst_ffmpeg_init_pix_fmt_info ();

  // Initialize parameters such as bitrate / GOP / max bframe / profile / level / color
  /* build global ffmpeg param/property info */
  gst_ffmpeg_cfg_init ();

  // Register audio enc/video enc/audio dec/video dec/demux/mux/interlace respectively
  gst_ffmpegaudenc_register (plugin);
  gst_ffmpegvidenc_register (plugin);
  gst_ffmpegauddec_register (plugin);
  gst_ffmpegviddec_register (plugin);
  gst_ffmpegdemux_register (plugin);
  gst_ffmpegmux_register (plugin);
  gst_ffmpegdeinterlace_register (plugin);

  /* Now we can return the pointer to the newly created Plugin object. */
  return TRUE;
}

gst_ffmpeg_avcodec_is_ffmpeg

Judge whether the version of ffmpeg is valid, avcodec_version is a function in ffmpeg, which is in ffmpeg / libavcodec / avcodec In C:

static inline gboolean
gst_ffmpeg_avcodec_is_ffmpeg (void)
{
  guint av_version = avcodec_version ();

  GST_DEBUG ("Using libavcodec version %d.%d.%d",
      av_version >> 16, (av_version & 0x00ff00) >> 8, av_version & 0xff);

  /* FFmpeg *_MICRO versions start at 100 and Libav's at 0 */
  if ((av_version & 0xff) < 100)
    return FALSE;

  return TRUE;
}

gst_ ffmpegdemux_ Expansion of register

Define typeinfo

Several function pointers for initializing typeinfo:

  • base_init = gst_ffmpegdemux_base_init

  • class_init = gst_ffmpegdemux_class_init

  • instance_init = gst_ffmpegdemux_init

      GTypeInfo typeinfo = {
        sizeof (GstFFMpegDemuxClass),
        (GBaseInitFunc) gst_ffmpegdemux_base_init,
        NULL,
        (GClassInitFunc) gst_ffmpegdemux_class_init,
        NULL,
        NULL,
        sizeof (GstFFMpegDemux),
        0,
        (GInstanceInitFunc) gst_ffmpegdemux_init,
      };
    

Register all demuxer s in ffmpeg

The code is as follows, av_demuxer_iterate is defined in ffmpeg / libavformat / allformats C, iterate and traverse demuxer in turn_ All demux in the list.

The whole while loop will traverse all plugin s, if register_typefind_func set to false, gst_type_find_register certainly won't come.

while ((in_plugin = av_demuxer_iterate (&i))) {
    /* Don't use the typefind functions of formats for which we already have
     * better typefind functions */
    if (!strcmp (in_plugin->name, "mov,mp4,m4a,3gp,3g2,mj2") ||
        !strcmp (in_plugin->name, "ass") ||
        // ...
        ) {
        // typefind is set to FALSE without libav
        register_typefind_func = FALSE;
    }
    
    /* Set the rank of demuxers known to work to MARGINAL.
     * Set demuxers for which we already have another implementation to NONE
     * Set All others to NONE*/
    if (!strcmp(in_plugin->name, "mov,mp4,m4a,3gp,3g2,mj2") ||
        !strcmp(in_plugin->name, "matroska,webm") ||
        !strcmp(in_plugin->name, "mpegts") ||
        !strcmp(in_plugin->name, "flv") ||
        // ...
        ) {
        // The code of this department has been modified to set the value of avdemux to the maximum rank value and add mov, etc
        rank = GST_RANK_PRIMARY + 1;
    }
    
    // Generate type_name. Finally, you can see avdemux through gst-inspect-1.0 | grep avdemux_gif,avdemux_ape, wait
    /* construct the type */
    type_name = g_strdup_printf ("avdemux_%s", in_plugin->name);
    g_strdelimit (type_name, ".,|-<> ", '_');
    
    
    // Generate typefind_name, dup in_ plugin->name
    typefind_name = g_strdup_printf ("avtype_%s", in_plugin->name);
    g_strdelimit (typefind_name, ".,|-<> ", '_');

    // g_type_register_static is a function of GObject
    // GST_TYPE_ELEMENT is the element type returned
    // typeinfo is the initial typeinfo
    /* create the type now */
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
    g_type_set_qdata (type, GST_FFDEMUX_PARAMS_QDATA, (gpointer) in_plugin);
    
    // gst_element_register register plugin
    // register_ typefind_ When func is true, register type_find
    if (!gst_element_register (plugin, type_name, rank, type) ||
        (register_typefind_func == TRUE &&
            !gst_type_find_register (plugin, typefind_name, rank,
                gst_ffmpegdemux_type_find, extensions, NULL,
                (gpointer) in_plugin, NULL))) {
      g_warning ("Registration of type %s failed", type_name);
      g_free (type_name);
      g_free (typefind_name);
      g_free (extensions);
      return FALSE;
    }
}

g_type_register_static

gobject/gtype.c

GType
g_type_register_static (GType            parent_type,
			const gchar     *type_name,
			const GTypeInfo *info,
			GTypeFlags	 flags)
{
  TypeNode *pnode, *node;
  GType type = 0;
  
  g_assert_type_system_initialized ();
  g_return_val_if_fail (parent_type > 0, 0);
  g_return_val_if_fail (type_name != NULL, 0);
  g_return_val_if_fail (info != NULL, 0);
  
  if (!check_type_name_I (type_name) ||
      !check_derivation_I (parent_type, type_name))
    return 0;
  if (info->class_finalize)
    {
      g_warning ("class finalizer specified for static type '%s'",
		 type_name);
      return 0;
    }
  
  pnode = lookup_type_node_I (parent_type);
  G_WRITE_LOCK (&type_rw_lock);
  type_data_ref_Wm (pnode);
  if (check_type_info_I (pnode, NODE_FUNDAMENTAL_TYPE (pnode), type_name, info))
    {
      node = type_node_new_W (pnode, type_name, NULL);
      type_add_flags_W (node, flags);
      type = NODE_TYPE (node);
      // This associates info with node
      type_data_make_W (node, info,
			check_value_table_I (type_name, info->value_table) ? info->value_table : NULL);
    }
  G_WRITE_UNLOCK (&type_rw_lock);
  
  return type;
}

type_data_make_W

From type_ data_ make_ Class can be seen in the implementation of W_ Init, GST in front_ ffmpegdemux_ The three corresponding base functions in GTypeinfo defined by register at the beginning_ init = gst_ ffmpegdemux_ base_ init,class_init = gst_ffmpegdemux_class_init,instance_init = gst_ffmpegdemux_init is associated with data in this function.

struct _GTypeInfo
{
  /* interface types, classed types, instantiated types */
  guint16                class_size;
  
  GBaseInitFunc          base_init;
  GBaseFinalizeFunc      base_finalize;
  
  /* interface types, classed types, instantiated types */
  GClassInitFunc         class_init;
  GClassFinalizeFunc     class_finalize;
  gconstpointer          class_data;
  
  /* instantiated types */
  guint16                instance_size;
  guint16                n_preallocs;
  GInstanceInitFunc      instance_init;
  
  /* value handling */
  const GTypeValueTable	*value_table;
};
union _TypeData
{
  CommonData         common;
  BoxedData          boxed;
  IFaceData          iface;
  ClassData          class;
  InstanceData       instance;
};
struct _InstanceData
{
  CommonData         common;
  guint16            class_size;
  guint16            class_private_size;
  int volatile       init_state; /* atomic - g_type_class_ref reads it unlocked */
  GBaseInitFunc      class_init_base;
  GBaseFinalizeFunc  class_finalize_base;
  GClassInitFunc     class_init;
  GClassFinalizeFunc class_finalize;
  gconstpointer      class_data;
  gpointer           class;
  guint16            instance_size;
  guint16            private_size;
  guint16            n_preallocs;
  GInstanceInitFunc  instance_init;
};
struct _ClassData
{
  CommonData         common;
  guint16            class_size;
  guint16            class_private_size;
  int volatile       init_state; /* atomic - g_type_class_ref reads it unlocked */
  GBaseInitFunc      class_init_base;
  GBaseFinalizeFunc  class_finalize_base;
  GClassInitFunc     class_init;
  GClassFinalizeFunc class_finalize;
  gconstpointer      class_data;
  gpointer           class;
};

TypeData is a union type, which can be_ ClassData, or_ InstanceData, in type_data_make_W use_ Base in GTypeInfo_ init,class_init,instance_init to assign to the corresponding item, then instance_ Where is init called?

static void
type_data_make_W (TypeNode              *node,
		  const GTypeInfo       *info,
		  const GTypeValueTable *value_table)
{
  TypeData *data;
  GTypeValueTable *vtable = NULL;
  guint vtable_size = 0;
  
  g_assert (node->data == NULL && info != NULL);
  
  if (!value_table)
    {
      TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));
      
      if (pnode)
	vtable = pnode->data->common.value_table;
      else
	{
	  static const GTypeValueTable zero_vtable = { NULL, };
	  
	  value_table = &zero_vtable;
	}
    }
  if (value_table)
    {
      /* need to setup vtable_size since we have to allocate it with data in one chunk */
      vtable_size = sizeof (GTypeValueTable);
      if (value_table->collect_format)
	vtable_size += strlen (value_table->collect_format);
      if (value_table->lcopy_format)
	vtable_size += strlen (value_table->lcopy_format);
      vtable_size += 2;
    }
   
  if (node->is_instantiatable) /* careful, is_instantiatable is also is_classed */
    {
      TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));

      data = g_malloc0 (sizeof (InstanceData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (InstanceData));
      data->instance.class_size = info->class_size;
      data->instance.class_init_base = info->base_init;
      data->instance.class_finalize_base = info->base_finalize;
      data->instance.class_init = info->class_init;
      data->instance.class_finalize = info->class_finalize;
      data->instance.class_data = info->class_data;
      data->instance.class = NULL;
      data->instance.init_state = UNINITIALIZED;
      data->instance.instance_size = info->instance_size;
      /* We'll set the final value for data->instance.private size
       * after the parent class has been initialized
       */
      data->instance.private_size = 0;
      data->instance.class_private_size = 0;
      if (pnode)
        data->instance.class_private_size = pnode->data->instance.class_private_size;
      data->instance.n_preallocs = MIN (info->n_preallocs, 1024);
      data->instance.instance_init = info->instance_init;
    }
  else if (node->is_classed) /* only classed */
    {
      TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));

      data = g_malloc0 (sizeof (ClassData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (ClassData));
      data->class.class_size = info->class_size;
      data->class.class_init_base = info->base_init;
      data->class.class_finalize_base = info->base_finalize;
      data->class.class_init = info->class_init;
      data->class.class_finalize = info->class_finalize;
      data->class.class_data = info->class_data;
      data->class.class = NULL;
      data->class.class_private_size = 0;
      if (pnode)
        data->class.class_private_size = pnode->data->class.class_private_size;
      data->class.init_state = UNINITIALIZED;
    }
  else if (NODE_IS_IFACE (node))
    {
      data = g_malloc0 (sizeof (IFaceData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (IFaceData));
      data->iface.vtable_size = info->class_size;
      data->iface.vtable_init_base = info->base_init;
      data->iface.vtable_finalize_base = info->base_finalize;
      data->iface.dflt_init = info->class_init;
      data->iface.dflt_finalize = info->class_finalize;
      data->iface.dflt_data = info->class_data;
      data->iface.dflt_vtable = NULL;
    }
  else if (NODE_IS_BOXED (node))
    {
      data = g_malloc0 (sizeof (BoxedData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (BoxedData));
    }
  else
    {
      data = g_malloc0 (sizeof (CommonData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (CommonData));
    }
  
  node->data = data;
  
  if (vtable_size)
    {
      gchar *p;
      
      /* we allocate the vtable and its strings together with the type data, so
       * children can take over their parent's vtable pointer, and we don't
       * need to worry freeing it or not when the child data is destroyed
       */
      *vtable = *value_table;
      p = G_STRUCT_MEMBER_P (vtable, sizeof (*vtable));
      p[0] = 0;
      vtable->collect_format = p;
      if (value_table->collect_format)
	{
	  strcat (p, value_table->collect_format);
	  p += strlen (value_table->collect_format);
	}
      p++;
      p[0] = 0;
      vtable->lcopy_format = p;
      if (value_table->lcopy_format)
	strcat  (p, value_table->lcopy_format);
    }
  node->data->common.value_table = vtable;
  node->mutatable_check_cache = (node->data->common.value_table->value_init != NULL &&
				 !((G_TYPE_FLAG_VALUE_ABSTRACT | G_TYPE_FLAG_ABSTRACT) &
				   GPOINTER_TO_UINT (type_get_qdata_L (node, static_quark_type_flags))));
  
  g_assert (node->data->common.value_table != NULL); /* paranoid */

  g_atomic_int_set ((int *) &node->ref_count, 1);
}

g_type_create_instance

gobject/gtype.c

Call instance instance_ Size is in G_ type_ create_ In instance, these belong to Glib, which is actually through g_object_new to indirectly call g_type_create_instance, the call stack is roughly as follows. See Glib's code for details:

g_object_new
	- g_object_new_internal
		- g_type_create_instance
GTypeInstance*
g_type_create_instance (GType type)
{
  TypeNode *node;
  GTypeInstance *instance;
  GTypeClass *class;
  gchar *allocated;
  gint private_size;
  gint ivar_size;
  guint i;

  node = lookup_type_node_I (type);
  if (!node || !node->is_instantiatable)
    {
      g_error ("cannot create new instance of invalid (non-instantiatable) type '%s'",
		 type_descriptive_name_I (type));
    }
  /* G_TYPE_IS_ABSTRACT() is an external call: _U */
  if (!node->mutatable_check_cache && G_TYPE_IS_ABSTRACT (type))
    {
      g_error ("cannot create instance of abstract (non-instantiatable) type '%s'",
		 type_descriptive_name_I (type));
    }
  
  class = g_type_class_ref (type);

  /* We allocate the 'private' areas before the normal instance data, in
   * reverse order.  This allows the private area of a particular class
   * to always be at a constant relative address to the instance data.
   * If we stored the private data after the instance data this would
   * not be the case (since a subclass that added more instance
   * variables would push the private data further along).
   *
   * This presents problems for valgrindability, of course, so we do a
   * workaround for that case.  We identify the start of the object to
   * valgrind as an allocated block (so that pointers to objects show up
   * as 'reachable' instead of 'possibly lost').  We then add an extra
   * pointer at the end of the object, after all instance data, back to
   * the start of the private area so that it is also recorded as
   * reachable.  We also add extra private space at the start because
   * valgrind doesn't seem to like us claiming to have allocated an
   * address that it saw allocated by malloc().
   */
  private_size = node->data->instance.private_size;
  ivar_size = node->data->instance.instance_size;

#ifdef ENABLE_VALGRIND
  if (private_size && RUNNING_ON_VALGRIND)
    {
      private_size += ALIGN_STRUCT (1);

      /* Allocate one extra pointer size... */
      allocated = g_slice_alloc0 (private_size + ivar_size + sizeof (gpointer));
      /* ... and point it back to the start of the private data. */
      *(gpointer *) (allocated + private_size + ivar_size) = allocated + ALIGN_STRUCT (1);

      /* Tell valgrind that it should treat the object itself as such */
      VALGRIND_MALLOCLIKE_BLOCK (allocated + private_size, ivar_size + sizeof (gpointer), 0, TRUE);
      VALGRIND_MALLOCLIKE_BLOCK (allocated + ALIGN_STRUCT (1), private_size - ALIGN_STRUCT (1), 0, TRUE);
    }
  else
#endif
    allocated = g_slice_alloc0 (private_size + ivar_size);

  instance = (GTypeInstance *) (allocated + private_size);

  for (i = node->n_supers; i > 0; i--)
    {
      TypeNode *pnode;
      
      pnode = lookup_type_node_I (node->supers[i]);
      if (pnode->data->instance.instance_init)
	{
	  instance->g_class = pnode->data->instance.class;
	  pnode->data->instance.instance_init (instance, class);
	}
    }

  instance->g_class = class;
  if (node->data->instance.instance_init)
    node->data->instance.instance_init (instance, class);

#ifdef	G_ENABLE_DEBUG
  IF_DEBUG (INSTANCE_COUNT)
    {
      g_atomic_int_inc ((int *) &node->instance_count);
    }
#endif

  TRACE(GOBJECT_OBJECT_NEW(instance, type));

  return instance;
}

av_demuxer_iterate

Traverse demuxer in turn_ All demux in list

miui12-q-cas-stable.xmlconst AVInputFormat *av_demuxer_iterate(void **opaque)
{
    static const uintptr_t size = sizeof(demuxer_list)/sizeof(demuxer_list[0]) - 1;
    uintptr_t i = (uintptr_t)*opaque;
    const AVInputFormat *f = NULL;

    if (i < size) {
        f = demuxer_list[i];
    } else if (indev_list) {
        f = indev_list[i - size];
    }

    // opaque is passed in by an int * pointer, and the initial value of * quaque is 0,
    // Assigned to i,i points to demuxer_ The first demuxer in the list, i+1, is the second corresponding address
    if (f)
        *opaque = (void*)(i + 1);
    return f;
}

demuxer_list

demuxer_list has more than 300 lines and defines various demux to FF_ mov_ Taking demuxer as an example, we can see that the definition of movdemux includes MOV, MP4, m4a, 3gp, 3g2 and MJ2

static const AVInputFormat * const demuxer_list[] = {
    &ff_aa_demuxer,
    &ff_aac_demuxer,
    &ff_aax_demuxer,
    &ff_ac3_demuxer,
    &ff_ace_demuxer,
    &ff_acm_demuxer,
    // ...
};

ff_mov_demuxer

AVInputFormat ff_mov_demuxer = {
    .name           = "mov,mp4,m4a,3gp,3g2,mj2",
    .long_name      = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
    .priv_class     = &mov_class,
    .priv_data_size = sizeof(MOVContext),
    .extensions     = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v",
    .read_probe     = mov_probe,
    .read_header    = mov_read_header,
    .read_packet    = mov_read_packet,
    .read_close     = mov_read_close,
    .read_seek      = mov_read_seek,
    .flags          = AVFMT_NO_BYTE_SEEK | AVFMT_SEEK_TO_PTS,
};

gst_element_register

Through gst_element_register() function registers the corresponding information of the plugin into gstreamer. Through this function, you can create a type elementfactory with name and priority rank, and add elementfactory to the registry.

gboolean
gst_element_register (GstPlugin * plugin, const gchar * name, guint rank,
    GType type)
{
  GstPluginFeature *existing_feature;
  GstRegistry *registry;
  GstElementFactory *factory;
  GType *interfaces;
  guint n_interfaces, i;
  GstElementClass *klass;
  GList *item;

  g_return_val_if_fail (name != NULL, FALSE);
  g_return_val_if_fail (g_type_is_a (type, GST_TYPE_ELEMENT), FALSE);

  registry = gst_registry_get ();

  /* check if feature already exists, if it exists there is no need to update it
   * when the registry is getting updated, outdated plugins and all their
   * features are removed and readded.
   */
  existing_feature = gst_registry_lookup_feature (registry, name);
  if (existing_feature && existing_feature->plugin == plugin) {
    GST_DEBUG_OBJECT (registry, "update existing feature %p (%s)",
        existing_feature, name);
    factory = GST_ELEMENT_FACTORY_CAST (existing_feature);
    factory->type = type;
    existing_feature->loaded = TRUE;
    g_type_set_qdata (type, __gst_elementclass_factory, factory);
    gst_object_unref (existing_feature);
    return TRUE;
  } else if (existing_feature) {
    gst_object_unref (existing_feature);
  }

  factory = g_object_new (GST_TYPE_ELEMENT_FACTORY, NULL);
  gst_plugin_feature_set_name (GST_PLUGIN_FEATURE_CAST (factory), name);
  GST_LOG_OBJECT (factory, "Created new elementfactory for type %s",
      g_type_name (type));

  /* provide info needed during class structure setup */
  g_type_set_qdata (type, __gst_elementclass_factory, factory);
  klass = GST_ELEMENT_CLASS (g_type_class_ref (type));

  CHECK_METADATA_FIELD (klass, name, GST_ELEMENT_METADATA_LONGNAME);
  CHECK_METADATA_FIELD (klass, name, GST_ELEMENT_METADATA_KLASS);
  CHECK_METADATA_FIELD (klass, name, GST_ELEMENT_METADATA_DESCRIPTION);
  CHECK_METADATA_FIELD (klass, name, GST_ELEMENT_METADATA_AUTHOR);

  factory->type = type;
  factory->metadata = gst_structure_copy ((GstStructure *) klass->metadata);

  for (item = klass->padtemplates; item; item = item->next) {
    GstPadTemplate *templ = item->data;
    GstStaticPadTemplate *newt;
    gchar *caps_string = gst_caps_to_string (templ->caps);

    newt = g_slice_new (GstStaticPadTemplate);
    newt->name_template = g_intern_string (templ->name_template);
    newt->direction = templ->direction;
    newt->presence = templ->presence;
    newt->static_caps.caps = NULL;
    newt->static_caps.string = g_intern_string (caps_string);
    factory->staticpadtemplates =
        g_list_append (factory->staticpadtemplates, newt);

    g_free (caps_string);
  }
  factory->numpadtemplates = klass->numpadtemplates;

  /* special stuff for URI handling */
  if (g_type_is_a (type, GST_TYPE_URI_HANDLER)) {
    GstURIHandlerInterface *iface = (GstURIHandlerInterface *)
        g_type_interface_peek (klass, GST_TYPE_URI_HANDLER);

    if (!iface || !iface->get_type || !iface->get_protocols)
      goto urierror;
    if (iface->get_type)
      factory->uri_type = iface->get_type (factory->type);
    if (!GST_URI_TYPE_IS_VALID (factory->uri_type))
      goto urierror;
    if (iface->get_protocols) {
      const gchar *const *protocols;

      protocols = iface->get_protocols (factory->type);
      factory->uri_protocols = g_strdupv ((gchar **) protocols);
    }
    if (!factory->uri_protocols)
      goto urierror;
  }

  interfaces = g_type_interfaces (type, &n_interfaces);
  for (i = 0; i < n_interfaces; i++) {
    __gst_element_factory_add_interface (factory, g_type_name (interfaces[i]));
  }
  g_free (interfaces);

  if (plugin && plugin->desc.name) {
    GST_PLUGIN_FEATURE_CAST (factory)->plugin_name = plugin->desc.name;
    GST_PLUGIN_FEATURE_CAST (factory)->plugin = plugin;
    g_object_add_weak_pointer ((GObject *) plugin,
        (gpointer *) & GST_PLUGIN_FEATURE_CAST (factory)->plugin);
  } else {
    GST_PLUGIN_FEATURE_CAST (factory)->plugin_name = "NULL";
    GST_PLUGIN_FEATURE_CAST (factory)->plugin = NULL;
  }
  gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE_CAST (factory), rank);
  GST_PLUGIN_FEATURE_CAST (factory)->loaded = TRUE;

  gst_registry_add_feature (registry, GST_PLUGIN_FEATURE_CAST (factory));

  return TRUE;

  /* ERRORS */
urierror:
  {
    GST_WARNING_OBJECT (factory, "error with uri handler!");
    gst_element_factory_cleanup (factory);
    return FALSE;
  }

detailserror:
  {
    gst_element_factory_cleanup (factory);
    return FALSE;
  }
}

Keywords: GStreamer

Added by 10legit10quit on Tue, 15 Feb 2022 17:10:24 +0200