Example #1
0
static gboolean
gst_kate_enc_source_query (GstPad * pad, GstQuery * query)
{
  GstKateEnc *ke;
  gboolean res = FALSE;

  ke = GST_KATE_ENC (gst_pad_get_parent (pad));

  GST_DEBUG ("source query %d", GST_QUERY_TYPE (query));

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_CONVERT:
    {
      GstFormat src_fmt, dest_fmt;
      gint64 src_val, dest_val;

      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
      if (!gst_kate_enc_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val)) {
        return gst_pad_query_default (pad, query);
      }
      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
      res = TRUE;
    }
      break;
    default:
      res = gst_pad_query_default (pad, query);
      break;
  }

  gst_object_unref (ke);

  return res;
}
Example #2
0
/* chain function
 * this function does the actual processing
 */
static GstFlowReturn
gst_kate_enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstKateEnc *ke = GST_KATE_ENC (parent);
  GstFlowReturn rflow;

  GST_DEBUG_OBJECT (ke, "got packet, %" G_GSIZE_FORMAT " bytes",
      gst_buffer_get_size (buf));

  /* first push headers if we haven't done that yet */
  rflow = gst_kate_enc_flush_headers (ke);

  if (G_LIKELY (rflow == GST_FLOW_OK)) {
    /* flush any packet we had waiting */
    rflow = gst_kate_enc_flush_waiting (ke, GST_BUFFER_TIMESTAMP (buf));

    if (G_LIKELY (rflow == GST_FLOW_OK)) {
      if (ke->format == GST_KATE_FORMAT_SPU) {
        /* encode a kate_bitmap */
        rflow = gst_kate_enc_chain_spu (ke, buf);
      } else {
        /* encode text */
        rflow = gst_kate_enc_chain_text (ke, buf);
      }
    }
  }

  gst_buffer_unref (buf);

  return rflow;
}
Example #3
0
/* chain function
 * this function does the actual processing
 */
