<commit_before>#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gst/gst.h>

#include "kmsagnosticbin.h"
#include "kmsagnosticcaps.h"

#define PLUGIN_NAME "agnosticbin"

#define INPUT_TEE "input_tee"
#define DECODED_TEE "decoded_tee"

#define OLD_EVENT_FUNC_DATA "old_event_func"
#define START_STOP_EVENT_FUNC_DATA "start_stop_event_func"
#define AGNOSTIC_BIN_DATA "agnostic_bin"
#define DECODEBIN_VALVE_DATA "decodebin_valve"

#define ENCODER_QUEUE_DATA "enc_queue"
#define ENCODER_TEE_DATA "enc_tee"
#define ENCODER_CONVERT_DATA "enc_convert"

#define START_STOP_EVENT_NAME "event/start-stop"
#define START "start"

static GstStaticCaps static_raw_audio_caps =
GST_STATIC_CAPS (KMS_AGNOSTIC_RAW_AUDIO_CAPS);
static GstStaticCaps static_raw_video_caps =
GST_STATIC_CAPS (KMS_AGNOSTIC_RAW_VIDEO_CAPS);

static GstStaticCaps static_raw_caps = GST_STATIC_CAPS (KMS_AGNOSTIC_RAW_CAPS);

GST_DEBUG_CATEGORY_STATIC (kms_agnostic_bin_debug);
#define GST_CAT_DEFAULT kms_agnostic_bin_debug

#define kms_agnostic_bin_parent_class parent_class
G_DEFINE_TYPE (KmsAgnosticBin, kms_agnostic_bin, GST_TYPE_BIN);

typedef void (*GstStartStopFunction) (KmsAgnosticBin * agnosticbin,
    GstElement * element, gboolean start);

/* Filter signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

enum
{
  PROP_0
};

/* the capabilities of the inputs and outputs. */
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (KMS_AGNOSTIC_AGNOSTIC_CAPS)
    );

static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src_%u",
    GST_PAD_SRC,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS (KMS_AGNOSTIC_AGNOSTIC_CAPS)
    );

static void
kms_agnostic_bin_valve_start_stop (KmsAgnosticBin * agnosticbin,
    GstElement * valve, gboolean start)
{
  g_object_set (valve, "drop", !start, NULL);
}

static void
kms_agnostic_bin_decodebin_start_stop (KmsAgnosticBin * agnosticbin,
    GstElement * decodebin, gboolean start)
{
  GstElement *valve =
      g_object_get_data (G_OBJECT (decodebin), DECODEBIN_VALVE_DATA);

  g_object_set (valve, "drop", !start, NULL);
}

static gboolean
kms_agnostic_bin_start_stop_event_handler (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  gboolean ret = TRUE;
  GstPadEventFunction old_func =
      g_object_get_data (G_OBJECT (pad), OLD_EVENT_FUNC_DATA);

  if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) {
    const GstStructure *st = gst_event_get_structure (event);

    if (st != NULL
        && g_strcmp0 (gst_structure_get_name (st),
            START_STOP_EVENT_NAME) == 0) {
      gboolean start;

      if (gst_structure_get_boolean (st, START, &start)) {
        KmsAgnosticBin *agnosticbin =
            g_object_get_data (G_OBJECT (pad), AGNOSTIC_BIN_DATA);
        GstStartStopFunction callback =
            g_object_get_data (G_OBJECT (pad), START_STOP_EVENT_FUNC_DATA);
        GST_INFO ("Received event: %P", event);

        if (callback != NULL)
          callback (agnosticbin, GST_ELEMENT (parent), start);

        gst_event_unref (event);
        return TRUE;
      }
    }
  }

  if (old_func != NULL)
    ret = old_func (pad, parent, event);
  else
    ret = gst_pad_event_default (pad, parent, event);

  return ret;
}

