static GstStateChangeReturn
gst_play_sink_video_convert_change_state (GstElement * element,
        GstStateChange transition)
{
    GstStateChangeReturn ret;
    GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (element);

    switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
        GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
        if (gst_pad_is_blocked (self->sink_proxypad))
            gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
                                            (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
                                            (GDestroyNotify) gst_object_unref);
        GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
        break;
    default:
        break;
    }

    ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
    if (ret == GST_STATE_CHANGE_FAILURE)
        return ret;

    switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
        GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
        gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
        if (self->conv) {
            gst_element_set_state (self->conv, GST_STATE_NULL);
            gst_bin_remove (GST_BIN_CAST (self), self->conv);
            self->conv = NULL;
        }
        if (self->scale) {
            gst_element_set_state (self->scale, GST_STATE_NULL);
            gst_bin_remove (GST_BIN_CAST (self), self->scale);
            self->scale = NULL;
        }
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
                                  self->sink_proxypad);
        self->raw = FALSE;
        GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
        break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
        GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
        if (!gst_pad_is_blocked (self->sink_proxypad))
            gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
                                            (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
                                            (GDestroyNotify) gst_object_unref);
        GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
    default:
        break;
    }

    return ret;
}
static gboolean
gst_play_sink_video_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
{
    GstPlaySinkVideoConvert *self =
        GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
    gboolean ret;
    GstStructure *s;
    const gchar *name;
    gboolean reconfigure = FALSE;

    GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
    s = gst_caps_get_structure (caps, 0);
    name = gst_structure_get_name (s);

    if (g_str_has_prefix (name, "video/x-raw-")) {
        if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
            GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw");
            reconfigure = TRUE;
            gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
                                            (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
                                            (GDestroyNotify) gst_object_unref);
        }
    } else {
        if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
            GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw");
            reconfigure = TRUE;
            gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
                                            (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
                                            (GDestroyNotify) gst_object_unref);
        }
    }

    /* Otherwise the setcaps below fails */
    if (reconfigure) {
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
    }
    GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);

    ret = gst_ghost_pad_setcaps_default (pad, caps);

    GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps,
                      ret);

    gst_object_unref (self);

    return ret;
}
static void
pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self)
{
    GstPad *peer;
    GstCaps *caps;
    gboolean raw;

    GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
    self->sink_proxypad_blocked = blocked;
    GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
    if (!blocked)
        goto done;

    /* There must be a peer at this point */
    peer = gst_pad_get_peer (self->sinkpad);
    caps = gst_pad_get_negotiated_caps (peer);
    if (!caps)
        caps = gst_pad_get_caps_reffed (peer);
    gst_object_unref (peer);

    raw = is_raw_caps (caps);
    GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw);
    gst_caps_unref (caps);

    if (raw == self->raw)
        goto unblock;
    self->raw = raw;

    if (raw) {
        GstBin *bin = GST_BIN_CAST (self);
        GstElement *head = NULL, *prev = NULL;
        GstPad *pad;

        GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");

        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);

        self->conv = gst_element_factory_make ("ffmpegcolorspace", "conv");
        if (self->conv == NULL) {
            post_missing_element_message (self, "ffmpegcolorspace");
            GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
                                 (_("Missing element '%s' - check your GStreamer installation."),
                                  "ffmpegcolorspace"), ("video rendering might fail"));
        } else {
            gst_bin_add (bin, self->conv);
            gst_element_sync_state_with_parent (self->conv);
            distribute_running_time (self->conv, &self->segment);
            prev = head = self->conv;
        }

        self->scale = gst_element_factory_make ("videoscale", "scale");
        if (self->scale == NULL) {
            post_missing_element_message (self, "videoscale");
            GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
                                 (_("Missing element '%s' - check your GStreamer installation."),
                                  "videoscale"), ("possibly a liboil version mismatch?"));
        } else {
            /* Add black borders if necessary to keep the DAR */
            g_object_set (self->scale, "add-borders", TRUE, NULL);
            gst_bin_add (bin, self->scale);
            gst_element_sync_state_with_parent (self->scale);
            distribute_running_time (self->scale, &self->segment);
            if (prev) {
                if (!gst_element_link_pads_full (prev, "src", self->scale, "sink",
                                                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
                    goto link_failed;
            } else {
                head = self->scale;
            }
            prev = self->scale;
        }

        if (head) {
            pad = gst_element_get_static_pad (head, "sink");
            gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad);
            gst_object_unref (pad);
        }

        if (prev) {
            pad = gst_element_get_static_pad (prev, "src");
            gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad);
            gst_object_unref (pad);
        }

        if (!head && !prev) {
            gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
                                      self->sink_proxypad);
        }

        GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
    } else {
        GstBin *bin = GST_BIN_CAST (self);

        GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");

        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);

        if (self->conv) {
            gst_element_set_state (self->conv, GST_STATE_NULL);
            gst_bin_remove (bin, self->conv);
            self->conv = NULL;
        }
        if (self->scale) {
            gst_element_set_state (self->scale, GST_STATE_NULL);
            gst_bin_remove (bin, self->scale);
            self->scale = NULL;
        }

        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
                                  self->sink_proxypad);

        GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
    }