static GstFlowReturn
gst_kate_enc_chain (GstPad * pad, GstBuffer * buf)
{
  GstKateEnc *ke = GST_KATE_ENC (gst_pad_get_parent (pad));
  GstFlowReturn rflow = GST_FLOW_OK;
  GstCaps *caps;
  const gchar *mime_type = NULL;

  GST_DEBUG_OBJECT (ke, "got packet, %u bytes", GST_BUFFER_SIZE (buf));

  /* get the type of the data we're being sent */
  caps = GST_PAD_CAPS (pad);
  if (G_UNLIKELY (caps == NULL)) {
    GST_WARNING_OBJECT (ke, "No input caps set");
    rflow = GST_FLOW_NOT_NEGOTIATED;
  } else {
    const GstStructure *structure = gst_caps_get_structure (caps, 0);
    if (structure)
      mime_type = gst_structure_get_name (structure);

    if (mime_type) {
      GST_LOG_OBJECT (ke, "Packet has MIME type %s", mime_type);

      /* first push headers if we haven't done that yet */
      rflow = gst_kate_enc_flush_headers (ke);

      if (G_LIKELY (rflow == GST_FLOW_OK)) {
        /* flush any packet we had waiting */
        rflow = gst_kate_enc_flush_waiting (ke, GST_BUFFER_TIMESTAMP (buf));

        if (G_LIKELY (rflow == GST_FLOW_OK)) {
          if (!strcmp (mime_type, GST_KATE_SPU_MIME_TYPE)) {
            /* encode a kate_bitmap */
            rflow = gst_kate_enc_chain_spu (ke, buf);
          } else {
            /* encode text */
            rflow = gst_kate_enc_chain_text (ke, buf, mime_type);
          }
        }
      }
    } else {
      GST_WARNING_OBJECT (ke, "Packet has no MIME type, ignored");
    }
  }

  gst_buffer_unref (buf);

  gst_object_unref (ke);

  GST_LOG_OBJECT (ke, "Leaving chain function");

  return rflow;
}
Example #4
0
static void
gst_kate_enc_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstKateEnc *ke = GST_KATE_ENC (object);
  const char *str;

  switch (prop_id) {
    case ARG_LANGUAGE:
      if (ke->language) {
        g_free (ke->language);
        ke->language = NULL;
      }
      str = g_value_get_string (value);
      if (str)
        ke->language = g_strdup (str);
      break;
    case ARG_CATEGORY:
      if (ke->category) {
        g_free (ke->category);
        ke->category = NULL;
      }
      str = g_value_get_string (value);
      if (str)
        ke->category = g_strdup (str);
      break;
    case ARG_GRANULE_RATE_NUM:
      ke->granule_rate_numerator = g_value_get_int (value);
      break;
    case ARG_GRANULE_RATE_DEN:
      ke->granule_rate_denominator = g_value_get_int (value);
      break;
    case ARG_GRANULE_SHIFT:
      ke->granule_rate_denominator = g_value_get_int (value);
      break;
    case ARG_KEEPALIVE_MIN_TIME:
      ke->keepalive_min_time = g_value_get_float (value);
      break;
    case ARG_ORIGINAL_CANVAS_WIDTH:
      ke->original_canvas_width = g_value_get_int (value);
      break;
    case ARG_ORIGINAL_CANVAS_HEIGHT:
      ke->original_canvas_height = g_value_get_int (value);
      break;
    case ARG_DEFAULT_SPU_DURATION:
      ke->default_spu_duration = g_value_get_float (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}
Example #5
0
static gboolean
gst_kate_enc_convert (GstPad * pad, GstFormat src_fmt, gint64 src_val,
    GstFormat * dest_fmt, gint64 * dest_val)
{
  GstKateEnc *ke;
  gboolean res = FALSE;

  if (src_fmt == *dest_fmt) {
    *dest_val = src_val;
    return TRUE;
  }

  ke = GST_KATE_ENC (gst_pad_get_parent (pad));

  if (!ke->initialized) {
    GST_WARNING_OBJECT (ke, "not initialized yet");
    gst_object_unref (ke);
    return FALSE;
  }

  if (src_fmt == GST_FORMAT_BYTES || *dest_fmt == GST_FORMAT_BYTES) {
    GST_WARNING_OBJECT (ke, "unsupported format");
    gst_object_unref (ke);
    return FALSE;
  }

  switch (src_fmt) {
    case GST_FORMAT_DEFAULT:
      switch (*dest_fmt) {
        case GST_FORMAT_TIME:
          *dest_val = gst_kate_enc_granule_time (&ke->k, src_val);
          res = TRUE;
          break;
        default:
          res = FALSE;
          break;
      }
      break;
    default:
      res = FALSE;
      break;
  }

  if (!res) {
    GST_WARNING_OBJECT (ke, "unsupported format");
  }

  gst_object_unref (ke);
  return res;
}
Example #6
0
static void
gst_kate_enc_dispose (GObject * object)
{
  GstKateEnc *ke = GST_KATE_ENC (object);

  GST_LOG_OBJECT (ke, "disposing");

  if (ke->language) {
    g_free (ke->language);
    ke->language = NULL;
  }
  if (ke->category) {
    g_free (ke->category);
    ke->category = NULL;
  }

  GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
}
Example #7
0
static gboolean
gst_kate_enc_setcaps (GstPad * pad, GstCaps * caps)
{
  GstKateEnc *ke;

  ke = GST_KATE_ENC (GST_PAD_PARENT (pad));

  GST_LOG_OBJECT (ke, "input caps: %" GST_PTR_FORMAT, caps);

  /* One day we could try to automatically set the category based on the
   * input format, assuming that the input is subtitles. Currently that
   * doesn't work yet though, because we send the header packets already from
   * the sink event handler when receiving the newsegment event, so before
   * the first buffer (might be tricky to change too, given that there could
   * be no data at the beginning for a long time). So for now we just try to
   * make sure people didn't set the category to something obviously wrong. */
  if (ke->category != NULL) {
    GstStructure *s = gst_caps_get_structure (caps, 0);

    if (gst_structure_has_name (s, "text/plain") ||
        gst_structure_has_name (s, "text/x-pango-markup")) {
      if (strcmp (ke->category, "K-SPU") == 0 ||
          strcmp (ke->category, "spu-subtitles") == 0) {
        GST_ELEMENT_WARNING (ke, LIBRARY, SETTINGS, (NULL),
            ("Category set to '%s', but input is text-based.", ke->category));
      }
    } else if (gst_structure_has_name (s, "video/x-dvd-subpicture")) {
      if (strcmp (ke->category, "SUB") == 0 ||
          strcmp (ke->category, "subtitles") == 0) {
        GST_ELEMENT_WARNING (ke, LIBRARY, SETTINGS, (NULL),
            ("Category set to '%s', but input is subpictures.", ke->category));
      }
    } else {
      GST_ERROR_OBJECT (ke, "unexpected input caps %" GST_PTR_FORMAT, caps);
      return FALSE;
    }
  }

  return TRUE;
}
Example #8
0
static void
gst_kate_enc_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstKateEnc *ke = GST_KATE_ENC (object);

  switch (prop_id) {
    case ARG_LANGUAGE:
      g_value_set_string (value, ke->language ? ke->language : "");
      break;
    case ARG_CATEGORY:
      g_value_set_string (value, ke->category ? ke->category : "");
      break;
    case ARG_GRANULE_RATE_NUM:
      g_value_set_int (value, ke->granule_rate_numerator);
      break;
    case ARG_GRANULE_RATE_DEN:
      g_value_set_int (value, ke->granule_rate_denominator);
      break;
    case ARG_GRANULE_SHIFT:
      g_value_set_int (value, ke->granule_shift);
      break;
    case ARG_KEEPALIVE_MIN_TIME:
      g_value_set_float (value, ke->keepalive_min_time);
      break;
    case ARG_ORIGINAL_CANVAS_WIDTH:
      g_value_set_int (value, ke->original_canvas_width);
      break;
    case ARG_ORIGINAL_CANVAS_HEIGHT:
      g_value_set_int (value, ke->original_canvas_height);
      break;
    case ARG_DEFAULT_SPU_DURATION:
      g_value_set_float (value, ke->default_spu_duration);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}
Example #9
0
static void
gst_kate_enc_metadata_set1 (const GstTagList * list, const gchar * tag,
    gpointer kateenc)
{
  GstKateEnc *ke = GST_KATE_ENC (kateenc);
  GList *vc_list, *l;

  vc_list = gst_tag_to_vorbis_comments (list, tag);

  for (l = vc_list; l != NULL; l = l->next) {
    const gchar *vc_string = (const gchar *) l->data;
    gchar *key = NULL, *val = NULL;

    GST_LOG_OBJECT (ke, "Kate comment: %s", vc_string);
    if (gst_tag_parse_extended_comment (vc_string, &key, NULL, &val, TRUE)) {
      kate_comment_add_tag (&ke->kc, key, val);
      g_free (key);
      g_free (val);
    }
  }

  g_list_foreach (vc_list, (GFunc) g_free, NULL);
  g_list_free (vc_list);
}
Example #10
0
static gboolean
gst_kate_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
  GstKateEnc *ke = GST_KATE_ENC (parent);
  const GstStructure *structure;
  gboolean ret;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      ret = gst_kate_enc_setcaps (ke, caps);
      gst_event_unref (event);
      break;
    }
    case GST_EVENT_SEGMENT:{
      GstSegment seg;

      GST_LOG_OBJECT (ke, "Got newsegment event");

      gst_event_copy_segment (event, &seg);

      if (!ke->headers_sent) {
        if (ke->pending_segment)
          gst_event_unref (ke->pending_segment);
        ke->pending_segment = event;
        event = NULL;
      }

      if (ke->initialized) {
        GST_LOG_OBJECT (ke, "ensuring all headers are in");
        if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) {
          GST_WARNING_OBJECT (ke, "Failed to flush headers");
        } else {
          if (seg.format != GST_FORMAT_TIME
              || !GST_CLOCK_TIME_IS_VALID (seg.start)) {
            GST_WARNING_OBJECT (ke,
                "No time in newsegment event %p, format %d, timestamp %"
                G_GINT64_FORMAT, event, (int) seg.format, seg.start);
            /* to be safe, we'd need to generate a keepalive anyway, but we'd have to guess at the timestamp to use; a
               good guess would be the last known timestamp plus the keepalive time, but if we then get a packet with a
               timestamp less than this, it would fail to encode, which would be Bad. If we don't encode a keepalive, we
               run the risk of stalling the pipeline and hanging, which is Very Bad. Oh dear. We can't exit(-1), can we ? */
          } else {
            float t = seg.start / (double) GST_SECOND;

            if (ke->delayed_spu
                && t - ke->delayed_start / (double) GST_SECOND >=
                ke->default_spu_duration) {
              if (G_UNLIKELY (gst_kate_enc_flush_waiting (ke,
                          seg.start) != GST_FLOW_OK)) {
                GST_WARNING_OBJECT (ke, "Failed to encode delayed packet");
                /* continue with new segment handling anyway */
              }
            }

            GST_LOG_OBJECT (ke, "ts %f, last %f (min %f)", t,
                ke->last_timestamp / (double) GST_SECOND,
                ke->keepalive_min_time);
            if (ke->keepalive_min_time > 0.0f
                && t - ke->last_timestamp / (double) GST_SECOND >=
                ke->keepalive_min_time) {
              /* we only generate a keepalive if there is no SPU waiting, as it would
                 mean out of sequence start times - and granulepos */
              if (!ke->delayed_spu) {
                gst_kate_enc_generate_keepalive (ke, seg.start);
              }
            }
          }
        }
      }
      if (event)
        ret = gst_pad_push_event (ke->srcpad, event);
      else
        ret = TRUE;
      break;
    }
    case GST_EVENT_CUSTOM_DOWNSTREAM:
      GST_LOG_OBJECT (ke, "Got custom downstream event");
      /* adapted from the dvdsubdec element */
      structure = gst_event_get_structure (event);
      if (structure != NULL
          && gst_structure_has_name (structure, "application/x-gst-dvd")) {
        if (ke->initialized) {
          GST_LOG_OBJECT (ke, "ensuring all headers are in");
          if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) {
            GST_WARNING_OBJECT (ke, "Failed to flush headers");
          } else {
            const gchar *event_name =
                gst_structure_get_string (structure, "event");
            if (event_name) {
              if (!strcmp (event_name, "dvd-spu-clut-change")) {
                gchar name[16];
                int idx;
                gboolean found;
                gint value;
                GST_INFO_OBJECT (ke, "New CLUT received");
                for (idx = 0; idx < 16; ++idx) {
                  g_snprintf (name, sizeof (name), "clut%02d", idx);
                  found = gst_structure_get_int (structure, name, &value);
                  if (found) {
                    ke->spu_clut[idx] = value;
                  } else {
                    GST_WARNING_OBJECT (ke,
                        "DVD CLUT event did not contain %s field", name);
                  }
                }
              } else if (!strcmp (event_name, "dvd-lang-codes")) {
                /* we can't know which stream corresponds to us */
              }
            } else {
              GST_WARNING_OBJECT (ke, "custom downstream event with no name");
            }
          }
        }
      }
      ret = gst_pad_push_event (ke->srcpad, event);
      break;

    case GST_EVENT_TAG:
      GST_LOG_OBJECT (ke, "Got tag event");
      if (ke->tags) {
        GstTagList *list;

        gst_event_parse_tag (event, &list);
        gst_tag_list_insert (ke->tags, list,
            gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (ke)));
      } else {
        g_assert_not_reached ();
      }
      ret = gst_pad_event_default (pad, parent, event);
      break;

    case GST_EVENT_EOS:
      GST_INFO_OBJECT (ke, "Got EOS event");
      if (ke->initialized) {
        GST_LOG_OBJECT (ke, "ensuring all headers are in");
        if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) {
          GST_WARNING_OBJECT (ke, "Failed to flush headers");
        } else {
          kate_packet kp;
          int ret;
          GstClockTime delayed_end =
              ke->delayed_start + ke->default_spu_duration * GST_SECOND;

          if (G_UNLIKELY (gst_kate_enc_flush_waiting (ke,
                      delayed_end) != GST_FLOW_OK)) {
            GST_WARNING_OBJECT (ke, "Failed to encode delayed packet");
            /* continue with EOS handling anyway */
          }

          ret = kate_encode_finish (&ke->k, -1, &kp);
          if (ret < 0) {
            GST_WARNING_OBJECT (ke, "Failed to encode EOS packet: %s",
                gst_kate_util_get_error_message (ret));
          } else {
            kate_int64_t granpos = kate_encode_get_granule (&ke->k);
            GST_LOG_OBJECT (ke, "EOS packet encoded");
            if (gst_kate_enc_push_and_free_kate_packet (ke, &kp, granpos,
                    ke->latest_end_time, 0, FALSE)) {
              GST_WARNING_OBJECT (ke, "Failed to push EOS packet");
            }
          }
        }
      }
      ret = gst_pad_event_default (pad, parent, event);
      break;

    default:
      GST_LOG_OBJECT (ke, "Got unhandled event");
      ret = gst_pad_event_default (pad, parent, event);
      break;
  }

  return ret;
}
Example #11
0
static GstStateChangeReturn
gst_kate_enc_change_state (GstElement * element, GstStateChange transition)
{
  GstKateEnc *ke = GST_KATE_ENC (element);
  GstStateChangeReturn res;
  int ret;

  GST_INFO_OBJECT (ke, "gst_kate_enc_change_state");

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      ke->tags = gst_tag_list_new_empty ();
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      GST_DEBUG_OBJECT (ke, "READY -> PAUSED, initializing kate state");
      ret = kate_info_init (&ke->ki);
      if (ret < 0) {
        GST_WARNING_OBJECT (ke, "failed to initialize kate info structure: %s",
            gst_kate_util_get_error_message (ret));
        break;
      }
      if (ke->language) {
        ret = kate_info_set_language (&ke->ki, ke->language);
        if (ret < 0) {
          GST_WARNING_OBJECT (ke, "failed to set stream language: %s",
              gst_kate_util_get_error_message (ret));
          break;
        }
      }
      if (ke->category) {
        ret = kate_info_set_category (&ke->ki, ke->category);
        if (ret < 0) {
          GST_WARNING_OBJECT (ke, "failed to set stream category: %s",
              gst_kate_util_get_error_message (ret));
          break;
        }
      }
      ret =
          kate_info_set_original_canvas_size (&ke->ki,
          ke->original_canvas_width, ke->original_canvas_height);
      if (ret < 0) {
        GST_WARNING_OBJECT (ke, "failed to set original canvas size: %s",
            gst_kate_util_get_error_message (ret));
        break;
      }
      ret = kate_comment_init (&ke->kc);
      if (ret < 0) {
        GST_WARNING_OBJECT (ke,
            "failed to initialize kate comment structure: %s",
            gst_kate_util_get_error_message (ret));
        break;
      }
      ret = kate_encode_init (&ke->k, &ke->ki);
      if (ret < 0) {
        GST_WARNING_OBJECT (ke, "failed to initialize kate state: %s",
            gst_kate_util_get_error_message (ret));
        break;
      }
      ke->headers_sent = FALSE;
      ke->initialized = TRUE;
      ke->last_timestamp = 0;
      ke->latest_end_time = 0;
      ke->format = GST_KATE_FORMAT_UNDEFINED;
      break;
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
      break;
    case GST_STATE_CHANGE_READY_TO_NULL:
      gst_tag_list_unref (ke->tags);
      ke->tags = NULL;
      break;
    default:
      break;
  }

  res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
  if (res == GST_STATE_CHANGE_FAILURE) {
    GST_WARNING_OBJECT (ke, "Parent failed to change state");
    return res;
  }

  switch (transition) {
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
      break;
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      GST_DEBUG_OBJECT (ke, "PAUSED -> READY, clearing kate state");
      if (ke->initialized) {
        kate_clear (&ke->k);
        kate_info_clear (&ke->ki);
        kate_comment_clear (&ke->kc);
        ke->initialized = FALSE;
        ke->last_timestamp = 0;
        ke->latest_end_time = 0;
      }
      gst_event_replace (&ke->pending_segment, NULL);
      break;
    case GST_STATE_CHANGE_READY_TO_NULL:
      break;
    default:
      break;
  }

  GST_DEBUG_OBJECT (ke, "State change done");

  return res;
}