static void
kms_agnostic_bin_set_start_stop_event_handler (KmsAgnosticBin * agnosticbin,
    GstElement * element, const char *pad_name, GstStartStopFunction callback)
{
  GstPad *pad = gst_element_get_static_pad (element, pad_name);

  if (pad == NULL)
    return;

  g_object_set_data (G_OBJECT (pad), OLD_EVENT_FUNC_DATA, pad->eventfunc);
  g_object_set_data (G_OBJECT (pad), START_STOP_EVENT_FUNC_DATA, callback);
  g_object_set_data (G_OBJECT (pad), AGNOSTIC_BIN_DATA, agnosticbin);
  gst_pad_set_event_function (pad, kms_agnostic_bin_start_stop_event_handler);
  g_object_unref (pad);
}

static void
kms_agnostic_bin_send_start_stop_event (GstPad * pad, gboolean start)
{
  GstStructure *data =
      gst_structure_new (START_STOP_EVENT_NAME, START, G_TYPE_BOOLEAN, start,
      NULL);
  GstEvent *event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, data);

  if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
    gst_pad_send_event (pad, event);
  } else {
    GstPad *peer = gst_pad_get_peer (pad);

    if (peer != NULL && GST_PAD_DIRECTION (peer) == GST_PAD_SRC)
      gst_pad_send_event (peer, event);
    else
      gst_event_unref (event);

    if (peer != NULL)
      g_object_unref (peer);
  }
}

static void
kms_agnostic_bin_send_force_key_unit_event (GstPad * pad)
{
  GstStructure *s;
  GstEvent *force_key_unit_event;

  GST_DEBUG ("Sending key ");
  s = gst_structure_new ("GstForceKeyUnit",
      "all-headers", G_TYPE_BOOLEAN, TRUE, NULL);
  force_key_unit_event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s);
  gst_pad_send_event (pad, force_key_unit_event);
}

static void
kms_agnostic_bin_unlink_from_tee (GstElement * element, const gchar * pad_name)
{
  GstPad *sink = gst_element_get_static_pad (element, pad_name);
  GstPad *tee_src = gst_pad_get_peer (sink);
  GstElement *tee;

  g_object_unref (sink);

  if (tee_src != NULL) {
    tee = gst_pad_get_parent_element (tee_src);

    if (tee != NULL) {
      gint n_pads = 0;
      GstPad *tee_sink = gst_element_get_static_pad (tee, "sink");

      GST_PAD_STREAM_LOCK (tee_sink);
      gst_element_unlink (tee, element);
      gst_element_release_request_pad (tee, tee_src);

      g_object_get (tee, "num-src-pads", &n_pads, NULL);
      if (n_pads == 0)
        kms_agnostic_bin_send_start_stop_event (tee_sink, FALSE);

      GST_PAD_STREAM_UNLOCK (tee_sink);
      g_object_unref (tee_sink);
    }

    g_object_unref (tee_src);
  }
}

static void
kms_agnostic_bin_link_to_tee (GstElement * tee, GstElement * element,
    const gchar * sink_name)
{
  GstPad *tee_sink = gst_element_get_static_pad (tee, "sink");

  GstPad *sink = gst_element_get_static_pad (element, sink_name);
  GstPad *tee_src = gst_pad_get_peer (sink);

  if (tee_src != NULL) {
    GstElement *old_tee;

    old_tee = gst_pad_get_parent_element (tee_src);

    if (tee != old_tee)
      kms_agnostic_bin_unlink_from_tee (element, sink_name);

    if (old_tee)
      g_object_unref (old_tee);

    g_object_unref (tee_src);
  }

  g_object_unref (sink);

  GST_PAD_STREAM_LOCK (tee_sink);
  tee_src = gst_element_get_request_pad (tee, "src_%u");
  if (tee_src != NULL) {
    if (!gst_element_link_pads (tee, GST_OBJECT_NAME (tee_src), element,
            sink_name)) {
      gst_element_release_request_pad (tee, tee_src);
    } else {
      kms_agnostic_bin_send_start_stop_event (tee_sink, TRUE);

      kms_agnostic_bin_send_force_key_unit_event (tee_src);
    }

    g_object_unref (tee_src);
  }

  GST_PAD_STREAM_UNLOCK (tee_sink);
  g_object_unref (tee_sink);
}