unblock:
    gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
                                    (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
                                    (GDestroyNotify) gst_object_unref);

done:
    GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
    return;

link_failed:
    {
        GST_ELEMENT_ERROR (self, CORE, PAD,
                           (NULL), ("Failed to configure the video converter."));
        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
                                  self->sink_proxypad);
        gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
                                        (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
                                        (GDestroyNotify) gst_object_unref);
        return;
    }
}
Beispiel #4
0
static gboolean
create_elements (RsnDvdBin * dvdbin)
{
  GstPadTemplate *src_templ = NULL;
  GstPad *src = NULL;
  GstPad *sink = NULL;
  RsnDvdBinPadBlockCtx *bctx = NULL;

  if (!try_create_piece (dvdbin, DVD_ELEM_SOURCE, NULL,
          RESIN_TYPE_DVDSRC, "dvdsrc", "DVD source")) {
    return FALSE;
  }

  /* FIXME: Locking */
  if (dvdbin->device) {
    g_object_set (G_OBJECT (dvdbin->pieces[DVD_ELEM_SOURCE]),
        "device", dvdbin->device, NULL);
  }

  if (!try_create_piece (dvdbin, DVD_ELEM_DEMUX,
          NULL, GST_TYPE_FLUPS_DEMUX, "dvddemux", "DVD demuxer"))
    return FALSE;

  if (gst_element_link (dvdbin->pieces[DVD_ELEM_SOURCE],
          dvdbin->pieces[DVD_ELEM_DEMUX]) == FALSE)
    goto failed_connect;

  /* Listen for new pads from the demuxer */
  g_signal_connect (G_OBJECT (dvdbin->pieces[DVD_ELEM_DEMUX]), "pad-added",
      G_CALLBACK (demux_pad_added), dvdbin);

  g_signal_connect (G_OBJECT (dvdbin->pieces[DVD_ELEM_DEMUX]), "no-more-pads",
      G_CALLBACK (demux_no_more_pads), dvdbin);

  if (!try_create_piece (dvdbin, DVD_ELEM_MQUEUE, "multiqueue", 0, "mq",
          "multiqueue"))
    return FALSE;

  g_object_set (dvdbin->pieces[DVD_ELEM_MQUEUE],
      "max-size-time", (7 * GST_SECOND / 10), "max-size-bytes", 0,
      "max-size-buffers", 0, NULL);

  /* Decodebin will throw a missing element message to find an MPEG decoder */
  if (!try_create_piece (dvdbin, DVD_ELEM_VIDDEC, NULL, RSN_TYPE_VIDEODEC,
          "viddec", "video decoder"))
    return FALSE;

  if (!try_create_piece (dvdbin, DVD_ELEM_PARSET, NULL, RSN_TYPE_RSNPARSETTER,
          "rsnparsetter", "Aspect ratio adjustment"))
    return FALSE;

  src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDDEC], "src");
  sink = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_PARSET], "sink");
  if (src == NULL || sink == NULL)
    goto failed_viddec_connect;
  if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink)))
    goto failed_viddec_connect;
  gst_object_unref (src);
  gst_object_unref (sink);
  src = sink = NULL;

  src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_PARSET], "src");
  if (src == NULL)
    goto failed_video_ghost;
  src_templ = gst_static_pad_template_get (&video_src_template);
  dvdbin->video_pad = gst_ghost_pad_new_from_template ("video", src, src_templ);
  gst_object_unref (src_templ);
  if (dvdbin->video_pad == NULL)
    goto failed_video_ghost;
  gst_pad_set_active (dvdbin->video_pad, TRUE);
  bctx = g_slice_new (RsnDvdBinPadBlockCtx);
  bctx->dvdbin = gst_object_ref (dvdbin);
  bctx->pad = gst_object_ref (dvdbin->video_pad);
  gst_pad_set_blocked_async_full (src, TRUE,
      (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
      _pad_block_destroy_notify);
  gst_object_unref (src);
  src = NULL;

  if (!try_create_piece (dvdbin, DVD_ELEM_SPU_SELECT, NULL,
          RSN_TYPE_STREAM_SELECTOR, "subpselect", "Subpicture stream selector"))
    return FALSE;

  /* Add a single standalone queue to hold a single buffer of SPU data */
  if (!try_create_piece (dvdbin, DVD_ELEM_SPUQ, "queue", 0, "spu_q",
          "subpicture decoder buffer"))
    return FALSE;
  g_object_set (dvdbin->pieces[DVD_ELEM_SPUQ],
      "max-size-time", G_GUINT64_CONSTANT (0), "max-size-bytes", 0,
      "max-size-buffers", 1, NULL);

  src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU_SELECT], "src");
  sink = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPUQ], "sink");
  if (src == NULL || sink == NULL)
    goto failed_spuq_connect;
  if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink)))
    goto failed_spuq_connect;
  gst_object_unref (src);
  gst_object_unref (sink);
  src = sink = NULL;

  src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPUQ], "src");
  if (src == NULL)
    goto failed_spu_ghost;
  src_templ = gst_static_pad_template_get (&subpicture_src_template);
  dvdbin->subpicture_pad =
      gst_ghost_pad_new_from_template ("subpicture", src, src_templ);
  gst_object_unref (src_templ);
  if (dvdbin->subpicture_pad == NULL)
    goto failed_spu_ghost;
  gst_pad_set_active (dvdbin->subpicture_pad, TRUE);
  bctx = g_slice_new (RsnDvdBinPadBlockCtx);
  bctx->dvdbin = gst_object_ref (dvdbin);
  bctx->pad = gst_object_ref (dvdbin->subpicture_pad);
  gst_pad_set_blocked_async_full (src, TRUE,
      (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
      _pad_block_destroy_notify);
  gst_object_unref (src);
  src = NULL;

  if (!try_create_piece (dvdbin, DVD_ELEM_AUD_SELECT, NULL,
          RSN_TYPE_STREAM_SELECTOR, "audioselect", "Audio stream selector"))
    return FALSE;

  if (!try_create_piece (dvdbin, DVD_ELEM_AUDDEC, NULL,
          RSN_TYPE_AUDIODEC, "auddec", "audio decoder"))
    return FALSE;

  /* rsnaudiomunge goes after the audio decoding to regulate the stream */
  if (!try_create_piece (dvdbin, DVD_ELEM_AUD_MUNGE, NULL,
          RSN_TYPE_AUDIOMUNGE, "audiomunge", "Audio output filter"))
    return FALSE;

  src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUDDEC], "src");
  sink =
      gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUD_MUNGE], "sink");
  if (src == NULL || sink == NULL)
    goto failed_aud_connect;
  if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink)))
    goto failed_aud_connect;
  gst_object_unref (sink);
  gst_object_unref (src);
  src = sink = NULL;

  src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUD_SELECT], "src");
  sink = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUDDEC], "sink");
  if (src == NULL || sink == NULL)
    goto failed_aud_connect;
  if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink)))
    goto failed_aud_connect;
  gst_object_unref (sink);
  gst_object_unref (src);
  src = sink = NULL;

  /* ghost audio munge output pad onto bin */
  src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUD_MUNGE], "src");
  if (src == NULL)
    goto failed_aud_ghost;
  src_templ = gst_static_pad_template_get (&audio_src_template);
  dvdbin->audio_pad = gst_ghost_pad_new_from_template ("audio", src, src_templ);
  gst_object_unref (src_templ);
  if (dvdbin->audio_pad == NULL)
    goto failed_aud_ghost;
  gst_pad_set_active (dvdbin->audio_pad, TRUE);
  bctx = g_slice_new (RsnDvdBinPadBlockCtx);
  bctx->dvdbin = gst_object_ref (dvdbin);
  bctx->pad = gst_object_ref (dvdbin->audio_pad);
  gst_pad_set_blocked_async_full (src, TRUE,
      (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
      _pad_block_destroy_notify);
  gst_object_unref (src);
  src = NULL;

  if (dvdbin->video_added && (dvdbin->audio_added || dvdbin->audio_broken)
      && dvdbin->subpicture_added) {
    GST_DEBUG_OBJECT (dvdbin, "Firing no more pads");
    gst_element_no_more_pads (GST_ELEMENT (dvdbin));
  }

  return TRUE;

failed_connect:
  GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
      ("Could not connect DVD source and demuxer elements"));
  goto error_out;
failed_viddec_connect:
  GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
      ("Could not connect DVD video decoder and aspect ratio adjuster"));
  goto error_out;
failed_video_ghost:
  GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
      ("Could not ghost video output pad"));
  goto error_out;
failed_spuq_connect:
  GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
      ("Could not connect DVD subpicture selector and buffer elements"));
  goto error_out;
failed_spu_ghost:
  GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
      ("Could not ghost SPU output pad"));
  goto error_out;
failed_aud_connect:
  GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
      ("Could not connect DVD audio decoder"));
  goto error_out;
failed_aud_ghost:
  GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
      ("Could not ghost audio output pad"));
  goto error_out;
error_out:
  if (src != NULL)
    gst_object_unref (src);
  if (sink != NULL)
    gst_object_unref (sink);
  return FALSE;
}