static void
kms_agnostic_bin_encoder_start_stop (KmsAgnosticBin * agnosticbin,
    GstElement * encoder, gboolean start)
{
  GstElement *queue, *tee, *convert;

  if (start)
    return;

  convert = g_object_get_data (G_OBJECT (encoder), ENCODER_CONVERT_DATA);
  queue = g_object_get_data (G_OBJECT (encoder), ENCODER_QUEUE_DATA);
  tee = g_object_get_data (G_OBJECT (encoder), ENCODER_TEE_DATA);

  g_hash_table_remove (agnosticbin->encoded_tees, GST_OBJECT_NAME (tee));

  if (queue != NULL)
    kms_agnostic_bin_unlink_from_tee (queue, "sink");

  gst_element_set_state (queue, GST_STATE_NULL);
  gst_element_set_state (convert, GST_STATE_NULL);
  gst_element_set_state (encoder, GST_STATE_NULL);
  gst_element_set_state (tee, GST_STATE_NULL);

  gst_bin_remove_many (GST_BIN (agnosticbin), queue, convert, encoder, tee,
      NULL);
}

static GstElement *
kms_agnostic_get_convert_element_for_raw_caps (GstCaps * raw_caps)
{
  GstElement *convert = NULL;
  GstCaps *audio_caps = gst_static_caps_get (&static_raw_audio_caps);
  GstCaps *video_caps = gst_static_caps_get (&static_raw_video_caps);

  if (gst_caps_can_intersect (raw_caps, video_caps)) {
    convert = gst_element_factory_make ("videoconvert", NULL);
  } else if (gst_caps_can_intersect (raw_caps, audio_caps)) {
    convert = gst_element_factory_make ("audioconvert", NULL);
  }

  if (convert == NULL)
    convert = gst_element_factory_make ("identity", NULL);

  gst_caps_unref (audio_caps);
  gst_caps_unref (video_caps);

  return convert;
}

static void
configure_encoder (GstElement * encoder, const gchar * factory_name)
{
  GST_DEBUG ("Configure encoder: %s", factory_name);
  if (g_strcmp0 ("vp8enc", factory_name) == 0) {
    g_object_set (G_OBJECT (encoder), "deadline", 80 * GST_MSECOND,
        "threads", 4, NULL);
  }
}

/* This functions is called with the KMS_AGNOSTIC_BIN_LOCK held */
static GstElement *
kms_agnostic_bin_create_encoded_tee (KmsAgnosticBin * agnosticbin,
    GstCaps * allowed_caps)
{
  GstElement *queue, *encoder, *decoded_tee, *tee = NULL, *convert;
  GList *encoder_list, *filtered_list, *l;
  GstElementFactory *encoder_factory = NULL;
  GstPad *decoded_tee_sink;
  GstCaps *raw_caps;

  GST_DEBUG ("Creating a new encoded tee for caps: %P", allowed_caps);

  decoded_tee = gst_bin_get_by_name (GST_BIN (agnosticbin), DECODED_TEE);
  if (decoded_tee == NULL)
    return NULL;

  decoded_tee_sink = gst_element_get_static_pad (decoded_tee, "sink");
  raw_caps = gst_pad_query_caps (decoded_tee_sink, NULL);

  if (raw_caps == NULL)
    goto release_decoded_tee;

  encoder_list =
      gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER,
      GST_RANK_NONE);
  filtered_list =
      gst_element_factory_list_filter (encoder_list, allowed_caps, GST_PAD_SRC,
      FALSE);

  for (l = filtered_list; l != NULL && encoder_factory == NULL; l = l->next) {
    encoder_factory = GST_ELEMENT_FACTORY (l->data);
    if (gst_element_factory_get_num_pad_templates (encoder_factory) != 2) {
      encoder_factory = NULL;
    }
  }
<commit_msg>agnosticbin: Use allowed caps instead of query to get current raw caps<commit_after>#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gst/gst.h>

#include "kmsagnosticbin.h"
#include "kmsagnosticcaps.h"

#define PLUGIN_NAME "agnosticbin"

#define INPUT_TEE "input_tee"
#define DECODED_TEE "decoded_tee"

#define OLD_EVENT_FUNC_DATA "old_event_func"
#define START_STOP_EVENT_FUNC_DATA "start_stop_event_func"
#define AGNOSTIC_BIN_DATA "agnostic_bin"
#define DECODEBIN_VALVE_DATA "decodebin_valve"

#define ENCODER_QUEUE_DATA "enc_queue"
#define ENCODER_TEE_DATA "enc_tee"
#define ENCODER_CONVERT_DATA "enc_convert"

#define START_STOP_EVENT_NAME "event/start-stop"
#define START "start"

static GstStaticCaps static_raw_audio_caps =
GST_STATIC_CAPS (KMS_AGNOSTIC_RAW_AUDIO_CAPS);
static GstStaticCaps static_raw_video_caps =
GST_STATIC_CAPS (KMS_AGNOSTIC_RAW_VIDEO_CAPS);

static GstStaticCaps static_raw_caps = GST_STATIC_CAPS (KMS_AGNOSTIC_RAW_CAPS);

GST_DEBUG_CATEGORY_STATIC (kms_agnostic_bin_debug);
#define GST_CAT_DEFAULT kms_agnostic_bin_debug

#define kms_agnostic_bin_parent_class parent_class
G_DEFINE_TYPE (KmsAgnosticBin, kms_agnostic_bin, GST_TYPE_BIN);

typedef void (*GstStartStopFunction) (KmsAgnosticBin * agnosticbin,
    GstElement * element, gboolean start);

/* Filter signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

enum
{
  PROP_0
};

/* the capabilities of the inputs and outputs. */
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (KMS_AGNOSTIC_AGNOSTIC_CAPS)
    );

static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src_%u",
    GST_PAD_SRC,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS (KMS_AGNOSTIC_AGNOSTIC_CAPS)
    );

static void
kms_agnostic_bin_valve_start_stop (KmsAgnosticBin * agnosticbin,
    GstElement * valve, gboolean start)
{
  g_object_set (valve, "drop", !start, NULL);
}

static void
kms_agnostic_bin_decodebin_start_stop (KmsAgnosticBin * agnosticbin,
    GstElement * decodebin, gboolean start)
{
  GstElement *valve =
      g_object_get_data (G_OBJECT (decodebin), DECODEBIN_VALVE_DATA);

  g_object_set (valve, "drop", !start, NULL);
}

static gboolean
kms_agnostic_bin_start_stop_event_handler (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  gboolean ret = TRUE;
  GstPadEventFunction old_func =
      g_object_get_data (G_OBJECT (pad), OLD_EVENT_FUNC_DATA);

  if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) {
    const GstStructure *st = gst_event_get_structure (event);

    if (st != NULL
        && g_strcmp0 (gst_structure_get_name (st),
            START_STOP_EVENT_NAME) == 0) {
      gboolean start;

      if (gst_structure_get_boolean (st, START, &start)) {
        KmsAgnosticBin *agnosticbin =
            g_object_get_data (G_OBJECT (pad), AGNOSTIC_BIN_DATA);
        GstStartStopFunction callback =
            g_object_get_data (G_OBJECT (pad), START_STOP_EVENT_FUNC_DATA);
        GST_INFO ("Received event: %P", event);

        if (callback != NULL)
          callback (agnosticbin, GST_ELEMENT (parent), start);

        gst_event_unref (event);
        return TRUE;
      }
    }
  }

  if (old_func != NULL)
    ret = old_func (pad, parent, event);
  else
    ret = gst_pad_event_default (pad, parent, event);

  return ret;
}

static void
kms_agnostic_bin_set_start_stop_event_handler (KmsAgnosticBin * agnosticbin,
    GstElement * element, const char *pad_name, GstStartStopFunction callback)
{
  GstPad *pad = gst_element_get_static_pad (element, pad_name);

  if (pad == NULL)
    return;

  g_object_set_data (G_OBJECT (pad), OLD_EVENT_FUNC_DATA, pad->eventfunc);
  g_object_set_data (G_OBJECT (pad), START_STOP_EVENT_FUNC_DATA, callback);
  g_object_set_data (G_OBJECT (pad), AGNOSTIC_BIN_DATA, agnosticbin);
  gst_pad_set_event_function (pad, kms_agnostic_bin_start_stop_event_handler);
  g_object_unref (pad);
}

static void
kms_agnostic_bin_send_start_stop_event (GstPad * pad, gboolean start)
{
  GstStructure *data =
      gst_structure_new (START_STOP_EVENT_NAME, START, G_TYPE_BOOLEAN, start,
      NULL);
  GstEvent *event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, data);

  if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
    gst_pad_send_event (pad, event);
  } else {
    GstPad *peer = gst_pad_get_peer (pad);

    if (peer != NULL && GST_PAD_DIRECTION (peer) == GST_PAD_SRC)
      gst_pad_send_event (peer, event);
    else
      gst_event_unref (event);

    if (peer != NULL)
      g_object_unref (peer);
  }
}

static void
kms_agnostic_bin_send_force_key_unit_event (GstPad * pad)
{
  GstStructure *s;
  GstEvent *force_key_unit_event;

  GST_DEBUG ("Sending key ");
  s = gst_structure_new ("GstForceKeyUnit",
      "all-headers", G_TYPE_BOOLEAN, TRUE, NULL);
  force_key_unit_event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s);
  gst_pad_send_event (pad, force_key_unit_event);
}

static void
kms_agnostic_bin_unlink_from_tee (GstElement * element, const gchar * pad_name)
{
  GstPad *sink = gst_element_get_static_pad (element, pad_name);
  GstPad *tee_src = gst_pad_get_peer (sink);
  GstElement *tee;

  g_object_unref (sink);

  if (tee_src != NULL) {
    tee = gst_pad_get_parent_element (tee_src);

    if (tee != NULL) {
      gint n_pads = 0;
      GstPad *tee_sink = gst_element_get_static_pad (tee, "sink");

      GST_PAD_STREAM_LOCK (tee_sink);
      gst_element_unlink (tee, element);
      gst_element_release_request_pad (tee, tee_src);

      g_object_get (tee, "num-src-pads", &n_pads, NULL);
      if (n_pads == 0)
        kms_agnostic_bin_send_start_stop_event (tee_sink, FALSE);

      GST_PAD_STREAM_UNLOCK (tee_sink);
      g_object_unref (tee_sink);
    }

    g_object_unref (tee_src);
  }
}

static void
kms_agnostic_bin_link_to_tee (GstElement * tee, GstElement * element,
    const gchar * sink_name)
{
  GstPad *tee_sink = gst_element_get_static_pad (tee, "sink");

  GstPad *sink = gst_element_get_static_pad (element, sink_name);
  GstPad *tee_src = gst_pad_get_peer (sink);

  if (tee_src != NULL) {
    GstElement *old_tee;

    old_tee = gst_pad_get_parent_element (tee_src);

    if (tee != old_tee)
      kms_agnostic_bin_unlink_from_tee (element, sink_name);

    if (old_tee)
      g_object_unref (old_tee);

    g_object_unref (tee_src);
  }

  g_object_unref (sink);

  GST_PAD_STREAM_LOCK (tee_sink);
  tee_src = gst_element_get_request_pad (tee, "src_%u");
  if (tee_src != NULL) {
    if (!gst_element_link_pads (tee, GST_OBJECT_NAME (tee_src), element,
            sink_name)) {
      gst_element_release_request_pad (tee, tee_src);
    } else {
      kms_agnostic_bin_send_start_stop_event (tee_sink, TRUE);

      kms_agnostic_bin_send_force_key_unit_event (tee_src);
    }

    g_object_unref (tee_src);
  }

  GST_PAD_STREAM_UNLOCK (tee_sink);
  g_object_unref (tee_sink);
}

static void
kms_agnostic_bin_encoder_start_stop (KmsAgnosticBin * agnosticbin,
    GstElement * encoder, gboolean start)
{
  GstElement *queue, *tee, *convert;

  if (start)
    return;

  convert = g_object_get_data (G_OBJECT (encoder), ENCODER_CONVERT_DATA);
  queue = g_object_get_data (G_OBJECT (encoder), ENCODER_QUEUE_DATA);
  tee = g_object_get_data (G_OBJECT (encoder), ENCODER_TEE_DATA);

  g_hash_table_remove (agnosticbin->encoded_tees, GST_OBJECT_NAME (tee));

  if (queue != NULL)
    kms_agnostic_bin_unlink_from_tee (queue, "sink");

  gst_element_set_state (queue, GST_STATE_NULL);
  gst_element_set_state (convert, GST_STATE_NULL);
  gst_element_set_state (encoder, GST_STATE_NULL);
  gst_element_set_state (tee, GST_STATE_NULL);

  gst_bin_remove_many (GST_BIN (agnosticbin), queue, convert, encoder, tee,
      NULL);
}

static GstElement *
kms_agnostic_get_convert_element_for_raw_caps (GstCaps * raw_caps)
{
  GstElement *convert = NULL;
  GstCaps *audio_caps = gst_static_caps_get (&static_raw_audio_caps);
  GstCaps *video_caps = gst_static_caps_get (&static_raw_video_caps);

  if (gst_caps_can_intersect (raw_caps, video_caps)) {
    convert = gst_element_factory_make ("videoconvert", NULL);
  } else if (gst_caps_can_intersect (raw_caps, audio_caps)) {
    convert = gst_element_factory_make ("audioconvert", NULL);
  }

  if (convert == NULL)
    convert = gst_element_factory_make ("identity", NULL);

  gst_caps_unref (audio_caps);
  gst_caps_unref (video_caps);

  return convert;
}

static void
configure_encoder (GstElement * encoder, const gchar * factory_name)
{
  GST_DEBUG ("Configure encoder: %s", factory_name);
  if (g_strcmp0 ("vp8enc", factory_name) == 0) {
    g_object_set (G_OBJECT (encoder), "deadline", 80 * GST_MSECOND,
        "threads", 4, NULL);
  }
}

/* This functions is called with the KMS_AGNOSTIC_BIN_LOCK held */
static GstElement *
kms_agnostic_bin_create_encoded_tee (KmsAgnosticBin * agnosticbin,
    GstCaps * allowed_caps)
{
  GstElement *queue, *encoder, *decoded_tee, *tee = NULL, *convert;
  GList *encoder_list, *filtered_list, *l;
  GstElementFactory *encoder_factory = NULL;
  GstPad *decoded_tee_sink;
  GstCaps *raw_caps;

  GST_DEBUG ("Creating a new encoded tee for caps: %P", allowed_caps);

  decoded_tee = gst_bin_get_by_name (GST_BIN (agnosticbin), DECODED_TEE);
  if (decoded_tee == NULL)
    return NULL;

  decoded_tee_sink = gst_element_get_static_pad (decoded_tee, "sink");
  raw_caps = gst_pad_get_allowed_caps (decoded_tee_sink);

  if (raw_caps == NULL)
    goto release_decoded_tee;

  encoder_list =
      gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER,
      GST_RANK_NONE);
  filtered_list =
      gst_element_factory_list_filter (encoder_list, allowed_caps, GST_PAD_SRC,
      FALSE);

  for (l = filtered_list; l != NULL && encoder_factory == NULL; l = l->next) {
    encoder_factory = GST_ELEMENT_FACTORY (l->data);
    if (gst_element_factory_get_num_pad_templates (encoder_factory) != 2) {
      encoder_factory = NULL;
    }
  }

{'fraction_non_alphanumeric': 0.09508908292535204, 'fraction_numerical': 0.0012113258971382427, 'mean_word_length': 4.822509550396709, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5032467', 'n_tokens_mistral': 7896, 'n_tokens_neox': 7696, 'n_words': 1456}